@AddonAccessible
public interface ActivatorModel extends Identifiable, Callable<Boolean> {

    /**
     * it this method returns false (and only if it returns false) it will not get restarted once stopped
     * <p>
     * Internally there is a limit of 100 times the activator is allowed to finish exceptionally (everything but
     * returning false)
     * </p>
     * @return true if the activator should get restarted, false if not
     * @throws Exception if unable to compute a result
     */
    @Override
    Boolean call() throws Exception;
} @AddonAccessible
public interface AddOnInformation extends Identifiable {
    /**
     * Gets the name of the addOn.
     *
     * @return The name of the addOn.
     */
    String getName();

    /**
     * Gets the version of the addOn.
     *
     * @return The version of the addOn.
     */
    Version getVersion();

    /**
     * Gets the provider, or the author, of the addOn.
     *
     * @return the author of the addOn.
     */
    String getProvider();

    /**
     * Gets the SDK version that this addOn uses.
     *
     * @return The SDK version that this addOn uses.
     */
    Version getSdkVersion();

    /**
     * Gets the serverID of the addOn, if it has one (used to match the addOn with the server).
     *
     * @return The serverID of the addOn, if it has one (used to match the addOn with the server).
     */
    Optional<Integer> getServerID();

    /**
     * Gets the artifactID of the addOn. This is the maven artifactID.
     *
     * @return The artifactID of the addOn. This is the maven artifactID.
     */
    String getArtifactID();

    /**
     * Gets the fully classified ID. An example would be intellimate.izou.addon.myaddon.
     *
     * @return The fully classified ID. An example would be intellimate.izou.addon.myaddon.
     */
    @Override
    String getID();
} @AddonAccessible
public interface AddOnModel extends ExtensionPoint, Identifiable {
    /**
     * Internal initiation of addOn - fake constructor, comes before prepare
     * @param context the context to initialize with
     */
    void initAddOn(Context context);

    /**
     * Use this method to register your Modules.
     * It will time out after 3 seconds!
     */
    void register();

    /**
     * Gets the associated Plugin.
     *
     * @return the Plugin.
     */
    PluginWrapper getPlugin();

    /**
     * Sets the Plugin IF it is not already set.
     *
     * @param plugin the plugin
     */
    void setPlugin(PluginWrapper plugin);
} @AddonAccessible
public interface Identifiable {
    /**
     * An ID must always be unique.
     * A Class like Activator or OutputPlugin can just provide their .class.getCanonicalName()
     * If you have to implement this interface multiple times, just concatenate unique Strings to
     * .class.getCanonicalName()
     * @return A String containing an ID
     */
    String getID();

    /**
     * checks whether this instance is the owner of this Identification
     * @param identification the identification to check
     * @return true if the same, false if not
     */
    default boolean isOwner(Identification identification) {
        return getID().equals(identification.getID());
    }
} @AddonAccessible
public interface Identification {
    /**
     * returns the ID of the owner of the Identification
     * @return a String containing the ID
     */
    String getID();

    /**
     * this method returns whether this Identification Object was created by the owner
     * @return true if created by the owner, false if not
     */
    boolean isCreatedFromInstance();
} @Deprecated
@AddonAccessible
public interface IdentificationManager extends IdentificationManagerM {
    /**
     * returns the IndentificationManager-Instance
     * @return the instance
     */
    @Deprecated
    static IdentificationManagerM getInstance() {
        return IdentificationManagerImpl.singletonInstance;
    }
} @AddonAccessible
//This Exception is more of a placeholder, but in future version we will certainly check if an ID is valid
public class IllegalIDException extends RuntimeException {
    /**
     * Constructs a new exception with {@code null} as its detail message.
     * The cause is not initialized, and may subsequently be initialized by a
     * call to {@link #initCause}.
     */
    public IllegalIDException() {
        super("The Identification does not fulfil the requirements");
    }

