package com.laytonsmith.core.extensions; import com.laytonsmith.PureUtilities.ClassLoading.ClassDiscovery; import com.laytonsmith.PureUtilities.ClassLoading.DynamicClassLoader; import com.laytonsmith.PureUtilities.Version; import com.laytonsmith.abstraction.StaticLayer; import com.laytonsmith.annotations.api; import com.laytonsmith.core.CHVersion; import com.laytonsmith.core.events.AbstractEvent; import com.laytonsmith.core.events.Driver; import com.laytonsmith.core.events.Event; import com.laytonsmith.core.events.EventMixinInterface; import com.laytonsmith.core.functions.FunctionBase; import java.lang.reflect.Constructor; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.EnumMap; import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; /** * Extension tracking and control class. * @author Jason Unger <entityreborn@gmail.com> */ public class ExtensionTracker { /* package */ String identifier; /* package */ Version version; /* package */ List<Extension> allExtensions; private final DynamicClassLoader dcl; private final ClassDiscovery cd; /* package */ final Map<api.Platforms, Map<String, FunctionBase>> functions; /* package */ final Map<String, Set<api.Platforms>> supportedPlatforms; /* package */ final Map<Driver, Set<Event>> events; /* package */ final URL container; /* package */ boolean isShutdown; public ExtensionTracker(URL container, ClassDiscovery cd, DynamicClassLoader dcl) { functions = new EnumMap<>(api.Platforms.class); supportedPlatforms = new HashMap<>(); for(api.Platforms p : api.Platforms.values()){ functions.put(p, new HashMap<String, FunctionBase>()); } this.events = new EnumMap<>(Driver.class); this.allExtensions = new ArrayList<>(); this.version = CHVersion.V0_0_0; this.container = container; this.cd = cd; this.dcl = dcl; } public void shutdownTracker() { // Remove as much as possible from memory. if (isShutdown) { return; } cd.removeDiscoveryLocation(container); cd.removePreCache(container); dcl.removeJar(container); isShutdown = true; } /** * @return the extensions known to this tracker */ public List<Extension> getExtensions() { return Collections.unmodifiableList(allExtensions); } /** * @return the functions */ public Set<FunctionBase> getFunctions() { Set<FunctionBase> retn = new HashSet<>(); for (Map<String, FunctionBase> function: functions.values()) { retn.addAll(function.values()); } return retn; } public void registerFunction(FunctionBase f) { api api = f.getClass().getAnnotation(api.class); api.Platforms [] platforms = api.platform(); if(!api.enabled()){ return; } if(supportedPlatforms.get(f.getName()) == null){ supportedPlatforms.put(f.getName(), EnumSet.noneOf(api.Platforms.class)); } supportedPlatforms.get(f.getName()).addAll(Arrays.asList(platforms)); for (api.Platforms platform : platforms) { try { functions.get(platform).put(f.getName(), f); } catch(UnsupportedOperationException e){ //This function isn't done yet, and during production this is a serious problem, //but it will be caught when we test all the functions, so for now just ignore it, //since this function is called during initial initialization } } } /** * @return the events */ public Set<Event> getEvents() { Set<Event> retn = new HashSet<>(); for (Set<Event> set: events.values()) { retn.addAll(set); } return retn; } public Set<Event> getEvents(Driver type) { Set<Event> retn = events.get(type); if (retn == null) { return Collections.emptySet(); } return retn; } public void registerEvent(Event e) { if(e instanceof AbstractEvent){ AbstractEvent ae = (AbstractEvent) e; //Get the mixin for this server, and add it to e Class mixinClass = StaticLayer.GetServerEventMixin(); try{ Constructor mixinConstructor = mixinClass.getConstructor(AbstractEvent.class); EventMixinInterface mixin = (EventMixinInterface) mixinConstructor.newInstance(e); ae.setAbstractEventMixin(mixin); } catch(Exception ex){ //This is a serious problem, and it should kill the plugin, for fast failure detection. throw new Error("Could not properly instantiate the mixin class. " + "The constructor with the signature \"public " + mixinClass.getSimpleName() + "(AbstractEvent e)\" is missing" + " from " + mixinClass.getName()); } } //Finally, add it to the list, and hook it. if(!events.containsKey(e.driver())){ events.put(e.driver(), new TreeSet<Event>()); } events.get(e.driver()).add(e); } /** * Get the internal identifier for this tracker. * @return */ public String getIdentifier() { return identifier; } /** * Get the internal version for this tracker. * @return */ public Version getVersion() { return version; } }