private boolean removeShapeless(ShapelessRecipe recipe) {
   boolean failed = false;
   Plugin plugin = recipe.getPlugin();
   if (plugin != null) {
     if (!registeredShapelessRecipes.containsKey(plugin)) {
       return false;
     }
     if (!registeredShapelessRecipes.get(plugin).containsKey(recipe.getIngredients().size())) {
       return false;
     }
     if (!registeredShapelessRecipes
         .get(plugin)
         .get(recipe.getIngredients().size())
         .contains(recipe)) {
       return false;
     }
     failed =
         !registeredShapelessRecipes
                 .get(recipe.getPlugin())
                 .get(recipe.getIngredients().size())
                 .remove(recipe)
             || failed;
   }
   if (!allShapelessRecipes.containsKey(recipe.getIngredients().size())) {
     return false;
   }
   failed = !allShapelessRecipes.get(recipe.getIngredients().size()).remove(recipe) || failed;
   return !failed;
 }
  private boolean registerShapeless(ShapelessRecipe recipe) {
    boolean failed = false;
    Plugin plugin = recipe.getPlugin();
    if (plugin != null) {
      ConcurrentHashMap<Integer, Set<ShapelessRecipe>> recipesMap =
          (ConcurrentHashMap<Integer, Set<ShapelessRecipe>>) registeredShapelessRecipes.get(plugin);
      if (recipesMap == null) {
        recipesMap = new ConcurrentHashMap<Integer, Set<ShapelessRecipe>>();
        registeredShapelessRecipes.put(plugin, recipesMap);
      }
      if (recipesMap.get(recipe.getIngredients().size()) == null) {
        Set<ShapelessRecipe> recipes =
            Collections.newSetFromMap(new ConcurrentHashMap<ShapelessRecipe, Boolean>());
        registeredShapelessRecipes.get(plugin).put(recipe.getIngredients().size(), recipes);
      }
      failed =
          !registeredShapelessRecipes.get(plugin).get(recipe.getIngredients().size()).add(recipe)
              || failed;
    }

    if (allShapelessRecipes.get(recipe.getIngredients().size()) == null) {
      Set<ShapelessRecipe> recipes =
          Collections.newSetFromMap(new ConcurrentHashMap<ShapelessRecipe, Boolean>());
      allShapelessRecipes.put(recipe.getIngredients().size(), recipes);
    }
    failed = !allShapelessRecipes.get(recipe.getIngredients().size()).add(recipe) || failed;
    return !failed;
  }
  @Override
  public ShapelessRecipe matchShapelessRecipe(Plugin plugin, List<Material> materials) {
    Set<Material> unique = new HashSet<Material>();
    List<Material> parentList = new ArrayList<Material>();
    for (Material m : materials) {
      if (m.isSubMaterial()) {
        m = m.getParentMaterial();
      }
      parentList.add(m);
    }
    unique.addAll(parentList);
    unique.removeAll(Collections.singletonList(null));

    ShapelessRecipe recipe = null;
    if (registeredShapelessRecipes.containsKey(plugin)
        && registeredShapelessRecipes.get(plugin).containsKey(unique.size())) {
      for (ShapelessRecipe r : registeredShapelessRecipes.get(plugin).get(unique.size())) {
        if (r.getIncludeData()) {
          List<Material> materialsCopy = new ArrayList<Material>(materials);
          List<Material> ingredientsCopy = new ArrayList<Material>(r.getIngredients());
          Collections.sort(materialsCopy, new MaterialComparable());
          Collections.sort(ingredientsCopy, new MaterialComparable());
          if (materialsCopy.equals(ingredientsCopy)) {
            recipe = r;
            break;
          }
        } else {
          List<Material> parentsCopy = new ArrayList<Material>(parentList);
          List<Material> ingredientsCopy = new ArrayList<Material>(r.getIngredients());
          Collections.sort(parentsCopy, new MaterialComparable());
          Collections.sort(ingredientsCopy, new MaterialComparable());
          if (parentsCopy.equals(ingredientsCopy)) {
            recipe = r;
            break;
          }
        }
      }
    }
    if (recipe == null) {
      recipe = matchShapelessRecipe(materials);
    }

    return recipe;
  }