    /**
     * Constructs a new exception with the specified detail message.  The
     * cause is not initialized, and may subsequently be initialized by
     * a call to {@link #initCause}.
     *
     * @param message the detail message. The detail message is saved for
     *                later retrieval by the {@link #getMessage()} method.
     */
    public IllegalIDException(String message) {
        super(message);
    }
} @SuppressWarnings("FieldCanBeLocal")
public class Main {
    public static AtomicBoolean jfxToolKitInit;
    private static final long INIT_TIME_LIMIT = 10000;
    private final OutputManager outputManager;
    private final OutputControllerManager outputControllerManager;
    private final ResourceManager resourceManager;
    private final EventDistributor eventDistributor;
    private final LocalEventManager localEventManager;
    private final ActivatorManager activatorManager;
    private final AddOnManager addOnManager;
    private final AddOnInformationManager addOnInformationManager;
    private final FileManager fileManager;
    private final FilePublisher filePublisher;
    private final IzouLogger izouLogger;
    private final ThreadPoolManager threadPoolManager;
    private final SecurityManager securityManager;
    private final SystemInitializer systemInitializer;
    private final SoundManager soundManager;
    private final SystemMail systemMail;
    private final Logger fileLogger = LogManager.getLogger(this.getClass());
    private FileSystemManager fileSystemManager;

    /**
     * Creates a new Main instance with a optionally disabled lib-folder
     *
     * @param javaFX true if javaFX should be started, false otherwise
     * @param disableLibFolder  if true, izou will not load plugin from the lib-folder
     */
    private Main(boolean javaFX, boolean disableLibFolder) {
        this(null, javaFX, disableLibFolder);
    }

    /**
     * If you want to debug your Plugin, you can get an Main instance with this Method, JavaFX is disabled
     *
     * @param addOns a List of AddOns to run
     */
    public Main(List<AddOnModel> addOns) {
        this(addOns, false, false);
    }

    /**
     * If you want to debug your Plugin, you can get an Main instance with this Method
     *
     * @param disableLibFolder  if true, izou will not load plugin from the lib-folder
     * @param addOns a List of AddOns to run
     */
    public Main(List<AddOnModel> addOns, boolean disableLibFolder) {
        this(addOns, false, disableLibFolder);
    }

    /**
     * If you want to debug your Plugin, you can get an Main instance with this Method
     *
     * @param javaFX true if javaFX should be started, false otherwise
     * @param disableLibFolder  if true, izou will not load plugin from the lib-folder
     * @param addOns a List of AddOns to run
     */
    public Main(List<AddOnModel> addOns, boolean javaFX, boolean disableLibFolder) {
        fileLogger.debug("Starting Izou");
        fileLogger.debug("Initializing...");
        systemInitializer = initSystem();
        if (addOns != null) {
            fileLogger.debug("setting to debug");
            System.setProperty("debug", "true");
        }

        addOnInformationManager = new AddOnInformationManager(this); // Put this before the addOnManager, it needs it
        addOnManager = new AddOnManager(this);
        threadPoolManager = new ThreadPoolManager(this);
        izouLogger = new IzouLogger();
        outputManager = new OutputManager(this);
        outputControllerManager = new OutputControllerManager();
        resourceManager = new ResourceManager(this);
        eventDistributor = new EventDistributor(this);
        localEventManager = new LocalEventManager(this);
        threadPoolManager.getIzouThreadPool().submit(localEventManager);
        activatorManager = new ActivatorManager(this);
        filePublisher = new FilePublisher(this);
        soundManager = new SoundManager(this);

        fileManager = initFileManager();

        systemInitializer.registerWithPropertiesManager();

        setUpJavaFX(javaFX);

        fileLogger.debug("Done initializing.");
        fileLogger.debug("Adding addons..");

        // Starting security manager
        systemMail = initMail();
        securityManager = startSecurity(systemMail);

        if (addOns != null && !disableLibFolder) {
            fileLogger.debug("adding addons from the parameter without registering");
            addOnManager.addAddOnsWithoutRegistering(addOns);
        } else if (addOns != null) {
            fileLogger.debug("adding and registering addons from the parameter");
            addOnManager.addAndRegisterAddOns(addOns);
        }
        if (!disableLibFolder) {
            fileLogger.debug("retrieving addons & registering them");
            addOnManager.retrieveAndRegisterAddOns();
        }
    }

