public class BehaviourRegistry implements IBehaviourRegistry {

    public static final BehaviourRegistry INSTANCE = new BehaviourRegistry();

    private final List<ShearMadnessBehaviour> behaviours = new LinkedList<>();

    @Override
    public void registerBehaviour(Function<ItemStack, Boolean> handlesVariant, Function<EntitySheep, BehaviourBase> behaviourFactory) {
        behaviours.add(new ShearMadnessBehaviour(handlesVariant, behaviourFactory));
    }

    public Iterable<BehaviourBase> getApplicableBehaviours(ItemStack itemStack, EntitySheep entity) {
        final Iterator<ShearMadnessBehaviour> baseIterator = behaviours.iterator();

        return () -> new BehaviourIterator(itemStack, baseIterator, entity);
    }

    private static class BehaviourIterator implements Iterator<BehaviourBase> {
        private final Iterator<ShearMadnessBehaviour> baseIterator;
        private final EntitySheep entity;
        private ShearMadnessBehaviour nextBehaviour = null;

        private final ItemStack itemStack;

        private BehaviourIterator(ItemStack itemStack, Iterator<ShearMadnessBehaviour> baseIterator, EntitySheep entity) {
            this.baseIterator = baseIterator;
            this.entity = entity;
            this.itemStack = itemStack;
        }

        @Override
        public boolean hasNext() {
            while (true) {
                if (!baseIterator.hasNext()) {
                    return false;
                }
                nextBehaviour = baseIterator.next();
                if (nextBehaviour.canHandleItemStack(itemStack)) {
                    return true;
                }
            }
        }

        @Override
        public BehaviourBase next() {
            if (nextBehaviour == null) {
                throw new NoSuchElementException("Invalid iteration over behaviours");
            }
            return nextBehaviour.createBehaviourBase(entity);
        }
    }
} public class RegisterAdditionalCapabilitiesEvent extends Event {
} public class RegisterShearMadnessBehaviourEvent extends Event
{
    private final IBehaviourRegistry registry;

    public RegisterShearMadnessBehaviourEvent(IBehaviourRegistry registry) {

        this.registry = registry;
    }

    /**
     * @return A registry to declare variation against.
     */
    public IBehaviourRegistry getRegistry()
    {
        return registry;
    }
} public class RegisterShearMadnessCommandEvent extends Event {

    public RegisterShearMadnessCommandEvent(List<CommandBase> subCommands) {
        this.subCommands = subCommands;
    }

    private List<CommandBase> subCommands;

    public void addCommand(CommandBase childCommand) {
        subCommands.add(childCommand);
    }
} public class ChiseledSheepCapability implements IChiseledSheepCapability, IWriteExtraData
{
    @Nullable
    private ItemStack itemStack = null;
    private int itemIdentifier;
    private NBTTagCompound customData = null;

    @Override
    public boolean isChiseled()
    {
        return itemStack != null;
    }

    @Override
    public void chisel(@Nonnull ItemStack itemStack)
    {
        this.itemStack = itemStack;
        itemIdentifier = ItemStackUtils.getHash(itemStack);
    }

    @Override
    public void unChisel() {
        itemStack = null;
        itemIdentifier = 0;
    }

    @Override
    public ItemStack getChiselItemStack()
    {
        return itemStack;
    }

    @Override
    public int getItemIdentifier()
    {
        return itemIdentifier;
    }

    @Override
    public NBTTagCompound getExtraData() {
        if (customData == null) {
            customData = new NBTTagCompound();
        }
        return customData;
    }

    @Override
    public void setExtraData(NBTTagCompound extraData) {
        customData = extraData;
    }

    @Override
    public String toString()
    {
        return Objects.toStringHelper(this)
                .add("itemIdentifier", itemIdentifier)
                .add("itemStack", itemStack)
                .add("isChiseled", isChiseled())
                .toString();
    }
} public class ChiseledSheepCapabilityStorage implements IStorage<IChiseledSheepCapability>
{
    public static final IStorage<IChiseledSheepCapability> instance = new ChiseledSheepCapabilityStorage();

    @Override
    public NBTBase writeNBT(Capability<IChiseledSheepCapability> capability, IChiseledSheepCapability instance, EnumFacing side)
    {
        final NBTTagCompound compound = new NBTTagCompound();
        compound.setBoolean("isChiseled", instance.isChiseled());
        if (instance.isChiseled())
        {
            final NBTTagCompound targetTag = new NBTTagCompound();
            instance.getChiselItemStack().writeToNBT(targetTag);
            compound.setTag("chiselDefinition", targetTag);
        }

        final NBTTagCompound extraData = instance.getExtraData();
        compound.setTag("extraData", extraData);
        return compound;
    }

    @Override
    public void readNBT(Capability<IChiseledSheepCapability> capability, IChiseledSheepCapability instance, EnumFacing side, NBTBase nbt)
    {
        final NBTTagCompound compound = (NBTTagCompound) nbt;

        final boolean isChiseled = compound.getBoolean("isChiseled");
        if (isChiseled)
        {
            final ItemStack stack = ItemStack.loadItemStackFromNBT(compound.getCompoundTag("chiselDefinition"));
            instance.chisel(stack);
        }

        if (instance instanceof  IWriteExtraData) {
            ((IWriteExtraData) instance).setExtraData(compound.getCompoundTag("extraData"));
        }
    }
} public interface IChiseledSheepCapability
{
    boolean isChiseled();

    void unChisel();

    void chisel(ItemStack itemStack);

    ItemStack getChiselItemStack();

    int getItemIdentifier();

    NBTTagCompound getExtraData();
} @SuppressWarnings({"OptionalUsedAsFieldOrParameterType", "NonSerializableFieldInSerializableClass"})
public enum ConfigurationHandler
{
    INSTANCE;
    private static final String CONFIG_VERSION = "2";
    private File fileRef = null;
    private Configuration config = null;
    private Optional<Configuration> configOld = Optional.empty();

