<commit_before>/*
 * Minecraft Forge
 * Copyright (c) 2016.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation version 2.1
 * of the License.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

package net.minecraftforge.oredict;

import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;

import net.minecraftforge.fml.common.FMLLog;
import net.minecraftforge.fml.common.toposort.TopologicalSort;
import net.minecraftforge.fml.common.toposort.TopologicalSort.DirectedGraph;
import net.minecraft.item.crafting.CraftingManager;
import net.minecraft.item.crafting.IRecipe;
import net.minecraft.item.crafting.RecipeBookCloning;
import net.minecraft.item.crafting.RecipeFireworks;
import net.minecraft.item.crafting.RecipeRepairItem;
import net.minecraft.item.crafting.RecipeTippedArrow;
import net.minecraft.item.crafting.RecipesArmorDyes;
import net.minecraft.item.crafting.RecipesMapCloning;
import net.minecraft.item.crafting.RecipesMapExtending;
import net.minecraft.item.crafting.ShapedRecipes;
import net.minecraft.item.crafting.ShapelessRecipes;
import net.minecraft.item.crafting.ShieldRecipes;
import net.minecraft.item.crafting.RecipesBanners.RecipeAddPattern;
import net.minecraft.item.crafting.RecipesBanners.RecipeDuplicatePattern;
import static net.minecraftforge.oredict.RecipeSorter.Category.*;

public class RecipeSorter implements Comparator<IRecipe>
{
    public enum Category
    {
        UNKNOWN,
        SHAPELESS,
        SHAPED
    };

    private static class SortEntry
    {
        private String name;
        private Class<?> cls;
        private Category cat;
        List<String> before = Lists.newArrayList();
        List<String> after = Lists.newArrayList();

        private SortEntry(String name, Class<?> cls, Category cat, String deps)
        {
            this.name = name;
            this.cls = cls;
            this.cat = cat;
            parseDepends(deps);
        }

        private void parseDepends(String deps)
        {
            if (deps.isEmpty()) return;
            for (String dep : deps.split(" "))
            {
                if (dep.startsWith("before:"))
                {
                    before.add(dep.substring(7));
                }
                else if (dep.startsWith("after:"))
                {
                    after.add(dep.substring(6));
                }
                else
                {
                    throw new IllegalArgumentException("Invalid dependency: " + dep);
                }
            }
        }

        @Override
        public String toString()
        {
            StringBuilder buf = new StringBuilder();
            buf.append("RecipeEntry(\"").append(name).append("\", ");
            buf.append(cat.name()).append(", ");
            buf.append(cls ==  null ? "" : cls.getName()).append(")");

            if (before.size() > 0)
            {
                buf.append(" Before: ").append(Joiner.on(", ").join(before));
            }

            if (after.size() > 0)
            {
                buf.append(" After: ").append(Joiner.on(", ").join(after));
            }

            return buf.toString();
        }

        @Override
        public int hashCode()
        {
            return name.hashCode();
        }
    };

    private static Map<Class<?>, Category>     categories = Maps.newHashMap();
    //private static Map<String, Class<?>>       types = Maps.newHashMap();
    private static Map<String, SortEntry>   entries = Maps.newHashMap();
    private static Map<Class<?>, Integer>      priorities = Maps.newHashMap();

    public static final RecipeSorter INSTANCE = new RecipeSorter();
    private static boolean isDirty = true;

    private static SortEntry before = new SortEntry("Before", null, UNKNOWN, "");
    private static SortEntry after  = new SortEntry("After",  null, UNKNOWN, "");

    private RecipeSorter()
    {
        register("minecraft:shaped",       ShapedRecipes.class,       SHAPED,    "before:minecraft:shapeless");
        register("minecraft:mapextending", RecipesMapExtending.class, SHAPED,    "after:minecraft:shaped before:minecraft:shapeless");
        register("minecraft:shapeless",    ShapelessRecipes.class,    SHAPELESS, "after:minecraft:shaped");
        register("minecraft:shield_deco",  ShieldRecipes.Decoration.class, SHAPELESS, "after:minecraft:shapeless"); //Size 2
        register("minecraft:repair",       RecipeRepairItem.class,    SHAPELESS, "after:minecraft:shapeless"); //Size 4
        register("minecraft:bookcloning",  RecipeBookCloning.class,   SHAPELESS, "after:minecraft:shapeless"); //Size 9
        register("minecraft:tippedarrow",  RecipeTippedArrow.class,   SHAPELESS, "after:minecraft:shapeless"); //Size 9
        register("minecraft:fireworks",    RecipeFireworks.class,     SHAPELESS, "after:minecraft:shapeless"); //Size 10
        register("minecraft:armordyes",    RecipesArmorDyes.class,    SHAPELESS, "after:minecraft:shapeless"); //Size 10
        register("minecraft:mapcloning",   RecipesMapCloning.class,   SHAPELESS, "after:minecraft:shapeless"); //Size 10
        register("minecraft:pattern_dupe", RecipeDuplicatePattern.class, SHAPELESS, "after:minecraft:shapeless"); //Size 2
        register("minecraft:pattern_add",  RecipeAddPattern.class,       SHAPELESS, "after:minecraft:shapeless"); //Size 10

        register("forge:shapedore",     ShapedOreRecipe.class,    SHAPED,    "after:minecraft:shaped before:minecraft:shapeless");
        register("forge:shapelessore",  ShapelessOreRecipe.class, SHAPELESS, "after:minecraft:shapeless");
    }

    @Override
    public int compare(IRecipe r1, IRecipe r2)
    {
        Category c1 = getCategory(r1);
        Category c2 = getCategory(r2);
        if (c1 == SHAPELESS && c2 == SHAPED) return  1;
        if (c1 == SHAPED && c2 == SHAPELESS) return -1;
        if (r2.getRecipeSize() < r1.getRecipeSize()) return -1;
        if (r2.getRecipeSize() > r1.getRecipeSize()) return  1;
        return getPriority(r2) - getPriority(r1); // high priority value first!
    }

    private static Set<Class<?>> warned = Sets.newHashSet();
    public static void sortCraftManager()
    {
        bake();
        FMLLog.fine("Sorting recipes");
        warned.clear();
        Collections.sort(CraftingManager.getInstance().getRecipeList(), INSTANCE);
    }

    public static void register(String name, Class<?> recipe, Category category, String dependencies)
    {
        assert(category != UNKNOWN) : "Category must not be unknown!";
        isDirty = true;

        SortEntry entry = new SortEntry(name, recipe, category, dependencies);
        entries.put(name, entry);<commit_msg>Fix comparison stability of the Recipe Sorter with unknown recipes #2962 (#3030)<commit_after>/*
 * Minecraft Forge
 * Copyright (c) 2016.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation version 2.1
 * of the License.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

package net.minecraftforge.oredict;

import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;

import net.minecraftforge.fml.common.FMLLog;
import net.minecraftforge.fml.common.toposort.TopologicalSort;
import net.minecraftforge.fml.common.toposort.TopologicalSort.DirectedGraph;
import net.minecraft.item.crafting.CraftingManager;
import net.minecraft.item.crafting.IRecipe;
import net.minecraft.item.crafting.RecipeBookCloning;
import net.minecraft.item.crafting.RecipeFireworks;
import net.minecraft.item.crafting.RecipeRepairItem;
import net.minecraft.item.crafting.RecipeTippedArrow;
import net.minecraft.item.crafting.RecipesArmorDyes;
import net.minecraft.item.crafting.RecipesMapCloning;
import net.minecraft.item.crafting.RecipesMapExtending;
import net.minecraft.item.crafting.ShapedRecipes;
import net.minecraft.item.crafting.ShapelessRecipes;
import net.minecraft.item.crafting.ShieldRecipes;
import net.minecraft.item.crafting.RecipesBanners.RecipeAddPattern;
import net.minecraft.item.crafting.RecipesBanners.RecipeDuplicatePattern;
import static net.minecraftforge.oredict.RecipeSorter.Category.*;

public class RecipeSorter implements Comparator<IRecipe>
{
    public enum Category
    {
        UNKNOWN,
        SHAPELESS,
        SHAPED
    };

    private static class SortEntry
    {
        private String name;
        private Class<?> cls;
        private Category cat;
        List<String> before = Lists.newArrayList();
        List<String> after = Lists.newArrayList();

        private SortEntry(String name, Class<?> cls, Category cat, String deps)
        {
            this.name = name;
            this.cls = cls;
            this.cat = cat;
            parseDepends(deps);
        }

        private void parseDepends(String deps)
        {
            if (deps.isEmpty()) return;
            for (String dep : deps.split(" "))
            {
                if (dep.startsWith("before:"))
                {
                    before.add(dep.substring(7));
                }
                else if (dep.startsWith("after:"))
                {
                    after.add(dep.substring(6));
                }
                else
                {
                    throw new IllegalArgumentException("Invalid dependency: " + dep);
                }
            }
        }

        @Override
        public String toString()
        {
            StringBuilder buf = new StringBuilder();
            buf.append("RecipeEntry(\"").append(name).append("\", ");
            buf.append(cat.name()).append(", ");
            buf.append(cls ==  null ? "" : cls.getName()).append(")");

            if (before.size() > 0)
            {
                buf.append(" Before: ").append(Joiner.on(", ").join(before));
            }

            if (after.size() > 0)
            {
                buf.append(" After: ").append(Joiner.on(", ").join(after));
            }

            return buf.toString();
        }

        @Override
        public int hashCode()
        {
            return name.hashCode();
        }
    };

    private static Map<Class<?>, Category>     categories = Maps.newHashMap();
    //private static Map<String, Class<?>>       types = Maps.newHashMap();
    private static Map<String, SortEntry>   entries = Maps.newHashMap();
    private static Map<Class<?>, Integer>      priorities = Maps.newHashMap();

    public static final RecipeSorter INSTANCE = new RecipeSorter();
    private static boolean isDirty = true;

    private static SortEntry before = new SortEntry("Before", null, UNKNOWN, "");
    private static SortEntry after  = new SortEntry("After",  null, UNKNOWN, "");

    private RecipeSorter()
    {
        register("minecraft:shaped",       ShapedRecipes.class,       SHAPED,    "before:minecraft:shapeless");
        register("minecraft:mapextending", RecipesMapExtending.class, SHAPED,    "after:minecraft:shaped before:minecraft:shapeless");
        register("minecraft:shapeless",    ShapelessRecipes.class,    SHAPELESS, "after:minecraft:shaped");
        register("minecraft:shield_deco",  ShieldRecipes.Decoration.class, SHAPELESS, "after:minecraft:shapeless"); //Size 2
        register("minecraft:repair",       RecipeRepairItem.class,    SHAPELESS, "after:minecraft:shapeless"); //Size 4
        register("minecraft:bookcloning",  RecipeBookCloning.class,   SHAPELESS, "after:minecraft:shapeless"); //Size 9
        register("minecraft:tippedarrow",  RecipeTippedArrow.class,   SHAPELESS, "after:minecraft:shapeless"); //Size 9
        register("minecraft:fireworks",    RecipeFireworks.class,     SHAPELESS, "after:minecraft:shapeless"); //Size 10
        register("minecraft:armordyes",    RecipesArmorDyes.class,    SHAPELESS, "after:minecraft:shapeless"); //Size 10
        register("minecraft:mapcloning",   RecipesMapCloning.class,   SHAPELESS, "after:minecraft:shapeless"); //Size 10
        register("minecraft:pattern_dupe", RecipeDuplicatePattern.class, SHAPELESS, "after:minecraft:shapeless"); //Size 2
        register("minecraft:pattern_add",  RecipeAddPattern.class,       SHAPELESS, "after:minecraft:shapeless"); //Size 10

        register("forge:shapedore",     ShapedOreRecipe.class,    SHAPED,    "after:minecraft:shaped before:minecraft:shapeless");
        register("forge:shapelessore",  ShapelessOreRecipe.class, SHAPELESS, "after:minecraft:shapeless");
    }

    @Override
    public int compare(IRecipe r1, IRecipe r2)
    {
        Category c1 = getCategory(r1);
        Category c2 = getCategory(r2);
        int categoryComparison = -c1.compareTo(c2);
        if (categoryComparison != 0)
        {
            return categoryComparison;
        }
        else
        {
            if (r2.getRecipeSize() < r1.getRecipeSize()) return -1;
            if (r2.getRecipeSize() > r1.getRecipeSize()) return  1;
            return getPriority(r2) - getPriority(r1); // high priority value first!
        }
    }

    private static Set<Class<?>> warned = Sets.newHashSet();
    public static void sortCraftManager()
    {
        bake();
        FMLLog.fine("Sorting recipes");
        warned.clear();
        Collections.sort(CraftingManager.getInstance().getRecipeList(), INSTANCE);
    }

    public static void register(String name, Class<?> recipe, Category category, String dependencies)
    {
        assert(category != UNKNOWN) : "Category must not be unknown!";
        isDirty = true;

        SortEntry entry = new SortEntry(name, recipe, category, dependencies);
        entries.put(name, entry);
{'fraction_non_alphanumeric': 0.11304926764314248, 'fraction_numerical': 0.0075233022636484684, 'mean_word_length': 2.8309104820198927, 'pattern_counts': {'":': 0, '<': 35, '<?xml version=': 0, '>': 39, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6958358', 'n_tokens_mistral': 4357, 'n_tokens_neox': 4343, 'n_words': 993}