    public static void main(String[] args) {
        if (args.length > 0) {
            @SuppressWarnings("UnusedAssignment") Main main = new Main(Boolean.getBoolean(args[0]), false);
        } else {
            @SuppressWarnings("UnusedAssignment") Main main = new Main(false, false);
        }
    }

    private SystemMail initMail() {
        // Create system mail
        SystemMail mailTemp = null;
        try {
            mailTemp = SystemMail.createSystemMail();
        } catch (IllegalAccessException e) {
            fileLogger.fatal("Unable to create a system mail object");
        }
        return mailTemp;
    }

    private void setUpJavaFX(boolean javaFX) {
        // Starts javaFX if desired
        if (javaFX) {
            jfxToolKitInit = new AtomicBoolean(false);
            JavaFXInitializer.initToolKit();
            fileLogger.debug("Initializing JavaFX ToolKit");

            long startTime = System.currentTimeMillis();
            long duration = 0;
            while (!jfxToolKitInit.get() && duration < INIT_TIME_LIMIT) {
                duration = System.currentTimeMillis() - startTime;
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    fileLogger.error("Error happened while thread was sleeping", e);
                }
            }

            if (!jfxToolKitInit.get()) {
                fileLogger.error("Unable to Initialize JavaFX ToolKit");
            }
            fileLogger.debug("Done initializing JavaFX ToolKit");
        }
    }

    private SecurityManager startSecurity(SystemMail systemMail) {
        SecurityManager securityManagerTemp;
        try {
            securityManagerTemp = SecurityManager.createSecurityManager(systemMail, this);
        } catch (IllegalAccessException e) {
            securityManagerTemp = null;
            fileLogger.fatal("Security manager already exists", e);
        }
        if (!Boolean.getBoolean("noSecurity")) {
            try {
                System.setSecurityManager(securityManagerTemp);
            } catch (SecurityException e) {
                fileLogger.fatal("Security manager already exists", e);
            }
        }
        return securityManagerTemp;
    }

    private FileManager initFileManager() {
        FileManager fileManagerTemp;
        try {
            fileManagerTemp = new FileManager(this);
        } catch (IOException e) {
            fileManagerTemp = null;
            fileLogger.fatal("Failed to create the FileManager", e);
        }

        return fileManagerTemp;
    }

    public SoundManager getSoundManager() {
        return soundManager;
    }

    public SecurityManager getSecurityManager() {
        return securityManager;
    }

    public OutputManager getOutputManager() {
        return outputManager;
    }

    public OutputControllerManager getOutputControllerManager() {
        return outputControllerManager;
    }

    public LocalEventManager getLocalEventManager() {
        return localEventManager;
    }

    public ActivatorManager getActivatorManager() {
        return activatorManager;
    }

    public AddOnManager getAddOnManager() {
        return addOnManager;
    }

    public AddOnInformationManager getAddOnInformationManager() {
        return addOnInformationManager;
    }

    public ResourceManager getResourceManager() {
        return resourceManager;
    }

    public EventDistributor getEventDistributor() {
        return eventDistributor;
    }

    public ThreadPoolManager getThreadPoolManager() {
        return threadPoolManager;
    }

    public FileManager getFileManager() {
        return fileManager;
    }

    public FilePublisher getFilePublisher() {
        return filePublisher;
    }

    public IzouLogger getIzouLogger() {
        return izouLogger;
    }

    public FileSystemManager getFileSystemManager() {
        return fileSystemManager;
    }

    public SystemInitializer getSystemInitializer() {
        return systemInitializer;
    }

    private SystemInitializer initSystem() {
        // Setting up file system
        this.fileSystemManager = new FileSystemManager(this);
        try {
            fileSystemManager.createIzouFileSystem();
        } catch (IOException e) {
            fileLogger.fatal("Failed to create the FileSystemManager", e);
        }

        SystemInitializer systemInitializer = new SystemInitializer(this);
        systemInitializer.initSystem();
        return systemInitializer;
    }
} @AddonAccessible
public interface OutputControllerModel extends Identifiable {
    /**
     * Turns the external output device that this OutputControllerModel controls on.
     *
     * @return Returns true if the device was turned on successfully.
     */
    boolean turnOn();