    public static void init(File configFile)
    {
        INSTANCE.setConfig(configFile);
        MinecraftForge.EVENT_BUS.register(INSTANCE);
    }

    public Configuration getConfig()
    {
        return config;
    }

    private void setConfig(File configFile)
    {
        checkState(config == null, "ConfigurationHandler has been initialized more than once.");

        fileRef = configFile;

        config = new Configuration(configFile, CONFIG_VERSION);

        if (!CONFIG_VERSION.equals(config.getLoadedConfigVersion()))
        {
            final File fileBak = new File(fileRef.getAbsolutePath() + '_' + new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()) + ".old");
            Logger.warning("Your %s config file is out of date and could cause issues. The existing file will be renamed to %s and a new one will be generated.",
                    CommonReference.MOD_NAME, fileBak.getName());
            Logger.warning("%s will attempt to copy your old settings, but custom mod/tree settings will have to be migrated manually.", CommonReference.MOD_NAME);

            final boolean success = fileRef.renameTo(fileBak);
            Logger.warning("Rename %s successful.", success ? "was" : "was not");
            configOld = Optional.of(config);
            config = new Configuration(fileRef, CONFIG_VERSION);
        }

        syncConfig(true);
    }

    void syncConfig()
    {
        syncConfig(false);
    }

    @SuppressWarnings("OverlyBroadCatchBlock")
    private void syncConfig(boolean skipLoad)
    {
        if (!skipLoad)
        {
            try
            {
                config.load();
            } catch (final Exception e)
            {
                final File fileBak = new File(fileRef.getAbsolutePath() + '_' + new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()) + ".errored");
                Logger.severe("An exception occurred while loading your config file. This file will be renamed to %s and a new config file will be generated.", fileBak.getName());
                Logger.severe("Exception encountered: %s", e.getLocalizedMessage());

                final boolean success = fileRef.renameTo(fileBak);
                Logger.warning("Rename %s successful.", success ? "was" : "was not");

                config = new Configuration(fileRef, CONFIG_VERSION);
            }
            convertOldConfig();
        }

        MinecraftForge.EVENT_BUS.post(new ShearMadnessSyncSettingsEvent(config));

        Settings.syncConfig(config);


        saveConfig();
    }

    private void saveConfig()
    {
        if (config.hasChanged())
        {
            config.save();
        }
    }

    private void convertOldConfig()
    {
        if (configOld.isPresent())
        {
            // Handle old config versions (none yet)

            Settings.syncConfig(config);
            configOld = Optional.empty();
        }
    }

    @SubscribeEvent
    public void onConfigChanged(OnConfigChangedEvent event)
    {
        if (event.getModID().equalsIgnoreCase(CommonReference.MOD_ID))
        {
            saveConfig();
            syncConfig();
        }
    }

    @Override
    public String toString()
    {
        return Objects.toStringHelper(this)
                .add("fileRef", fileRef)
                .add("config", config)
                .add("configOld", configOld)
                .toString();
    }
} @SuppressWarnings({"StaticNonFinalField", "UtilityClass"})
public final class Proxies
{
    @SidedProxy(
            modId = CommonReference.MOD_ID,
            clientSide = "com.github.atomicblom.shearmadness.proxy.ClientRenderProxy",
            serverSide = "com.github.atomicblom.shearmadness.proxy.CommonRenderProxy")
    public static CommonRenderProxy renderProxy = null;

