/******************************************************************************* * gMix open source project - https://svs.informatik.uni-hamburg.de/gmix/ * Copyright (C) 2014 SVS * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. *******************************************************************************/ package staticContent.evaluation.simulator.gui.pluginRegistry; import java.awt.GraphicsDevice; import java.awt.GraphicsEnvironment; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Observer; import java.util.Set; import java.util.Vector; import javax.swing.JDialog; import javax.swing.JOptionPane; import org.apache.log4j.Level; import org.apache.log4j.Logger; import org.reflections.Reflections; import org.reflections.scanners.FieldAnnotationsScanner; import org.reflections.scanners.TypeAnnotationsScanner; import org.reflections.util.ClasspathHelper; import staticContent.evaluation.simulator.annotations.helper.PossibleValues; import staticContent.evaluation.simulator.annotations.plugin.Plugin; import staticContent.evaluation.simulator.annotations.plugin.PluginSuperclass; import staticContent.evaluation.simulator.annotations.plugin.SimGuiPlugin; import staticContent.evaluation.simulator.annotations.property.BoolProp; import staticContent.evaluation.simulator.annotations.property.BoolSimulationProperty; import staticContent.evaluation.simulator.annotations.property.DoubleProp; import staticContent.evaluation.simulator.annotations.property.DoubleSimulationProperty; import staticContent.evaluation.simulator.annotations.property.FloatProp; import staticContent.evaluation.simulator.annotations.property.FloatSimulationProperty; import staticContent.evaluation.simulator.annotations.property.IntProp; import staticContent.evaluation.simulator.annotations.property.IntSimulationProperty; import staticContent.evaluation.simulator.annotations.property.SimProp; import staticContent.evaluation.simulator.annotations.property.StringProp; import staticContent.evaluation.simulator.annotations.property.StringSimulationProperty; /** * The simprop registry * * Reads annotations. * Build simprop and plugin data models. * Manages simprop and plugin changes. * * @author alex * */ public class SimPropRegistry { private static Logger logger = Logger.getLogger(SimPropRegistry.class); private static SimPropRegistry _instance = null; private static String currentConfigFile; private Boolean unsavedChanges = false; /** * Singleton * * @return an instance of {@link SimPropRegistry} */ public static SimPropRegistry getInstance() { if (_instance == null) { _instance = new SimPropRegistry(); } return _instance; } // private final List<String> pluginLayer; @SuppressWarnings("rawtypes") private final List<Vector> deferList = new LinkedList<Vector>(); private int numberOfPluginLayers; @SuppressWarnings("unchecked") private final Map<String, String>[] pluginLayerMap = new HashMap[100]; /** * Maps all registered properties. * key: config name of the property * value: property */ private final Map<String, SimProp> properties = new HashMap<String, SimProp>(); /** * Stores the properties which should be varied. * key: config name of the property * value: values */ private final Map<String, String> propertiesToVary = new HashMap<String, String>(); /** * Maps the active plugins. Active plungins are those, which are enabled in the GUI. * key: name of plugin level * value: name of the plugin */ private final Map<String, String> activePlugins = new HashMap<String, String>(); /** * Maps the active plugins. Active plungins are those, which are enabled in the GUI. * key: name of the plugin * value: name of plugin level */ private final Map<String, String> activePluginsMapped = new HashMap<String, String>(); /** * Maps between the display name of a plugin layer and the config name. * key: display name of the plugin layer * value: config name of the plugin layer */ private final Map<String, String> layerMapDisplayNameToConfigName = new LinkedHashMap<String, String>(); /** * Maps between the display name of a plugin layer and the config name. * key: config name of the plugin layer * value: display name of the plugin layer */ private final Map<String, String> layerMapConfigNameToDisplayName = new LinkedHashMap<String, String>(); /** * Maps the registered plugins to the corresponding plugin layer. * key: config name of the plugin * value: display name of the plugin layer */ private final Map<String, Integer> staticConfigurationDisplay = new HashMap<String, Integer>(); private final Map<String, String> registeredPlugins = new HashMap<String, String>(); /** * Maps plugin ids to the corresponding plugin objects * key: id of the plugin * value: plugin object */ private final Map<String, SimGuiPlugin> plugins = new HashMap<String, SimGuiPlugin>(); /** * Maps an unique string to possible values (this is useful for predefined values for * the simprops) * key: id of the target * value: possibles Values (comma separated Strings) */ private final Map<String, String> possibleValueTargets = new HashMap<String, String>(); /** * Maps a layer display name to the order of the layer * key: layer display name (this is like a group name for a bunch of plugins) * value: order (position in the gui) */ private final Map<String, Integer> layerMapDisplayNameToOrder = new HashMap<String, Integer>(); /** * Maps a layer config name to the order of the layer * key: layer config name (layers can be considered as an abstraction for a group of plugins) * value: order (position in the gui) */ private final Map<String, Integer> layerMapConfigNameToOrder = new HashMap<String, Integer>(); /** * Maps a layer name to the * key: layer name * value: a bool that indicates whether the layer is static or dynamic (this influences * the section in which corresponding gui elements are displayed) */ private final Map<String, Boolean> isStaticLayerMap = new HashMap<String, Boolean>(); // TODO: Merge both order functions /** * @return the mapping between the order (position in the gui) * and the display name of a layer */ public Map<String, Integer> getLayerMapDisplayNameToOrder() { return this.layerMapDisplayNameToOrder; } /** * @return the mapping between the order (position in the gui) * and the config name of a layer */ public Map<String, Integer> getLayerMapConfigNameToOrder() { return this.layerMapConfigNameToOrder; } /** * @return a map that indicates if a layer is static or not. * static layers are those which are not directly connected * to a plugin. */ public Map<String, Boolean> getIsStaticLayerMap() { return this.isStaticLayerMap; } /** * constructor of the simpropregistry class. */ private SimPropRegistry() { // there are some properties that cant be caught by annotations this.propertiesToVary.put("PROPERTY_TO_VARY", ""); this.propertiesToVary.put("VALUES_FOR_THE_PROPERTY_TO_VARY", ""); this.propertiesToVary.put("USE_SECOND_PROPERTY_TO_VARY", ""); this.propertiesToVary.put("SECOND_PROPERTY_TO_VARY", ""); this.propertiesToVary.put("VALUES_FOR_THE_SECOND_PROPERTY_TO_VARY", ""); // scan predefined enum values this.scanForHelpers(); this.numberOfPluginLayers = 0; // scan plugins (dynamic) this.scanForPluginProperties(); // process defered simprops this.processDefered(); // scan static properties // this has to be done at least because it might // be possible that some properties go into // dynamic plugin sections this.scanForStaticProperties(); // this.toString(); // just for debugging } /** * Scans the enum values of annotated enums * e.g. desired experiments */ private void scanForHelpers() { Reflections reflectionsPlugins = new Reflections( ClasspathHelper.forPackage("staticContent.evaluation.simulator"), new TypeAnnotationsScanner()); Reflections reflectionsPlugins2 = new Reflections( ClasspathHelper.forPackage("userGeneratedContent.simulatorPlugIns"), new TypeAnnotationsScanner()); reflectionsPlugins = reflectionsPlugins.merge(reflectionsPlugins2); // Look for classes with PluginAnnotation Set<Class<?>> types = reflectionsPlugins.getTypesAnnotatedWith(staticContent.evaluation.simulator.annotations.helper.PossibleValues.class); for (Class<?> target : types) { String tmp = ""; // for all fields check if it is an enum // if so concatenate the enum constant to a comaseperated list of enum values for (Field f : target.getFields()){ if (f.isEnumConstant()) tmp = tmp + f.getName() + ","; } possibleValueTargets.put(target.getAnnotation(PossibleValues.class).id(),tmp); logger.log( Level.DEBUG ,"Registered PossibleValuesTarget: " + target.getAnnotation(PossibleValues.class).id()); } } // TODO: remove this peace of code /** * @return the entry set of all simprops */ public Set<Entry<String, SimProp>> getAllSimProps() { return this.getProperties().entrySet(); } public Map<String, String>[] getPluginLayerMap() { return this.pluginLayerMap; } // TODO: remove this peace of code /** * searches a simprop with a specific id * @param key is the id of a simprop * @return the correcponding simprop */ public SimProp getValue(String key) { return this.getProperties().get(key); } /** * registers a plugin * @param s simprop * @param isSuperClass has to be true if s is a property that is defined in a class annotated as a pluginsuperclass * @param isGlobal has to be true if s has a global (not static) annotation * @param pluginLayer id of the corresponding layer (plugin group) */ private void register(SimProp s, boolean isSuperClass, boolean isGlobal, String pluginLayer) { if (this.getProperties().containsKey(s.getPropertyID()) && !this.getProperties().get( s.getPropertyID() ).getPluginLayerID().equals(this.displayNameToConfigName(pluginLayer))) { // Error case GraphicsDevice graphicsDevice = GraphicsEnvironment .getLocalGraphicsEnvironment().getDefaultScreenDevice(); int x = graphicsDevice.getDisplayMode().getWidth(); int y = graphicsDevice.getDisplayMode().getHeight(); if ( !this.getProperties().get( s.getPropertyID() ).equals(pluginLayer) ) { JOptionPane alert = new JOptionPane("Redefinition of property '" + s.getPropertyID() + "' at superclass level detected \n (" + this.getProperties().get( s.getPropertyID() ).getPluginLayerID() + "" + ", " + this.displayNameToConfigName(pluginLayer) + ") \n" + "Please fix the problem!"); JDialog dialog = alert.createDialog(null, ""); int w = dialog.getWidth(); int h = dialog.getHeight(); dialog.setLocation((x / 2) - (w / 2), (y / 2) - (h / 2)); dialog.setVisible(true); System.exit(-1); }else{ JOptionPane alert = new JOptionPane("Redefinition of " + s.getPropertyID() + " at pluginlevel (" + s.getPluginID() + ") detected!"); JDialog dialog = alert.createDialog(null, ""); int w = dialog.getWidth(); int h = dialog.getHeight(); dialog.setLocation((x / 2) - (w / 2), (y / 2) - (h / 2)); dialog.setVisible(true); } } else if ( !this.getProperties().containsKey(s.getPropertyID()) && isSuperClass) { // superclass case logger.log(Level.DEBUG, "Associate superclass property " + s.getPropertyID() + " with " + s.getPluginLayerID()); s.setIsGlobal(true); this.getProperties().put(s.getPropertyID(), s); } else if ( !this.getProperties().containsKey(s.getPropertyID()) && isGlobal ) { // global case logger.log(Level.DEBUG, s.getPropertyID() + " with " + s.getPluginLayerID() + " is forced to be global"); s.setIsGlobal(true); this.getProperties().put(s.getPropertyID(), s); } else { // normal case logger.log(Level.DEBUG, "Register property (" + s.getPropertyID() + ", " + s.getPluginID() + ", " + s.getPluginLayerID() + ")"); s.setIsGlobal(false); this.getProperties().put(s.getPropertyID(), s); } } /** * Scans static simulation properties. Only handles PropertyAnnoations with inject parameter. */ private void scanForStaticProperties() { Reflections reflections = new Reflections( ClasspathHelper.forPackage("userGeneratedContent.simulatorPlugIns"), new FieldAnnotationsScanner()); Set<Field> fields = reflections.getFieldsAnnotatedWith(staticContent.evaluation.simulator.annotations.property.IntSimulationProperty.class); fields.addAll(reflections.getFieldsAnnotatedWith(staticContent.evaluation.simulator.annotations.property.FloatSimulationProperty.class)); fields.addAll(reflections.getFieldsAnnotatedWith(staticContent.evaluation.simulator.annotations.property.DoubleSimulationProperty.class)); fields.addAll(reflections.getFieldsAnnotatedWith(staticContent.evaluation.simulator.annotations.property.StringSimulationProperty.class)); fields.addAll(reflections.getFieldsAnnotatedWith(staticContent.evaluation.simulator.annotations.property.BoolSimulationProperty.class)); List<Class<? extends Annotation> > annotationTypes = new LinkedList<Class<? extends Annotation> >(); annotationTypes.add( IntSimulationProperty.class ); annotationTypes.add( BoolSimulationProperty.class ); annotationTypes.add( FloatSimulationProperty.class ); annotationTypes.add( DoubleSimulationProperty.class ); annotationTypes.add( StringSimulationProperty.class ); SimProp property; boolean globalProperty = true; for ( Field field : fields ){ Annotation[] a = field.getAnnotations(); for (Annotation element : a) { if (element.annotationType() == BoolSimulationProperty.class) { BoolSimulationProperty annotation = field.getAnnotation(BoolSimulationProperty.class); if ( !annotation.inject().equals("") ){ InjectionParser injection = new InjectionParser( annotation.inject(), annotation.key() ); String layerDisplayName = injection.getLayerDisplayName(); String layerConfigName = injection.getLayerConfigName(); String pluginDisplayName = injection.getPluginDisplayName(); String pluginConfigName = injection.getPluginConfigName(); int layerPosition = injection.getLayerPosition(); // int pluginPosition = injection.getPluginPosition(); property = new BoolProp(); property.setId(annotation.key()); property.setName(annotation.name()); property.setTooltip("Key: " + annotation.key() + "\n " + annotation.tooltip()); property.setPluginLayerID(layerConfigName); property.setPluginID(pluginConfigName); property.setEnable_requirements(annotation.enable_requirements()); property.setValue_requirements(annotation.value_requirements()); property.setEnable(true); property.setInfo(annotation.info()); property.setPosition(annotation.position()); globalProperty = annotation.global() || injection.isGlobalProperty(); property.setIsGlobal(globalProperty); property.setIsStatic(annotation.isStatic()); property.isPropertyToVary(annotation.property_to_vary()); if(property.isStatic()){ property.setPluginID(""); } this.getProperties().put(property.getPropertyID(), property); if ( !this.getLayerMapDisplayNameToConfigName().containsKey(layerDisplayName)){ logger.log( Level.DEBUG , "Register plugin layer (" + layerConfigName + ", " + layerDisplayName + ")"); logger.log( Level.DEBUG, "Set position for injected plugin layer " + layerConfigName + " to " + layerPosition); this.getLayerMapDisplayNameToConfigName().put(layerDisplayName, layerConfigName); this.layerMapConfigNameToDisplayName.put(layerConfigName, layerDisplayName); this.layerMapDisplayNameToOrder.put( layerDisplayName, layerPosition ); this.layerMapConfigNameToOrder.put( layerConfigName, layerPosition ); this.isStaticLayerMap.put(layerDisplayName,property.isStatic()); } else if ( !globalProperty && !property.isStatic()){ this.registerPlugin(pluginDisplayName, layerDisplayName, true); } } } if (element.annotationType() == IntSimulationProperty.class) { IntSimulationProperty annotation = field.getAnnotation(IntSimulationProperty.class); if ( !annotation.inject().equals("") ){ InjectionParser injection = new InjectionParser( annotation.inject(), annotation.key() ); String layerDisplayName = injection.getLayerDisplayName(); String layerConfigName = injection.getLayerConfigName(); String pluginDisplayName = injection.getPluginDisplayName(); String pluginConfigName = injection.getPluginConfigName(); int layerPosition = injection.getLayerPosition(); // int pluginPosition = injection.getPluginPosition(); property = new IntProp(); property.setId(annotation.key()); property.setName(annotation.name()); property.setTooltip("Key: " + annotation.key() + "\n " + annotation.tooltip()); property.setPluginLayerID(layerConfigName); property.setPluginID(pluginConfigName); property.setEnable_requirements(annotation.enable_requirements()); property.setValue_requirements(annotation.value_requirements()); property.setEnable(true); property.setInfo(annotation.info()); property.setPosition(annotation.position()); globalProperty = annotation.global() || injection.isGlobalProperty(); property.setIsGlobal(globalProperty); property.setIsStatic(annotation.isStatic()); property.isPropertyToVary(annotation.property_to_vary()); if(property.isStatic()){ property.setPluginID(""); } // IntProp specific annotation processing ((IntProp) property).setMinValue(annotation.min()); ((IntProp) property).setMaxValue(annotation.max()); ((IntProp) property).setEnableAuto(annotation.enableAuto()); ((IntProp) property).setEnableUnlimited(annotation.enableUnlimited()); ((IntProp) property).setStepSize(annotation.stepSize()); ((IntProp) property).setGuiElement(annotation.guiElement()); this.getProperties().put(property.getPropertyID(), property); if ( !this.getLayerMapDisplayNameToConfigName().containsKey(layerDisplayName)){ logger.log( Level.DEBUG , "Register plugin layer (" + layerConfigName + ", " + layerDisplayName + ")"); logger.log( Level.DEBUG, "Set position for injected plugin layer " + layerConfigName + " to " + layerPosition); this.getLayerMapDisplayNameToConfigName().put(layerDisplayName, layerConfigName); this.layerMapConfigNameToDisplayName.put(layerConfigName, layerDisplayName); this.layerMapDisplayNameToOrder.put( layerDisplayName, layerPosition ); this.layerMapConfigNameToOrder.put( layerConfigName, layerPosition ); this.isStaticLayerMap.put(layerDisplayName,property.isStatic()); } else if ( !globalProperty && !property.isStatic()){ this.registerPlugin(pluginDisplayName, layerDisplayName, true); } } } if (element.annotationType() == FloatSimulationProperty.class) { FloatSimulationProperty annotation = field.getAnnotation(FloatSimulationProperty.class); if ( !annotation.inject().equals("") ){ InjectionParser injection = new InjectionParser( annotation.inject(), annotation.key() ); String layerDisplayName = injection.getLayerDisplayName(); String layerConfigName = injection.getLayerConfigName(); String pluginDisplayName = injection.getPluginDisplayName(); String pluginConfigName = injection.getPluginConfigName(); int layerPosition = injection.getLayerPosition(); // int pluginPosition = injection.getPluginPosition(); property = new FloatProp(); property.setId(annotation.key()); property.setName(annotation.name()); property.setTooltip("Key: " + annotation.key() + "\n " + annotation.tooltip()); property.setPluginLayerID(layerConfigName); property.setPluginID(pluginConfigName); property.setEnable_requirements(annotation.enable_requirements()); property.setValue_requirements(annotation.value_requirements()); property.setEnable(true); property.setInfo(annotation.info()); property.setPosition(annotation.position()); globalProperty = annotation.global() || injection.isGlobalProperty(); property.setIsGlobal(globalProperty); property.setIsStatic(annotation.isStatic()); property.isPropertyToVary(annotation.property_to_vary()); if(property.isStatic()){ property.setPluginID(""); } ((FloatProp) property).setMinValue(annotation.min()); ((FloatProp) property).setMaxValue(annotation.max()); ((FloatProp) property).setEnableAuto(annotation.enableAuto()); ((FloatProp) property).setEnableUnlimited(annotation.enableUnlimited()); ((FloatProp) property).setStepSize(annotation.stepSize()); ((FloatProp) property).setGuiElement(annotation.guiElement()); this.getProperties().put(property.getPropertyID(), property); if ( !this.getLayerMapDisplayNameToConfigName().containsKey(layerDisplayName)){ logger.log( Level.DEBUG , "Register plugin layer (" + layerConfigName + ", " + layerDisplayName + ")"); logger.log( Level.DEBUG, "Set position for injected plugin layer " + layerConfigName + " to " + layerPosition); this.getLayerMapDisplayNameToConfigName().put(layerDisplayName, layerConfigName); this.layerMapConfigNameToDisplayName.put(layerConfigName, layerDisplayName); this.layerMapDisplayNameToOrder.put( layerDisplayName, layerPosition ); this.layerMapConfigNameToOrder.put( layerConfigName, layerPosition ); this.isStaticLayerMap.put(layerDisplayName,property.isStatic()); } else if ( !globalProperty && !property.isStatic()){ this.registerPlugin(pluginDisplayName, layerDisplayName, true); } } } if (element.annotationType() == DoubleSimulationProperty.class) { DoubleSimulationProperty annotation = field.getAnnotation(DoubleSimulationProperty.class); if ( !annotation.inject().equals("") ){ InjectionParser injection = new InjectionParser( annotation.inject(), annotation.key() ); String layerDisplayName = injection.getLayerDisplayName(); String layerConfigName = injection.getLayerConfigName(); String pluginDisplayName = injection.getPluginDisplayName(); String pluginConfigName = injection.getPluginConfigName(); int layerPosition = injection.getLayerPosition(); // int pluginPosition = injection.getPluginPosition(); property = new DoubleProp(); property.setId(annotation.key()); property.setName(annotation.name()); property.setTooltip("Key: " + annotation.key() + "\n " + annotation.tooltip()); property.setPluginLayerID(layerConfigName); property.setPluginID(pluginConfigName); property.setEnable_requirements(annotation.enable_requirements()); property.setValue_requirements(annotation.value_requirements()); property.setEnable(true); property.setInfo(annotation.info()); property.setPosition(annotation.position()); globalProperty = annotation.global() || injection.isGlobalProperty(); property.setIsGlobal(globalProperty); property.setIsStatic(annotation.isStatic()); property.isPropertyToVary(annotation.property_to_vary()); if(property.isStatic()){ property.setPluginID(""); } ((DoubleProp) property).setMinValue(annotation.min()); ((DoubleProp) property).setMaxValue(annotation.max()); ((DoubleProp) property).setEnableAuto(annotation.enableAuto()); ((DoubleProp) property).setEnableUnlimited(annotation.enableUnlimited()); ((DoubleProp) property).setStepSize(annotation.stepSize()); ((DoubleProp) property).setGuiElement(annotation.guiElement()); this.getProperties().put(property.getPropertyID(), property); if ( !this.getLayerMapDisplayNameToConfigName().containsKey(layerDisplayName)){ logger.log( Level.DEBUG , "Register plugin layer (" + layerConfigName + ", " + layerDisplayName + ")"); logger.log( Level.DEBUG, "Set position for injected plugin layer " + layerConfigName + " to " + layerPosition); this.getLayerMapDisplayNameToConfigName().put(layerDisplayName, layerConfigName); this.layerMapConfigNameToDisplayName.put(layerConfigName, layerDisplayName); this.layerMapDisplayNameToOrder.put( layerDisplayName, layerPosition ); this.layerMapConfigNameToOrder.put( layerConfigName, layerPosition ); this.isStaticLayerMap.put(layerConfigName,property.isStatic()); } else if ( !globalProperty && !property.isStatic()){ this.registerPlugin(pluginDisplayName, layerDisplayName, true); } } } if (element.annotationType() == StringSimulationProperty.class) { StringSimulationProperty annotation = field.getAnnotation(StringSimulationProperty.class); if ( !annotation.inject().equals("") ){ InjectionParser injection = new InjectionParser( annotation.inject(), annotation.key() ); String layerDisplayName = injection.getLayerDisplayName(); String layerConfigName = injection.getLayerConfigName(); String pluginDisplayName = injection.getPluginDisplayName(); String pluginConfigName = injection.getPluginConfigName(); int layerPosition = injection.getLayerPosition(); // int pluginPosition = injection.getPluginPosition(); property = new StringProp(); property.setId(annotation.key()); property.setName(annotation.name()); property.setTooltip("Key: " + annotation.key() + "\n " + annotation.tooltip()); property.setPluginLayerID(layerConfigName); property.setPluginID(pluginConfigName); property.setEnable_requirements(annotation.enable_requirements()); property.setValue_requirements(annotation.value_requirements()); property.setEnable(true); property.setInfo(annotation.info()); property.setPosition(annotation.position()); globalProperty = annotation.global() || injection.isGlobalProperty(); property.setIsGlobal(globalProperty); property.setIsStatic(annotation.isStatic()); property.isPropertyToVary(annotation.property_to_vary()); if(property.isStatic()){ property.setPluginID(""); } // ((StringProp) property).setValue(annotation.value()); String regex = "@\\w*"; if (annotation.possibleValues().matches(regex)){ ((StringProp) property).setPossibleValues(possibleValueTargets.get("StatisticsType")); }else{ ((StringProp) property).setPossibleValues(annotation.possibleValues()); } ((StringProp) property).setMultiSelection(annotation.multiSelection()); this.getProperties().put(property.getPropertyID(), property); if ( !this.getLayerMapDisplayNameToConfigName().containsKey(layerDisplayName)){ logger.log( Level.DEBUG , "Register plugin layer (" + layerConfigName + ", " + layerDisplayName + ")"); logger.log( Level.DEBUG, "Set position for injected plugin layer " + layerConfigName + " to " + layerPosition); this.getLayerMapDisplayNameToConfigName().put(layerDisplayName, layerConfigName); this.layerMapConfigNameToDisplayName.put(layerConfigName, layerDisplayName); this.layerMapDisplayNameToOrder.put( layerDisplayName, layerPosition ); this.layerMapConfigNameToOrder.put( layerConfigName, layerPosition ); this.isStaticLayerMap.put(layerConfigName,property.isStatic()); } else if ( !globalProperty && !property.isStatic()){ this.registerPlugin(pluginDisplayName, layerDisplayName, true); } } } } } // Field[] fieldArray = fields.toArray( new Field[fields.size()]); // readFields(null, fieldArray, null, false); } /** * Scans plugin dependent simulation properties */ @SuppressWarnings("unused") public void scanForPluginProperties() { SimGuiPlugin plugin; // TODO: Seems not to work properly, scans all packages Reflections reflectionsPlugins = new Reflections( ClasspathHelper.forPackage("userGeneratedContent.simulatorPlugIns"), new TypeAnnotationsScanner()); // Look for classes with PluginAnnotation Set<Class<?>> types = reflectionsPlugins.getTypesAnnotatedWith(staticContent.evaluation.simulator.annotations.plugin.Plugin.class); for (Class<?> pluginClass : types) { Plugin pluginAnnotation = pluginClass.getAnnotation(Plugin.class); plugin = new SimGuiPlugin(); plugin.setId(pluginClass.getName()); plugin.setConfigName(pluginAnnotation.pluginKey()); plugin.setDisplayName(pluginAnnotation.pluginName()); plugin.setPluginLayer(pluginAnnotation.pluginLayerKey()); plugin.isVisible(pluginAnnotation.visible()); plugin.isGlobal(pluginAnnotation.global()); plugin.allowGlobalFields(pluginAnnotation.allowGlobalFields()); // This is the direct superclass. The direct superclass does not need // to be annotated with @PluginSuperclass. It also can be annotated with // @Plugin but then it must have the same name Class<?> directSuperlass = pluginClass.getSuperclass(); // Find the annotated superclass Class<?> pluginSuperclass = plugin.getPluginSuperclass( pluginClass ); // If there is a plugin layer provided by the plugin annotation, a plugin can // be registered drectly ( e.g. StopAndGoMessage.java ). // Otherwise the we try to find the pluginSuperclasse an use its name ( e.g. StopAndGo.java ). boolean autodetectPluginLayer = pluginAnnotation.pluginLayerKey().equals(""); if ( !autodetectPluginLayer ){ logger.log(Level.DEBUG, "Disable autodetect for plugin " + plugin.getConfigName() ); // we need this for invisible plugins plugin.setFallbackLayer(pluginAnnotation.pluginLayerKey()); // Although autodetection is disabled, it is a good idea to check if there is a superclass // with @PluginSuperclass annotation. // Reason: This superclass might have some properties! if ( pluginSuperclass != null ) { PluginSuperclass pluginSuperclassAnnotation = pluginSuperclass.getAnnotation( PluginSuperclass.class ); // prefer pluginLayer from @PluginSuperclass annotation // let's overwrite it! String pluginSuperclassLayerKey = pluginSuperclassAnnotation.layerKey(); String pluginSuperclassLayerName = pluginSuperclassAnnotation.layerName(); plugin.setPluginLayer( pluginSuperclassLayerKey ); // we need this for invisible plugins plugin.setFallbackLayer(pluginSuperclassLayerKey); // Process PluginSuperclass fields if not already done if ( !this.getLayerMapDisplayNameToConfigName().containsKey( pluginSuperclassLayerName )){ // We can/must read PluginSuperclass fields without registering a plugin this.readFields( plugin, pluginSuperclass.getDeclaredFields(), pluginSuperclassLayerKey, true ); this.getLayerMapDisplayNameToConfigName().put( pluginSuperclassLayerName, pluginSuperclassLayerKey); this.layerMapConfigNameToDisplayName.put( pluginSuperclassLayerKey, pluginSuperclassLayerName ); } } // We have to defer the plugin registration, since now it is not clear // if there is another class of the same plugin that has a PluginSuperclass, // which has higher priority when setting the display name of the layer. this.deferedReadFields( plugin, pluginClass.getDeclaredFields(), pluginAnnotation.pluginLayerKey(), false ); // Now it is safe to register the plugin and read the fields // registerPlugin( plugin.getName(), plugin.getPluginLayer(), plugin.isVisible() ); // readFields( plugin, pluginClass.getDeclaredFields(), pluginAnnotation.pluginLayerKey(), false ); }else{ // AUTODETECT PLUGINLAYER (Lookup superclass) logger.log(Level.DEBUG, "Enable autodetect for plugin " + plugin.getConfigName() + ", PluginSuperclass=" + pluginSuperclass.getSimpleName()); String layerDisplayName = ""; String layerConfigName = ""; // Process PluginSuperclass information if ( pluginSuperclass != null ){ PluginSuperclass pluginSuperclassAnnotation = pluginSuperclass.getAnnotation( PluginSuperclass.class ); // Case: Invalid inheritance // If the direct superclass has a normal plugin annotation, the // provided name must match the actual plugin name. if (directSuperlass.isAnnotationPresent( Plugin.class ) && (directSuperlass.getAnnotation( Plugin.class ).pluginKey() != pluginAnnotation.pluginKey()) ){ logger.log( Level.ERROR , "Check the annotation in class " + directSuperlass.getCanonicalName() + " it should be @Plugin( name = \""+ plugin.getConfigName() +"\" ... ) or a valid @PluginSuperclass annotation."); } // Set the pluginLayer String pluginSuperclassLayerKey = pluginSuperclassAnnotation.layerKey(); String pluginSuperclassLayerName = pluginSuperclassAnnotation.layerName(); plugin.setPluginLayer( pluginSuperclassLayerKey ); // we need this for invisible plugins plugin.setFallbackLayer(pluginSuperclassLayerKey); // Process PluginSuperclass fields if not already done if ( !this.getLayerMapDisplayNameToConfigName().containsKey( pluginSuperclassLayerName )){ logger.log(Level.DEBUG, "Register PluginSuperclass for " + pluginSuperclassLayerKey); // register the plugin layer manually logger.log(Level.DEBUG, "Associate plugin layer (" + pluginSuperclassLayerKey + ", " + pluginSuperclassLayerName + ")"); this.readFields( plugin, pluginSuperclass.getDeclaredFields(), pluginSuperclassLayerKey, true ); this.getLayerMapDisplayNameToConfigName().put( pluginSuperclassLayerName, pluginSuperclassLayerKey); this.layerMapConfigNameToDisplayName.put( pluginSuperclassLayerKey, pluginSuperclassLayerName ); int layerPosition = pluginSuperclass.getAnnotation( PluginSuperclass.class ).position(); logger.log( Level.DEBUG, "Set position for plugin layer " + pluginSuperclassLayerKey + " to " + layerPosition); this.layerMapDisplayNameToOrder.put( pluginSuperclassLayerName, layerPosition ); this.layerMapConfigNameToOrder.put( pluginSuperclassLayerKey, layerPosition ); // PluginSuperclasses can provide fake plugins. Fake plugins will occur in the // jcombobox of the corresponding plugin level (see TopologyScript.java) String fakePlugins = pluginSuperclass.getAnnotation( PluginSuperclass.class ).fakePlugins(); if ( !fakePlugins.equals("") ){ String[] fakedPlugins = fakePlugins.split(","); for (String fakedPlugin : fakedPlugins) { String[] splitPlugin = fakedPlugin.split(":"); SimGuiPlugin fakePlugin = new SimGuiPlugin(); if ( splitPlugin.length >= 1){ fakePlugin.setConfigName(splitPlugin[0]); } if ( splitPlugin.length > 1 ){ fakePlugin.setDisplayName(splitPlugin[1]); } fakePlugin.setPluginLayer(pluginSuperclass.getAnnotation( PluginSuperclass.class ).layerKey()); fakePlugin.isVisible(true); plugins.put(fakedPlugin, fakePlugin); logger.log(Level.DEBUG, "Inject fake plugin " + fakedPlugin + " to " + layerConfigName ); this.registerPlugin(fakedPlugin, pluginSuperclass.getAnnotation( PluginSuperclass.class ).layerKey(), true); } } }else{ logger.log(Level.DEBUG, plugin.getConfigName() + " is caped by a superclass " + pluginSuperclass.getAnnotation( PluginSuperclass.class ).layerKey()); this.registerPlugin( plugin.getConfigName(), pluginSuperclass.getAnnotation( PluginSuperclass.class ).layerKey(), plugin.isVisible() ); } } else{ // The plugin is not O.K.! Each plugin must be caped by a pluginSuperclass logger.log(Level.ERROR, plugin.getConfigName() + " is not caped by a superclass"); System.exit(-1); } PluginSuperclass pluginSuperclassAnnotation = pluginSuperclass.getAnnotation( PluginSuperclass.class ); String pluginSuperclassLayerName = pluginSuperclassAnnotation.layerName(); this.deferedReadFields( plugin, pluginClass.getDeclaredFields(), layerConfigName, false ); } this.plugins.put(plugin.getConfigName(), plugin); } // call initial dependency-check for per plugin configurations DependencyChecker.checkAll(this); } /** * Deferes the registration of simprops (fields of plugins). * @param plugin is the corresponding plugin * @param declaredFields are the fields of the class that is annotated as a plugin * @param pluginLayerKey is the id of the layer the plugin belongs to * @param isSuperclass has to be true if the plugin registration was triggered by reading a pluginsuperclass annotation */ private void deferedReadFields(SimGuiPlugin plugin, Field[] declaredFields, String pluginLayerKey, boolean isSuperclass) { logger.log(Level.DEBUG, "Defer " + plugin.getConfigName() ); Vector<Object> deferInformation = new Vector<Object>(); deferInformation.add( plugin ); deferInformation.add( declaredFields ); deferInformation.add( pluginLayerKey); deferInformation.add( isSuperclass ); this.deferList.add( deferInformation ); } /** * Processes the list of defered plugin registrations */ private void processDefered(){ for ( Vector<Object> deferInformation : this.deferList ){ SimGuiPlugin plugin = (SimGuiPlugin) deferInformation.get( 0 ); Field[] declaredFields = (Field[]) deferInformation.get( 1 ); String pluginLayerKey = (String) deferInformation.get( 2 ); boolean isSuperclass = (boolean) deferInformation.get( 3 ); this.registerPlugin( plugin.getConfigName(), plugin.getPluginLayer(), plugin.isVisible() ); this.readFields( plugin, declaredFields, pluginLayerKey, isSuperclass ); } } /** * * Registers a plugin name with a plugin layer. * * @param plugin A plugin name e.g. * @param plugInLayer A player name e.g. * @param isVisible A boolean which specifies whether the plugin name will be * registered with a plugin layer (ture) or not (false). If a plugin is not * registerd with a plugin layer, it will be invisible whithin the gui. */ private void registerPlugin(String plugin, String plugInLayer, boolean isVisible) { if ( isVisible ){ GraphicsDevice graphicsDevice = GraphicsEnvironment .getLocalGraphicsEnvironment().getDefaultScreenDevice(); int x = graphicsDevice.getDisplayMode().getWidth(); int y = graphicsDevice.getDisplayMode().getHeight(); // Check if this plugin is already known for a different pluginlayer. // In this case show an alert an exit. The Pluginprogrammer has to fix ist if ( this.getRegisteredPlugins().containsKey(plugin) ){ if ( !this.getRegisteredPlugins().get(plugin).equals(plugInLayer) ){ JOptionPane alert = new JOptionPane("Reuse of Plugin name '" + plugin + "' in plugin layers: \n" + this.getRegisteredPlugins().get(plugin) + " \n" + plugInLayer + " \n" + "Please fix the problem!"); JDialog dialog = alert.createDialog(null, ""); int w = dialog.getWidth(); int h = dialog.getHeight(); dialog.setLocation((x / 2) - (w / 2), (y / 2) - (h / 2)); dialog.setVisible(true); System.exit(-1); } } // Otherwise, register the plugin the pluginlayer. // Plugins which are registered to a pluginlayer will occur in the // pluginlayer's corresponging jcombobox! // Plugins which are not registered are not visible in the gui. else { logger.log(Level.DEBUG, "Register plugin (" + plugin + ", " + plugInLayer + ")"); this.getRegisteredPlugins().put(plugin, plugInLayer); } } else{ logger.log(Level.DEBUG, "Plugin (" + plugin + ", " + plugInLayer + ") is set to invisible"); } } public Map<String, String> getRegisteredPlugins(){ return this.registeredPlugins; } /** * Registers the simprops (fields) for a plugin (as plugin annotated class) * @param plugin is the corresponding plugin * @param declaredFields are the fields of the class that is annotated as a plugin * @param pluginLayerKey is the id of the layer the plugin belongs to * @param isSuperclass has to be true if the plugin registration was triggered by reading a pluginsuperclass annotation */ private void readFields(SimGuiPlugin plugin, Field[] fields, String plugInLayer, boolean isSuperClass ) { // Skip invisible plugins if ( !plugin.isVisible() && !(plugin.isGlobal() || plugin.allowGlobalFields()) ){ logger.log(Level.DEBUG, plugin.getConfigName() + " is ignored due to isVisible=" + plugin.isVisible() + " and makeFieldsGlobal=" + plugin.isGlobal() + " and allowGlobalFields=" + plugin.allowGlobalFields()); return; } SimProp property; try { for (Field field : fields ) { Annotation[] a = field.getAnnotations(); for (Annotation element : a) { boolean isGlobal = false; if (element.annotationType() == BoolSimulationProperty.class) { BoolSimulationProperty annotation = field.getAnnotation(BoolSimulationProperty.class); property = new BoolProp(); if (annotation != null) { if ( !annotation.inject().equals("") ){ logger.log(Level.DEBUG, "Skip " + annotation.key() + " from " + plugin.getConfigName() + " has injection annotation"); continue; } // Uninjected properties are global under following conditions: // They are PluginSuperclass properties OR the if plugin allows global Properties. // If the plugin allows global properties each property which is annotated as global // will become a global plugin layer property ( e.g. LineChartPlotterCf.java ) isGlobal = isSuperClass || ( plugin.isGlobal() || annotation.global() ); property.setId(annotation.key()); if ( !isGlobal ){ property.setPluginID(plugin.getConfigName()); property.setIsGlobal( false ); }else{ property.setPluginID(""); property.setIsGlobal( true ); } logger.log(Level.DEBUG, annotation.key() + " is global=" + property.isGlobal() + " so plugin is " + property.getPluginID() ); property.setIsSuperclass( isSuperClass ); property.setPluginLayerID(plugInLayer); property.setName(annotation.name()); property.setTooltip("Key: " + annotation.key() + "\n " + annotation.tooltip()); property.setInfo(annotation.info()); property.setPosition(annotation.position()); property.isPropertyToVary(annotation.property_to_vary()); // This is why we have to defer all readFields() calls // Explanation: We call getPluginLayer() but the internal list which is used by this // function builds up dynamically. Therefore, it is possible to get an empty string or // null, if we have bad timing! // CAUTION: do not touch these expressions, we make use of lazy evaluation String possiblePluginLayer = this.getPluginLayer(plugin.getConfigName()); if ( (possiblePluginLayer != null) && !possiblePluginLayer.isEmpty() ){ property.setPluginLayerID(this.getPluginLayer(plugin.getConfigName())); }else{ if ( !plugin.getFallbackLayer().isEmpty() ){ logger.log(Level.DEBUG, "Fallback to " + plugin.getFallbackLayer()); property.setPluginLayerID(plugin.getFallbackLayer()); }else{ logger.log(Level.ERROR, "Can not determine the Layer for " + property.getName() + "! Reason unknown"); System.exit(-1); } } logger.log(Level.DEBUG, annotation.key() + " set layer to " + this.getPluginLayer(plugin.getConfigName())); property.setEnable_requirements(annotation.enable_requirements()); property.setValue_requirements(annotation.value_requirements()); property.setEnable(true); // ((BoolProp) property).setValue(annotation.value()); } this.register(property, isSuperClass, isGlobal, plugInLayer); } else if (element.annotationType() == IntSimulationProperty.class) { IntSimulationProperty annotation = field .getAnnotation(IntSimulationProperty.class); property = new IntProp(); if (annotation != null) { if ( !annotation.inject().equals("") ){ logger.log(Level.DEBUG, "Skip " + annotation.key() + " from " + plugin.getConfigName() + " has injection annotation"); continue; } // Uninjected properties are global under following conditions: // They are PluginSuperclass properties OR the if plugin allows global Properties. // If the plugin allows global properties each property which is annotated as global // will become a global plugin layer property ( e.g. LineChartPlotterCf.java ) isGlobal = isSuperClass || ( plugin.isGlobal() || annotation.global() ); property.setId(annotation.key()); if ( !isGlobal ){ property.setPluginID(plugin.getConfigName()); property.setIsGlobal( false ); }else{ property.setPluginID(""); property.setIsGlobal( true ); } logger.log(Level.DEBUG, annotation.key() + " is global=" + property.isGlobal() + " so plugin is " + property.getPluginID() ); property.setIsSuperclass( isSuperClass ); property.setPluginLayerID(plugInLayer); property.setName(annotation.name()); property.setTooltip("Key: " + annotation.key() + ", " + annotation.tooltip()); property.setInfo(annotation.info()); property.setPosition(annotation.position()); property.isPropertyToVary(annotation.property_to_vary()); // This is why we have to defer all readFields() calls // Explanation: We call getPluginLayer() but the internal list which is used by this // function builds up dynamically. Therefore, it is possible to get an empty string or // null, if we have bad timing! // CAUTION: do not touch these expressions, we make use of lazy evaluation String possiblePluginLayer = this.getPluginLayer(plugin.getConfigName()); if ( (possiblePluginLayer != null) && !possiblePluginLayer.isEmpty() ){ property.setPluginLayerID(this.getPluginLayer(plugin.getConfigName())); }else{ if ( !plugin.getFallbackLayer().isEmpty() ){ logger.log(Level.DEBUG, "Fallback to " + plugin.getFallbackLayer()); property.setPluginLayerID(plugin.getFallbackLayer()); }else{ logger.log(Level.ERROR, "Can not determine the Layer for " + property.getName() + "! Reason unknown"); System.exit(-1); } } logger.log(Level.DEBUG, annotation.key() + " set layer to " + this.getPluginLayer(plugin.getConfigName())); property.setEnable_requirements(annotation.enable_requirements()); property.setValue_requirements(annotation.value_requirements()); property.setEnable(true); ((IntProp) property).setMinValue(annotation.min()); ((IntProp) property).setMaxValue(annotation.max()); ((IntProp) property).setEnableAuto(annotation.enableAuto()); ((IntProp) property).setEnableUnlimited(annotation.enableUnlimited()); ((IntProp) property).setStepSize(annotation.stepSize()); ((IntProp) property).setGuiElement(annotation.guiElement()); } this.register(property, isSuperClass, isGlobal, plugInLayer); } else if (element.annotationType() == FloatSimulationProperty.class) { FloatSimulationProperty annotation = field .getAnnotation(FloatSimulationProperty.class); property = new FloatProp(); if (annotation != null) { if ( !annotation.inject().equals("") ){ logger.log(Level.DEBUG, "Skip " + annotation.key() + " from " + plugin.getConfigName() + " has injection annotation"); continue; } // Uninjected properties are global under following conditions: // They are PluginSuperclass properties OR the if plugin allows global Properties. // If the plugin allows global properties each property which is annotated as global // will become a global plugin layer property ( e.g. LineChartPlotterCf.java ) isGlobal = isSuperClass || ( plugin.isGlobal() || annotation.global() ); property.setId(annotation.key()); if ( !isGlobal ){ property.setPluginID(plugin.getConfigName()); property.setIsGlobal( false ); }else{ property.setPluginID(""); property.setIsGlobal( true ); } logger.log(Level.DEBUG, annotation.key() + " is global=" + property.isGlobal() + " so plugin is " + property.getPluginID() ); property.setIsSuperclass( isSuperClass ); property.setPluginLayerID(plugInLayer); property.setName(annotation.name()); property.setTooltip("Key: " + annotation.key() + ", " + annotation.tooltip()); property.setInfo(annotation.info()); property.setPosition(annotation.position()); property.isPropertyToVary(annotation.property_to_vary()); // This is why we have to defer all readFields() calls // Explanation: We call getPluginLayer() but the internal list which is used by this // function builds up dynamically. Therefore, it is possible to get an empty string or // null, if we have bad timing! // CAUTION: do not touch these expressions, we make use of lazy evaluation String possiblePluginLayer = this.getPluginLayer(plugin.getConfigName()); if ( (possiblePluginLayer != null) && !possiblePluginLayer.isEmpty() ){ property.setPluginLayerID(this.getPluginLayer(plugin.getConfigName())); }else{ if ( !plugin.getFallbackLayer().isEmpty() ){ logger.log(Level.DEBUG, "Fallback to " + plugin.getFallbackLayer()); property.setPluginLayerID(plugin.getFallbackLayer()); }else{ logger.log(Level.ERROR, "Can not determine the Layer for " + property.getName() + "! Reason unknown"); System.exit(-1); } } logger.log(Level.DEBUG, annotation.key() + " set layer to " + this.getPluginLayer(plugin.getConfigName())); property.setEnable_requirements(annotation.enable_requirements()); property.setValue_requirements(annotation.value_requirements()); property.setEnable(true); ((FloatProp) property).setMinValue(annotation.min()); ((FloatProp) property).setMaxValue(annotation.max()); ((FloatProp) property).setEnableAuto(annotation.enableAuto()); ((FloatProp) property).setEnableUnlimited(annotation.enableUnlimited()); ((FloatProp) property).setStepSize(annotation.stepSize()); ((FloatProp) property).setGuiElement(annotation.guiElement()); } this.register(property, isSuperClass, isGlobal, plugInLayer); } else if (element.annotationType() == DoubleSimulationProperty.class) { DoubleSimulationProperty annotation = field .getAnnotation(DoubleSimulationProperty.class); property = new DoubleProp(); if (annotation != null) { if ( !annotation.inject().equals("") ){ logger.log(Level.DEBUG, "Skip " + annotation.key() + " from " + plugin.getConfigName() + " has injection annotation"); continue; } // Uninjected properties are global under following conditions: // They are PluginSuperclass properties OR the if plugin allows global Properties. // If the plugin allows global properties each property which is annotated as global // will become a global plugin layer property ( e.g. LineChartPlotterCf.java ) isGlobal = isSuperClass || ( plugin.isGlobal() || annotation.global() ); property.setId(annotation.key()); if ( !isGlobal ){ property.setPluginID(plugin.getConfigName()); property.setIsGlobal( false ); }else{ property.setPluginID(""); property.setIsGlobal( true ); } logger.log(Level.DEBUG, annotation.key() + " is global=" + property.isGlobal() + " so plugin is " + property.getPluginID() ); property.setIsSuperclass( isSuperClass ); property.setPluginLayerID(plugInLayer); property.setName(annotation.name()); property.setTooltip("Key: " + annotation.key() + ", " + annotation.tooltip()); property.setInfo(annotation.info()); property.setPosition(annotation.position()); property.isPropertyToVary(annotation.property_to_vary()); // This is why we have to defer all readFields() calls // Explanation: We call getPluginLayer() but the internal list which is used by this // function builds up dynamically. Therefore, it is possible to get an empty string or // null, if we have bad timing! // CAUTION: do not touch these expressions, we make use of lazy evaluation String possiblePluginLayer = this.getPluginLayer(plugin.getConfigName()); if ( (possiblePluginLayer != null) && !possiblePluginLayer.isEmpty() ){ property.setPluginLayerID(this.getPluginLayer(plugin.getConfigName())); }else{ if ( !plugin.getFallbackLayer().isEmpty() ){ logger.log(Level.DEBUG, "Fallback to " + plugin.getFallbackLayer()); property.setPluginLayerID(plugin.getFallbackLayer()); }else{ logger.log(Level.ERROR, "Can not determine the Layer for " + property.getName() + "! Reason unknown"); System.exit(-1); } } logger.log(Level.DEBUG, annotation.key() + " set layer to " + this.getPluginLayer(plugin.getConfigName())); property.setEnable_requirements(annotation.enable_requirements()); property.setValue_requirements(annotation.value_requirements()); property.setEnable(true); ((DoubleProp) property).setMinValue(annotation.min()); ((DoubleProp) property).setMaxValue(annotation.max()); ((DoubleProp) property).setEnableAuto(annotation.enableAuto()); ((DoubleProp) property).setEnableUnlimited(annotation.enableUnlimited()); ((DoubleProp) property).setStepSize(annotation.stepSize()); ((DoubleProp) property).setGuiElement(annotation.guiElement()); } this.register(property, isSuperClass, isGlobal, plugInLayer); } else if (element.annotationType() == StringSimulationProperty.class) { StringSimulationProperty annotation = field .getAnnotation(StringSimulationProperty.class); property = new StringProp(); if (annotation != null) { if ( !annotation.inject().equals("") ){ logger.log(Level.DEBUG, "Skip " + annotation.key() + " from " + plugin.getConfigName() + " has injection annotation"); continue; } // Uninjected properties are global under following conditions: // 1. The property is a PluginSuperclass property. // The corresponding plugin is global. // The property itself is global ( e.g. LineChartPlotterCf.java ) isGlobal = isSuperClass || ( plugin.isGlobal() || annotation.global() ); property.setId(annotation.key()); if ( !isGlobal ){ property.setPluginID(plugin.getConfigName()); property.setIsGlobal( false ); }else{ property.setPluginID(""); property.setIsGlobal( true ); } logger.log(Level.DEBUG, annotation.key() + " is global=" + property.isGlobal() + " so plugin is " + property.getPluginID() ); property.setIsSuperclass( isSuperClass ); property.setPluginLayerID(plugInLayer); property.setName(annotation.name()); property.setTooltip("Key: " + annotation.key() + ", " + annotation.tooltip()); property.setInfo(annotation.info()); property.setPosition(annotation.position()); property.isPropertyToVary(annotation.property_to_vary()); // This is why we have to defer all readFields() calls // Explanation: We call getPluginLayer() but the internal list which is used by this // function builds up dynamically. Therefore, it is possible to get an empty string or // null, if we have bad timing! // CAUTION: do not touch these expressions, we make use of lazy evaluation String possiblePluginLayer = this.getPluginLayer(plugin.getConfigName()); if ( (possiblePluginLayer != null) && !possiblePluginLayer.isEmpty() ){ property.setPluginLayerID(this.getPluginLayer(plugin.getConfigName())); }else{ if ( !plugin.getFallbackLayer().isEmpty() ){ logger.log(Level.DEBUG, "Fallback to " + plugin.getFallbackLayer()); property.setPluginLayerID(plugin.getFallbackLayer()); }else{ logger.log(Level.ERROR, "Can not determine the Layer for " + property.getName() + "! Reason unknown"); System.exit(-1); } } logger.log(Level.DEBUG, annotation.key() + " set layer to " + this.getPluginLayer(plugin.getConfigName())); property.setEnable_requirements(annotation.enable_requirements()); property.setValue_requirements(annotation.value_requirements()); property.setEnable(true); ((StringProp) property).setPossibleValues(annotation.possibleValues()); ((StringProp) property).setMultiSelection(annotation.multiSelection()); } this.register(property, isSuperClass, isGlobal, plugInLayer); } else { logger.log(Level.ERROR, this + "Bad property type for field" + field.getName() ); continue; } } } } catch (Exception e) { e.printStackTrace(); } } /** * Looks up the mappingbetween plugin names and layer names * @param pluginName id of a plugin * @return The name of the layer the plugin belongs to */ private String getPluginLayer(String pluginName) { if ( this.getRegisteredPlugins().containsKey(pluginName) ){ return this.getRegisteredPlugins().get(pluginName); } logger.log(Level.ERROR, "Plugin " + pluginName + " is unknown"); return null; } /** * Scans the registered plugins and build up some mappings */ public void scanPlugins() { // Get the display names of plugin layers String[] pluginLayer = this.layerMapConfigNameToDisplayName.keySet().toArray(new String[0]); for (int i = 0; i < this.layerMapConfigNameToDisplayName.size(); i++) { this.pluginLayerMap[i] = new HashMap<String, String>(); } for ( Entry<String, String> entry : this.getRegisteredPlugins().entrySet() ) { String name = entry.getKey(); String layer = entry.getValue(); boolean insertFlag = false; for (int i = 0; i < this.getLayerMapDisplayNameToConfigName().size(); i++){ // Append plugins config name and corresponding layers display name // if plugin's layer matches if ( layer.equals(pluginLayer[i]) ){ this.pluginLayerMap[i].put( name, layer ); insertFlag = true; this.numberOfPluginLayers = Math.max(this.numberOfPluginLayers, i+1); } } if ( !insertFlag ) { logger.log(Level.ERROR, "No such plugin layer: " + layer ); } } } /** * Sets the value of a simprop * @param key identifier of a simprop * @param arg0 thevalue */ public void setValue(String key, Object arg0) { logger.log(Level.DEBUG, "Set " + key + " to " + arg0); if (arg0.getClass() == Boolean.class) { this.getProperties().get(key).setValue(arg0); } else if (arg0.getClass() == Float.class) { this.getProperties().get(key).setValue(arg0); } else if (arg0.getClass() == Double.class) { this.getProperties().get(key).setValue(arg0); }else if (arg0.getClass() == Integer.class) { this.getProperties().get(key).setValue(arg0); } else if (arg0.getClass() == String.class) { this.getProperties().get(key).setValue(arg0); } else { } setUnsavedChanges(true); DependencyChecker.checkAll(getInstance()); } /** * Can be used to get a list of simprops that belog to a specific layer * @param pluginLayer is the id of a layer * @return a list of simprops that belog to the specified layer */ public List<SimProp> getGlobalSimPropertiesByPluginLayer(String pluginLayer) { // TODO: order simproperties here List<SimProp> simPropsInPluginLayer = new LinkedList<SimProp>(); for ( String key : this.getProperties().keySet() ) { SimProp simProp = this.getProperties().get(key); if ( ( simProp.isSuperclass() || simProp.isGlobal() ) && simProp.getPluginLayerID().equals(this.getLayerMapDisplayNameToConfigName().get(pluginLayer) ) ){ simPropsInPluginLayer.add( simProp ); } } return simPropsInPluginLayer; } /** * Can be used to get a list of simprops that belog to a specific layer or plugin * @param pluginName is the id of a plugin * @param pluginLayer is the id of a layer * @return a list of simprops that belog to the specified layer or plugin */ public List<SimProp> getSimPropertiesByPluginOrPluginLayer(String pluginName, String pluginLayer) { // TODO: order simproperties here List<SimProp> simPropertiesInANamespace = new LinkedList<SimProp>(); for ( String key : this.getProperties().keySet() ) { SimProp simProp = this.getProperties().get(key); if ( simProp.getPluginID().equals(pluginName) || ( simProp.isSuperclass() && simProp.getPluginLayerID().equals( this.getLayerMapDisplayNameToConfigName().get(pluginLayer) )) ){ simPropertiesInANamespace.add( simProp ); } } return simPropertiesInANamespace; } /** * Sets a specific plugin to active (visible in the gui - will be written to configuration) * @param layerDisplayName is the layer the plugin belongs to * @param selectedPlugin is the plugin that should be activated */ public void setActivePlugins(String layerDisplayName, String selectedPlugin) { logger.log(Level.DEBUG, "setActivePlugins " + layerDisplayName + " plugin to " + selectedPlugin); this.activePlugins.put(layerDisplayName, selectedPlugin); this.activePluginsMapped.put( this.displayNameToConfigName(layerDisplayName), selectedPlugin); this.setUnsavedChanges(true); } /** * Sets a specific plugin to active (visible in the gui - will be written to configuration) * @param layerConfigName is the layer the plugin belongs to * @param selectedPlugin is the plugin that should be activated */ public void setActivePluginsMapped(String layerConfigName, String selectedPlugin) { logger.log(Level.DEBUG, "setActivePluginsMapped " + layerConfigName + " plugin to " + selectedPlugin); this.activePlugins.put(this.configNameToDisplayName(layerConfigName), selectedPlugin); this.activePluginsMapped.put(layerConfigName, selectedPlugin); } /** * Used to obtain a mapping from layer to active plugin * @param mapped indicates if the the key (layer) should be the config name (true) or the display name (false) * @return mapping from layer to active plugin */ public Map<String, String> getActivePlugins(boolean mapped) { if (mapped){ return this.activePluginsMapped; }else{ return this.activePlugins; } } // TODO: remove this peace of code /** * Used to obain a list of all layers * @return a list of all layers */ public List<String> getPluginLayers() { return new LinkedList<String>(this.getLayerMapDisplayNameToConfigName().keySet()); } // TODO: remove this peace of code /** * Translates layer display names to layer config names * @param is display name (gui) of a layer * @return the config name (configuration) of a layer */ public String displayNameToConfigName(String displayName) { return this.getLayerMapDisplayNameToConfigName().get(displayName); } /** * Translates layer config names to layer display names * @param is config name (configuration) of a layer * @return the display name (gui) of a layer */ public String configNameToDisplayName(String configName) { return this.layerMapConfigNameToDisplayName.get(configName); } /** * Used to obtain a list of all known config name of layers * @return The list of all layers config names */ public List<String> getConfigNamesForPluginLayers() { List<String> configNamesForPluginLayers = new LinkedList<String>(); for ( Entry<String, String> entry : this.layerMapConfigNameToDisplayName.entrySet() ) { configNamesForPluginLayers.add( entry.getKey() ); } return configNamesForPluginLayers; } @Override public String toString(){ String tree = ""; for ( String layer : this.getLayerMapDisplayNameToConfigName().keySet() ){ for ( String prop : this.getProperties().keySet() ){ if ( this.getProperties().get(prop).getPluginID().equals("") && ( this.getProperties().get(prop).isSuperclass() || this.getProperties().get(prop).isGlobal() ) && this.getProperties().get(prop).getPluginLayerID().equals(this.getLayerMapDisplayNameToConfigName().get(layer)) ){ // injected to plugin configuration } } for ( String plugin : this.getRegisteredPlugins().keySet() ){ if ( this.getRegisteredPlugins().get(plugin).equals(this.getLayerMapDisplayNameToConfigName().get(layer))){ // plugins for ( String prop : this.getProperties().keySet() ){ if ( this.getProperties().get(prop).getPluginID().equals(plugin) ){ // plugin dependent properties } } } } } return tree; } /** * Used to look up all plugins within a specific layer * @param pluginLayer is the id of the layer * @return A list of the plugins which belong to pluginLayer */ public Map<String, String> getPluginsInLayer( String pluginLayer ) { Map<String, String> tmp1 = new HashMap<String, String>(); for ( String layer : this.getLayerMapDisplayNameToConfigName().keySet() ){ if ( layer.equals( pluginLayer )){ for (SimGuiPlugin plugin : this.plugins.values() ){ if ( plugin.getPluginLayer().equals(this.layerMapDisplayNameToConfigName.get(pluginLayer)) && plugin.isVisible() ){ tmp1.put(plugin.getDisplayName(), plugin.getConfigName()); } } } } return tmp1; } public Map<String, Integer> getStaticConfigurationDisplay() { return this.staticConfigurationDisplay; } public Map<String, SimGuiPlugin> getPluginMap() { return this.plugins; } public Map<String, String> getLayerMapDisplayNameToConfigName() { return this.layerMapDisplayNameToConfigName; } public Map<String, SimProp> getProperties() { return this.properties; } /** * Used to look up simprops by their display names * @param name is a display name of a simprop * @return a first simporp that matches the name (this is dangerous since different simprops may have the same display name) */ public SimProp getPropertiesByName(String name) { for (SimProp simProp : this.properties.values() ){ if ( simProp.getName().equals(name)){ logger.log(Level.DEBUG, "found property " + name + " -> " + simProp.getPropertyID()); return simProp; } } logger.log(Level.DEBUG, "No such property " + name); return null; } /** * Used to look up simprops by their id * @param id is the id of a simprop * @return the simporp that matches the id (much better than getPropertiesByName) */ public String getPropertieNameByID(String id) { for (SimProp simProp : this.properties.values() ){ if ( simProp.getPropertyID().equals(id)){ return simProp.getName(); } } return "---"; } /** * Enables auto checkbox for a simprop * @param id is the id of the simprop * @param auto specifies wheter the checkbox will be enabled (true) or disables (false) * @param c is the class of the simprop (Float, Double or Integer) */ public void setAuto(String id, boolean auto, Class<?> c) { if (c == Float.class) { ((FloatProp) this.getProperties().get(id)).setAuto(auto); } else if (c == Double.class) { ((DoubleProp) this.getProperties().get(id)).setAuto(auto); }else if (c == Integer.class) { ((IntProp) this.getProperties().get(id)).setAuto(auto); } else { } } /** * Enables unlimited checkbox for a simprop * @param id is the id of the simprop * @param unlimited specifies wheter the checkbox will be enabled (true) or disables (false) * @param c is the class of the simprop (Float, Double or Integer) */ public void setUnlimited(String key, boolean unlimited, Class<?> c) { if (c == Float.class) { ((FloatProp) this.getProperties().get(key)).setUnlimited(unlimited); } else if (c == Double.class) { ((DoubleProp) this.getProperties().get(key)).setUnlimited(unlimited); }else if (c == Integer.class) { ((IntProp) this.getProperties().get(key)).setUnlimited(unlimited); } else { } } /** * Registers a gui element on a simprops as an observer * @param guiElement the observer * @param propertyID the simprop id */ public void registerGuiElement(Observer guiElement, String propertyID) { for ( Entry<String, SimProp> entry : this.properties.entrySet() ){ if (entry.getValue().getPropertyID().equals(propertyID)) { entry.getValue().register( guiElement ); } } } /** * Looks up the plugin display name given the plugin config name * @param pluginConfigName is the config name * @return The plugin display name */ public String getPluginDisplayName(String pluginConfigName) { for ( SimGuiPlugin simPlugin : this.plugins.values() ){ if ( simPlugin.getConfigName().equals(pluginConfigName) ) return simPlugin.getDisplayName(); } return pluginConfigName; } /** * Sets property to vary values * @param key is the key of the property * @param value is the new value of the property */ public void setPropertyToVaryValue(String key, String value) { propertiesToVary.put(key, value); } /** * @return The map of properties which should be varied */ public Map<String, String> getPropertiesToVary() { return propertiesToVary; } public String getCurrentConfigFile() { return currentConfigFile; } public void setCurrentConfigFile(String currentConfigFile) { SimPropRegistry.currentConfigFile = currentConfigFile; } public Boolean getUnsavedChanges() { return unsavedChanges; } public void setUnsavedChanges(Boolean unsavedChanges) { this.unsavedChanges = unsavedChanges; } }