    /**
     * Turns the external output device that this OutputControllerModel controls off
     *
     * @return Returns true if the device was turned off successfully.
     */
    boolean turnOff();
} @AddonAccessible
public interface OutputExtensionModel<X, T> extends Identifiable {
    /**
     * Checks if the outputExtension can execute with the current event
     *
     * @param event the event to check
     * @return the state of whether the outputExtension can execute with the current event
     */
    boolean canRun(EventModel event);

    /**
     * Gets the id of the output-plugin the outputExtension belongs to
     *
     * @return id of the output-plugin the outputExtension belongs to
     */
    String getPluginId();

    /**
     * Generates the data for the given Event
     *
     * @param event the event to generate for
     * @param t the optional argument
     * @return the result
     */
    X generate(EventModel event, T t);

    /**
     * Returns the ReturnType of the generic
     *
     * @return the type of the generic
     */
    //i don't think there is another way
    TypeToken<X> getReturnType();

    /**
     * returns the Type of the Argument or null if none
     * @return the type of the argument
     */
    //i don't think there is another way
    TypeToken<T> getArgumentType();
} @AddonAccessible
public interface OutputPluginModel<X, T> extends Runnable, Identifiable {
    /**
     * Adds an event to blockingQueue
     *
     * @param event the event to add
     * @throws IllegalStateException raised if problems adding an event to blockingQueue
     */
    void addToEventList(EventModel event);

    /**
     * callback method to notify that an OutputExtension was added
     * @param identification the Identification of the OutputExtension added
     */
    void outputExtensionAdded(Identification identification);

    /**
     * callback method to notify that an OutputExtension was added
     * @param identification the Identification of the OutputExtension added
     */
    void outputExtensionRemoved(Identification identification);

    /**
     * returns whether the OutputPlugin is running
     * @return true if it is running, false if not
     */
    boolean isRunning();

    /**
     * stops the OutputPlugin
     */
    void stop();

    /**
     * returns the Type of the one wants to receive from the OutputExtensions
     * @return the type of the generic
     */
    //i don't think there is another way
    TypeToken<T> getReceivingType();

    /**
     * returns the Type of the argument for the OutputExtensions, or null if none
     * @return the type of the Argument
     */
    //i don't think there is another way
    TypeToken<X> getArgumentType();
} @AddonAccessible
public interface ResourceModel<T> extends Identifiable {
    /**
     * returns the associated Resource data if set.
     * This method is thread-safe.
     * @return null or resource data
     */
    T getResource();

    /**
     * returns the ID of the Resource.
     * This method is thread-safe.
     * @return a String containing the ID of the resource
     */
    String getResourceID();

    /**
     * returns the provider of the Resource.
     * This method is thread-safe.
     * @return an Identification describing the provider of the Resource or null if not set
     */
    Identification getProvider();

    /**
     * returns whether a provider is set
     * @return true if this resource has an provider, false if not
     */
    boolean hasProvider();

    /**
     * returns the consumer of the object (if set).
     * @return null or an Identification describing the consumer of the Resource
     */
    Identification getConsumer();
} @AddonAccessible
@SuppressWarnings("UnusedDeclaration")
public interface ResourceBuilderModel extends Identifiable {
    /**
     * This method is called to register what resources the object provides.
     * just pass a List of Resources without Data in it.
     *
     * @return a List containing the resources the object provides
     */
    List<? extends ResourceModel> announceResources();
    /**
     * this method is called to register for what Events it wants to provide Resources.
     * <p>
     * The Event has to be in the following format: It should contain only one Descriptor and and one Resource with the
     * ID "description", which contains an description of the Event.
     * </p> 
     * @return a List containing ID's for the Events
     */
    List<? extends EventModel<?>> announceEvents();
    /**
     * This method is called when an object wants to get a Resource.
     *
     * <p>
     * Don't use the Resources provided as arguments, they are just the requests.
     * There is a timeout after 1 second.
     * </p> 
     * @param resources a list of resources without data
     * @param event if an event caused the action, it gets passed. It can also be null.
     * @return a list of resources with data
     */
    List<ResourceModel> provideResource(List<? extends ResourceModel> resources, Optional<EventModel> event);
} @AddonAccessible
public interface Context {
    /**
     * Returns the API used for interaction with Events.
     *
     * @return Events
     */
    Events getEvents();