    @SidedProxy(
            modId = CommonReference.MOD_ID,
            clientSide = "com.github.atomicblom.shearmadness.proxy.ClientBlockProxy",
            serverSide = "com.github.atomicblom.shearmadness.proxy.CommonBlockProxy")
    public static CommonBlockProxy blockProxy = null;

    @SidedProxy(
            modId = CommonReference.MOD_ID,
            clientSide = "com.github.atomicblom.shearmadness.proxy.ClientForgeEventProxy",
            serverSide = "com.github.atomicblom.shearmadness.proxy.CommonForgeEventProxy")
    public static CommonForgeEventProxy forgeEventProxy = null;

    @SidedProxy(
            modId = CommonReference.MOD_ID,
            clientSide = "com.github.atomicblom.shearmadness.proxy.ClientAudioProxy",
            serverSide = "com.github.atomicblom.shearmadness.proxy.CommonAudioProxy")
    public static CommonAudioProxy audioProxy = null;

    private Proxies() {}
} public final class Reference
{
    public static final CreativeTabs CreativeTab = CreativeTabs.BUILDING_BLOCKS;
    public static final String MOD_GUI_FACTORY = "com.github.atomicblom.shearmadness.configuration.client.ModGuiFactory";
    public static final String BEHAVIOUR_COMMENT = "Sets the behaviour when a sheep is sheared\n" +
            "* RevertSheep - will change the sheep back to a normal sheep (default).\n" +
            "* ChiselFarm  - will allow the sheep to produce chiseled blocks (warning, this currently allows duping ores).\n" +
            "* CannotShear - You cannot shear the sheep while chiseled.\n";
    public static final String ALLOW_REDSTONE_COMMENT = "Chiseled redstone sheep will trigger Redstone circuits.";
    public static final String ALLOW_BOOKSHELF_COMMENT = "Chiseled bookshelf sheep will affect enchanting tables.";
    public static final String ALLOW_GLOWSTONE_COMMENT = "Chiseled glowstone sheep will light up the area around them.\n" +
            "WARNING: testing shows this creates a lot of chunk recalculation. I do not recommend this option.";
    public static final String ALLOW_CACTUS_COMMENT = "Chiseled Cactus sheep will deal damage players and destroy items.";
    public static final String ALLOW_TNT_COMMENT = "Chiseled TNT Sheep will be explode if exposed to active redstone.";
    public static final String ALLOW_FIRE_DAMAGE_COMMENT = "Chiseled Magma sheep will deal fire damage when touched.";
    public static final String DEBUG_MODELS = "Models will be regenerated every frame drawn.";
    public static final String DEBUG_INVISIBLE_BLOCKS = "Invisible Blocks will be shown in-game.";
    public static final String ALLOW_AUTO_CRAFTING = "Crafting Table sheep will use their recipe to consume and produce items.";

    private Reference() {}

    public static final class Blocks {
        public static final String NORMAL_VARIANT = "normal";

        public static final ResourceLocation InvisibleRedstone = new ResourceLocation(CommonReference.MOD_ID, "invisibleRedstone");
        public static final ResourceLocation InvisibleGlowstone = new ResourceLocation(CommonReference.MOD_ID, "invisibleGlowstone");
        public static final ResourceLocation InvisibleBookshelf = new ResourceLocation(CommonReference.MOD_ID, "invisibleBookshelf");

        private Blocks() {}
    }

} public class ShearMadnessCommand extends CommandBase {
    private final List<CommandBase> childCommands;

    public ShearMadnessCommand(List<CommandBase> childCommands) {
        this.childCommands = Lists.newArrayList(childCommands);
    }

    @Override
    public String getCommandName() {
        return "shearmadness";
    }

    @Override
    public String getCommandUsage(ICommandSender sender) {
        return "shearmadness [subcommand]";
    }

    @Override
    public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException {
        if (args.length == 0) {
            throw new CommandException("nosubcommandspecified");
        }
        final Optional<CommandBase> first = childCommands
                .stream()
                .sorted(Comparator.comparing(ICommand::getCommandName))
                .filter(command -> command.getCommandName().equals(args[0]))
                .findFirst();
        if (!first.isPresent()) {
            throw new CommandException("nosuchsubcommand");
        }
        final CommandBase commandBase = first.get();

        final String[] parameters = Arrays.copyOfRange(args, 1, args.length);

        commandBase.execute(server, sender, parameters);
    }

    @Override
    public List<String> getTabCompletionOptions(MinecraftServer server, ICommandSender sender, String[] args, @Nullable BlockPos pos) {

        if (args.length <= 1) {
            return childCommands
                    .stream()
                    .map(ICommand::getCommandName)
                    .sorted()
                    .filter(command -> command.startsWith(args[0]))
                    .collect(Collectors.toList());
        }

        final Optional<CommandBase> first = childCommands
                .stream()
                .sorted(Comparator.comparing(ICommand::getCommandName))
                .filter(command -> command.getCommandName() == args[0])
                .findFirst();

        if (!first.isPresent()) {
            return Lists.newArrayList();
        }

        final String[] parameters = Arrays.copyOfRange(args, 1, args.length - 2);

        return first.get().getTabCompletionOptions(server, sender, parameters, pos);
    }
} public final class CommonReference {
    public static final String MOD_ID = "shearmadness";
    public static final String MOD_NAME = "Shear Madness";
    public static final String VERSION = "@MOD_VERSION@";

    private CommonReference() {}
}
import com.github.atomicblom.shearmadness.api.BehaviourRegistry;
import com.github.atomicblom.shearmadness.api.events.RegisterAdditionalCapabilitiesEvent;
import com.github.atomicblom.shearmadness.api.events.RegisterShearMadnessBehaviourEvent;
import com.github.atomicblom.shearmadness.api.events.RegisterShearMadnessCommandEvent;
import com.github.atomicblom.shearmadness.capability.ChiseledSheepCapability;
import com.github.atomicblom.shearmadness.capability.ChiseledSheepCapabilityStorage;
import com.github.atomicblom.shearmadness.api.capability.IChiseledSheepCapability;
import com.github.atomicblom.shearmadness.configuration.ConfigurationHandler;
import com.github.atomicblom.shearmadness.networking.*;
import com.github.atomicblom.shearmadness.proxy.Proxies;
import com.github.atomicblom.shearmadness.utility.Reference;
import com.github.atomicblom.shearmadness.utility.ShearMadnessCommand;
import com.github.atomicblom.shearmadness.variations.CommonReference;
import com.google.common.collect.Lists;
import net.minecraft.command.CommandBase;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.common.capabilities.CapabilityManager;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.Mod.EventHandler;
import net.minecraftforge.fml.common.event.FMLInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
import net.minecraftforge.fml.common.event.FMLServerStartingEvent;
import net.minecraftforge.fml.common.network.NetworkRegistry;
import net.minecraftforge.fml.common.network.simpleimpl.SimpleNetworkWrapper;
import net.minecraftforge.fml.relauncher.Side;
import java.util.ArrayList;
import java.util.List;
package com.github.atomicblom.shearmadness;



@SuppressWarnings("MethodMayBeStatic")
@Mod(modid = CommonReference.MOD_ID, version = CommonReference.VERSION, guiFactory = Reference.MOD_GUI_FACTORY, dependencies = "required-after:chisel@[MC1.10.2-0.0.7.7,)", acceptedMinecraftVersions = "[1.9.4, 1.11)")
public class ShearMadnessMod
{
    public static final SimpleNetworkWrapper CHANNEL = NetworkRegistry.INSTANCE.newSimpleChannel(CommonReference.MOD_ID);

    @EventHandler
    public void preInit(FMLPreInitializationEvent event)
    {
        ConfigurationHandler.init(event.getSuggestedConfigurationFile());