    /**
     * Returns the API used for interaction with Resource.
     *
     * @return Resource
     */
    Resources getResources();

    /**
     * Returns the API used for interaction with Files.
     *
     * @return Files
     */
    Files getFiles();

    /**
     * Returns the API used to log.
     *
     * @return Logger
     */
    ExtendedLogger getLogger();

    /**
     * Returns the API used to manage the ThreadPool.
     *
     * @return ThreadPool
     */
    ThreadPool getThreadPool();

    /**
     * Returns the API to manage the Activators.
     *
     * @return Activator
     */
    Activators getActivators();

    /**
     * Returns the API used to manage the OutputPlugins, OutputExtensions and OutputControllers.
     *
     * @return Output
     */
    Output getOutput();

    /**
     * Retruns the API used to interact with Izou.
     *
     * @return System.
     */
    System getSystem();

    /**
     * Gets the API to manage the addOns.
     *
     * @return AddOns.
     */
    AddOns getAddOns();

    /**
     * Gets the Secure Storage object of Izou. {@link SecureStorageImpl} allows addOns to safely store data so that other
     * addOns cannot access it. However while the data is encrypted, there is no guarantee that the end user cannot
     * decrypt the data and access it. So be careful to not store any sensitive information here
     *
     * @return the Secure Storage object of Izou
     */
    static SecureStorage getSecureStorage() {
        return SecureStorageImpl.getInstance();
    }
} @AddonAccessible
public interface FileSubscriber {
    /**
     * Method that is called when a file pertaining to a reloadableFile is changed.
     */
    void update();
} @AddonAccessible
public interface ReloadableFile extends Identifiable{

    /**
     * Method that system-manager uses to update file when it has detected a change in it.
     * @param eventType the Type which caused the reloading
     */
    void reloadFile(String eventType);
} public class IzouLogger {
    private static final Logger rootLogger = LogManager.getRootLogger();
    private final Logger fileLogger = LogManager.getLogger(this.getClass());

    /**
     * Creates a new file-logger for an addOn. The logger will log to a file with the addOnId as name in the logs folder
     * of Izou
     *
     * @param addOnId the Id of the addOn the logger is created for
     * @param level level of logger (at what level of log the logger should be activated
     * @return the new logger
     */
    public synchronized ExtendedLogger createFileLogger(String addOnId, String level) {
        try {
            LoggerContext ctx = LogManager.getContext(false);
            Configuration config = ((org.apache.logging.log4j.core.LoggerContext) ctx).getConfiguration();

            //creates a new pattern layout (what determines how the log is formated, i.e. date, thread etc.)
            Layout layout = PatternLayout.createLayout("%d %-5p [%t] %C{10} (%F:%L) - %m%n", config, null, null, true,
                    false, null, null);

            //creates a file appender for the logger (so that it knows what file to log to)
            Appender fileAppender = FileAppender.createAppender("logs" + File.separator + addOnId + ".log", "true",
                    "false", "file", "true", "false", "false", "4000", layout, null, "false", null, config);
            fileAppender.start();
            config.addAppender(fileAppender);

            //creates also a console appender for the logger (so that the logger also outputs the log in the console)
            Appender consoleAppender = ConsoleAppender.createAppender(layout, null, "SYSTEM_OUT", "console", null, null);
            consoleAppender.start();
            config.addAppender(consoleAppender);

            //adds appenders to an array called refs. It will later serve as references to the logger as to what
            // appenders it has
            AppenderRef fileRef = AppenderRef.createAppenderRef("file", Level.DEBUG, null);
            AppenderRef consoleRef = AppenderRef.createAppenderRef("console", Level.DEBUG, null);
            AppenderRef[] refs = new AppenderRef[]{fileRef, consoleRef};

            //creates the logger configurations for the logger, where the appender-references are also added
            LoggerConfig loggerConfig = LoggerConfig.createLogger("false", Level.DEBUG, addOnId,
                    "true", refs, null, config, null);
            loggerConfig.addAppender(fileAppender, Level.DEBUG, null);
            loggerConfig.addAppender(consoleAppender, Level.DEBUG, null);

            //finally creates the logger and returns it
            config.addLogger(addOnId, loggerConfig);
            ((org.apache.logging.log4j.core.LoggerContext) ctx).updateLoggers();
            ctx.getLogger(addOnId);
            ExtendedLogger logger = ctx.getLogger(addOnId);
            return logger;
        } catch(Exception e) {
            fileLogger.error("Unable to create FileLogger",e);
            return null;
        }
    }
} @SuppressWarnings("NullableProblems")
public class TrackingExecutorService implements ExecutorService {
    private final ExecutorService executorService;
    @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
    private final Identifiable identifiable;

    private TrackingExecutorService(ExecutorService executorService, Identifiable identifiable) {
        this.executorService = executorService;
        this.identifiable = identifiable;
    }

    /**
     * creates a new ExecutorService
     * @param executorService the ExecutorService to use under the hood
     * @param identifiable the identifiable to associate each submitted thread with
     * @return an instance of TrackingExecutorService
     * @throws IllegalIDException not yet implemented
     */
    public static TrackingExecutorService createTrackingExecutorService(ExecutorService executorService,
                                                                        Identifiable identifiable) throws IllegalIDException {
        return new TrackingExecutorService(executorService, identifiable);
    }

    /**
     * method not implemented
     */
    @Override
    //(maybe set executorService to null?)
    public void shutdown() {}

    /**
     * method not fully implemented.
     * It will not return the List of waiting Runnables and after calling it is still possible to submit Tasks
     * @return empty list
     */
    @Override
    public List<Runnable> shutdownNow() {
        return new ArrayList<>();
    }

    /**
     * method not implemented, will always return false
     */
    @Override
    public boolean isShutdown() {
        return executorService.isShutdown();
    }

    /**
     * method not implemented, will always return false
     */
    @Override
    public boolean isTerminated() {
        return executorService.isTerminated();
    }

    /**
     * method not implemented, will do nothing
     */
    @Override
    public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
        return false;
    }

    /**
     * submits a Task to the ThreadPool
     * @param task the Task to submit
     * @param <T> the type of the return
     * @return null if unable to obtain Identification
     */
    @Override
    public <T> Future<T> submit(Callable<T> task) {
        return executorService.submit(task);
    }

    @Override
    public <T> Future<T> submit(Runnable task, T result) {
        return executorService.submit(task, result);
    }

    @Override
    public Future<?> submit(Runnable task) {
        return executorService.submit(task);
    }

    @Override
    public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException {
        return executorService.invokeAll(tasks);
    }

    @Override
    public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException {
        return executorService.invokeAll(tasks, timeout, unit);
    }

    @Override
    public <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException {
        return executorService.invokeAny(tasks);
    }

    @Override
    public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
        return executorService.invokeAny(tasks, timeout, unit);
    }

    @Override
    public void execute(Runnable command) {
        executorService.execute(command);
    }
} public class IdentifiableSet<X extends Identifiable> extends AbstractSet<X> implements Set<X>, Cloneable {
    private Set<X> set = new HashSet<>();

    public IdentifiableSet(Set<X> set) {
        this.set = set;
    }

    public IdentifiableSet() {}

    /**
     * Returns the number of elements in this set (its cardinality).  If this
     * set contains more than <tt>Integer.MAX_VALUE</tt> elements, returns
     * <tt>Integer.MAX_VALUE</tt>.
     *
     * @return the number of elements in this set (its cardinality)
     */
    @Override
    public int size() {
        return set.size();
    }

    /**
     * Returns <tt>true</tt> if this set contains no elements.
     *
     * @return <tt>true</tt> if this set contains no elements
     */
    @Override
    public boolean isEmpty() {
        return set.isEmpty();
    }

    /**
     * Returns <tt>true</tt> if this set contains the specified element.
     * More formally, returns <tt>true</tt> if and only if this set
     * contains an element <tt>e</tt> such that
     * <tt>(o==null&nbsp;?&nbsp;e==null&nbsp;:&nbsp;o.equals(e))</tt>.
     *
     * @param o element whose presence in this set is to be tested
     * @return <tt>true</tt> if this set contains the specified element
     * @throws ClassCastException   if the type of the specified element
     *                              is incompatible with this set
     *                              (<a href="Collection.html#optional-restrictions">optional</a>)
     * @throws NullPointerException if the specified element is null and this
     *                              set does not permit null elements
     *                              (<a href="Collection.html#optional-restrictions">optional</a>)
     */
    @Override
    public boolean contains(Object o) {
        return set.contains(o);
    }

    /**
     * Returns an iterator over the elements in this set. The elements are returned in no particular order.
     * @return an Iterator over the elements in this set
     */
    public Iterator<X> iterator() {
        return set.iterator();
    }

    /**
     * Adds an Element to the Set
     * @param x the Element
     * @return true if this set did not already contain the specified element
     * @throws java.lang.IllegalArgumentException if it is not allowed to put Elements without Identification in this
     *                                            Set
     */
    @Override
    public boolean add(X x) {
        return set.add(x);
    }

    /**
     * {@inheritDoc}
     *
     * <p>
     *     This implementation iterates over the collection looking for the specified element.  If it finds the element,
     *     it removes the element from the collection using the iterator's remove method.
     * </p>
     *
     * @param o the object to remove
     * @throws ClassCastException            {@inheritDoc}
     * @throws NullPointerException          {@inheritDoc}
     */
    @Override
    public boolean remove(Object o) {
        return set.remove(o);
    }

    /**
     * {@inheritDoc}
     * <p>This implementation iterates over this collection, removing each
     * element using the <tt>Iterator.remove</tt> operation.  Most
     * implementations will probably choose to override this method for
     * efficiency.
     * </p>
     */
    @Override
    public void clear() {
        set.clear();
    }

    /**
     * Returns a shallow copy of this HashSet instance: the elements themselves are not cloned.
     *
     * @return a shallow copy of this set
     */
    @Override
    public Object clone() {
        try {
            IdentifiableSet<X> newSet = (IdentifiableSet<X>) super.clone();
            newSet.set = (Set<X>) ((HashSet<X>) set).clone();
            return newSet;
        } catch (CloneNotSupportedException e) {
            throw new InternalError();
        }
    }
}
import org.intellimate.izou.activator.ActivatorModel;
import org.intellimate.izou.identification.AddOnInformation;
import org.intellimate.izou.addon.AddOnModel;
import org.intellimate.izou.events.*;
import org.intellimate.izou.identification.Identifiable;
import org.intellimate.izou.identification.Identification;
import org.intellimate.izou.identification.IdentificationManager;
import org.intellimate.izou.identification.IllegalIDException;
import org.intellimate.izou.main.Main;
import org.intellimate.izou.output.OutputControllerModel;
import org.intellimate.izou.output.OutputExtensionModel;
import org.intellimate.izou.output.OutputPluginModel;
import org.intellimate.izou.resource.ResourceModel;
import org.intellimate.izou.resource.ResourceBuilderModel;
import org.intellimate.izou.system.Context;
import org.intellimate.izou.system.file.FileSubscriber;
import org.intellimate.izou.system.file.ReloadableFile;
import org.intellimate.izou.system.logger.IzouLogger;
import org.intellimate.izou.threadpool.TrackingExecutorService;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.spi.ExtendedLogger;
import org.intellimate.izou.util.IdentifiableSet;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.function.Consumer;
        private class DistributorImpl implements EventsDistributor {
            /**
             * with this method you can register EventPublisher add a Source of Events to the System.
             * <p>
             * This method represents a higher level of abstraction! Use the EventManager to fire Events!
             * This method is intended for use cases where you have an entire new source of events (e.g. network)
             * @param identification the Identification of the Source
             * @return An Optional Object which may or may not contains an EventPublisher
             * @throws IllegalIDException not yet implemented
             */
            @Override
            public Optional<EventCallable> registerEventPublisher(Identification identification) throws IllegalIDException {
                return main.getEventDistributor().registerEventPublisher(identification);
            }

            /**
             * with this method you can unregister EventPublisher add a Source of Events to the System.
             * <p>
             * This method represents a higher level of abstraction! Use the EventManager to fire Events!
             * This method is intended for use cases where you have an entire new source of events (e.g. network)
             * @param identification the Identification of the Source
             */
            @Override
            public void unregisterEventPublisher(Identification identification) {
                main.getEventDistributor().unregisterEventPublisher(identification);
            }

            /**
             * Registers an EventController to control EventDispatching-Behaviour
             * <p>
             * Method is thread-safe.
             * It is expected that this method executes quickly.
             *
             * @param eventsController the EventController Interface to control event-dispatching
             * @throws IllegalIDException not yet implemented
             */
            @Override
            public void registerEventsController(EventsControllerModel eventsController) throws IllegalIDException {
                main.getEventDistributor().registerEventsController(eventsController);
            }

            /**
             * Unregisters an EventController
             * <p>
             * Method is thread-safe.
             *
             * @param eventsController the EventController Interface to remove
             */
            @Override
            public void unregisterEventsController(EventsControllerModel eventsController) {
                main.getEventDistributor().unregisterEventsController(eventsController);
            }

            /**
             * fires the event concurrently, this is generally discouraged.
             * <p>
             * This method should not be used for normal Events, for for events which obey the following laws:<br>
             * 1. they are time critical.<br>
             * 2. addons are not expected to react in any way beside a small update<br>
             * 3. they are few.<br>
             * if your event matches the above laws, you may consider firing it concurrently.
             * </p>
             *
             * @param eventModel the EventModel
             */
            @Override
            public void fireEventConcurrently(EventModel<?> eventModel) {
                main.getEventDistributor().fireEventConcurrently(eventModel);
            }

            /**
             * returns the ID of the Manager (EventsDistributor)
             */
            @Override
            public Identification getManagerIdentification() {
                Optional<Identification> identification = IdentificationManager.getInstance()
                        .getIdentification(main.getEventDistributor());
                if (!identification.isPresent()) {
                    //should not happen
                    throw new RuntimeException("unable to obtain ID for EventsDistributor");
                }
                return identification.get();
            }
        }
    }

    private class ResourcesImpl implements Resources {
        /**
         * registers a ResourceBuilder.
         * <p>
         *  this method registers all the events, resourcesID etc.
         * </p>
         * @param resourceBuilder an instance of the ResourceBuilder
         * @throws IllegalIDException not yet implemented
         */
        @Override
        public void registerResourceBuilder(ResourceBuilderModel resourceBuilder) throws IllegalIDException {
            main.getResourceManager().registerResourceBuilder(resourceBuilder);
        }

        /**
         * unregister a ResourceBuilder.
         * <p>
         * this method unregisters all the events, resourcesID etc.
         * @param resourceBuilder an instance of the ResourceBuilder
         */
        @Override
        public void unregisterResourceBuilder(ResourceBuilderModel resourceBuilder) {
            main.getResourceManager().unregisterResourceBuilder(resourceBuilder);
        }

        /**
         * generates a resources
         * <p>
         * @param resource the resource to request
         * @param consumer the callback when the ResourceBuilder finishes
         * @throws IllegalIDException not yet implemented
         */
        @Override
        @Deprecated
        public void generateResource(ResourceModel resource, Consumer<List<ResourceModel>> consumer) throws IllegalIDException {