// PathVisio, // a tool for data visualization and analysis using Biological Pathways // Copyright 2006-2011 BiGCaT Bioinformatics // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package org.pathvisio.gui.handler; import java.util.Collection; import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.bridgedb.bio.Organism; import org.pathvisio.core.debug.Logger; import org.pathvisio.core.model.AlignType; import org.pathvisio.core.model.CellularComponentType; import org.pathvisio.core.model.DataNodeType; import org.pathvisio.core.model.GroupStyle; import org.pathvisio.core.model.LineStyle; import org.pathvisio.core.model.LineType; import org.pathvisio.core.model.ObjectType; import org.pathvisio.core.model.OrientationType; import org.pathvisio.core.model.PathwayElement; import org.pathvisio.core.model.Property; import org.pathvisio.core.model.PropertyManager; import org.pathvisio.core.model.PropertyType; import org.pathvisio.core.model.ShapeType; import org.pathvisio.core.model.StaticProperty; import org.pathvisio.core.model.StaticPropertyType; import org.pathvisio.core.model.ValignType; import org.pathvisio.core.preferences.GlobalPreference; import org.pathvisio.core.preferences.Preference; import org.pathvisio.core.preferences.PreferenceManager; /** * This class manages how properties should be displayed. It keeps track of TypeHandlers, which properties should be * visible, and the order in which properties should be displayed. *

* The main entry point for plugins are {@link #registerProperty(Property)} and * {@link #registerTypeHandler(TypeHandler)}. * * @author Mark Woon */ public class PropertyDisplayManager { private static final Map TYPE_HANDLERS = new HashMap(); private static final Map PROPERTY_PREFERENCES = new HashMap(); private static final Map DYNAMIC_PROPERTIES = new HashMap(); private static final Set MANAGED_DYNAMIC_PROPERTIES = new HashSet(); private static final Map> PROPERTY_SCOPE = new HashMap>(); private static boolean STORE_PREFERENCES = false; static { PreferenceManager.init(); // register core property types registerTypeHandler(new BooleanHandler()); registerTypeHandler(NumberHandler.buildHandler(StaticPropertyType.DOUBLE, Double.class)); registerTypeHandler(NumberHandler.buildHandler(StaticPropertyType.INTEGER, Integer.class)); registerTypeHandler(new AngleHandler()); registerTypeHandler(new ColorHandler()); registerTypeHandler(new CommentsHandler()); registerTypeHandler(new DataSourceHandler()); registerTypeHandler(new FontHandler()); registerTypeHandler(new ComboHandler(StaticPropertyType.GENETYPE, DataNodeType.getNames(), false)); registerTypeHandler(new ComboHandler(StaticPropertyType.GROUPSTYLETYPE, GroupStyle.getNames(), false)); registerTypeHandler(new ComboHandler(StaticPropertyType.LINESTYLE, LineStyle.getNames(), true)); registerTypeHandler(new ComboHandler(StaticPropertyType.LINETYPE, LineType.getVisibleNames(), LineType.getVisibleValues())); registerTypeHandler(new ComboHandler(StaticPropertyType.ORGANISM, Organism.latinNames(), false)); registerTypeHandler(new ComboHandler(StaticPropertyType.ORIENTATION, OrientationType.getNames(), true)); registerTypeHandler(new ComboHandler(StaticPropertyType.SHAPETYPE, ShapeType.getVisibleNames(), ShapeType.getVisibleValues())); registerTypeHandler(new ComboHandler(StaticPropertyType.VALIGNTYPE, ValignType.getNames(), ValignType.values())); registerTypeHandler(new ComboHandler(StaticPropertyType.ALIGNTYPE, AlignType.getNames(), AlignType.values())); registerTypeHandler(new ComboHandler(CellularComponentType.CELL_COMPONENT_TYPE, CellularComponentType.getNames(), false)); // register core properties for (StaticProperty p : StaticProperty.values()) { registerProperty(p); } //Register specific dynamic property //TODO: refactor as Static Property with next GPML update registerProperty(CellularComponentType.CELL_COMPONENT_PROPERTY); setPropertyScope( CellularComponentType.CELL_COMPONENT_PROPERTY, EnumSet.of(ObjectType.SHAPE) ); } /** * Private constructor - not meant to be instantiated. */ private PropertyDisplayManager() { } /** * Gets the visible property keys for the given PathwayElement. */ public static Set getVisiblePropertyKeys (PathwayElement e) { Set result = new HashSet(); // add static properties for (Property p : e.getStaticPropertyKeys()) { if (isVisible(p)) { result.add(p); } } // add dynamic properties for (String key : e.getDynamicPropertyKeys()) { Property p = getDynamicProperty(key); if (p == null || isVisible(p)) { // is visible if it's not associated with a property // or is associated with a property whose visibility preference is set result.add(key); } } // add managed dynamic properties for(Property p : getManagedDynamicProperties(e)) { if(isVisible(p)) result.add(p.getId()); } return result; } /** * Gets the TypeHandler for the given PropertyType. * * @return the TypeHandler for the given PropertyType or null if none exists */ public static TypeHandler getTypeHandler(PropertyType type) { return TYPE_HANDLERS.get(type); } /** * Registers a TypeHandler. * * @return true if no other TypeHandler has been registered for the given PropertyType, false otherwise */ public static boolean registerTypeHandler(TypeHandler handler) { boolean exists = TYPE_HANDLERS.containsKey(handler.getType()); if (exists) { Logger.log.info("Overwriting type handler for " + handler.getType()); } TYPE_HANDLERS.put(handler.getType(), handler); return !exists; } /** * Register a property here. *

* Dynamic properties will automatically be managed by the display manager, which means that the property * will be editable from the property panel (for all object types by default, see * {@link #setPropertyScope(Property, EnumSet)} to customize this behavior). Note that the property is not saved to * GPML unless the property is modified. */ public static void registerProperty(Property prop) { registerProperty(prop, true); } /** * Register a property here. *

* Dynamic properties will only be managed by the display manager if manage is set to true. When the * display manager manages a property, it means that the property will be editable from the property panel (for * all object types by default, see {@link #setPropertyScope(Property, EnumSet)} to customize this behavior). * Note that the property is not saved to GPML unless the property is modified. * * @param prop the Property being registered * @param manage true if the display manager should manage this property, false if not */ public static void registerProperty(Property prop, boolean manage) { if (!(prop instanceof StaticProperty)) { DYNAMIC_PROPERTIES.put(prop.getId(), prop); if (manage) { MANAGED_DYNAMIC_PROPERTIES.add(prop); } } loadPreference(prop); } /** * Get all managed dynamic properties that fall within the scope of the * given pathway element. * * @see #setPropertyScope(Property, EnumSet) for info on how to configure the property * scope. */ private static Collection getManagedDynamicProperties(PathwayElement e) { Set props = new HashSet(); ObjectType type = e.getObjectType(); for (Property p : MANAGED_DYNAMIC_PROPERTIES) { EnumSet scope = PROPERTY_SCOPE.get(p); if (scope == null || scope.contains(type)) props.add(p); } return props; } /** * Set the scope of a dynamic property (on which object types it applies). * The property panel will only display the property editor for the given * object types. * * @param prop * The property this scope applies to. * @param types * The object types that define the scope of this property. If * null is supplied as value, the property will be displayed on * all object types (default behavior). */ public static void setPropertyScope(Property prop, EnumSet types) { PROPERTY_SCOPE.put(prop, types); } /** * Get the scope of a property (defines on which object types it should * apply). * * @return The set of object types that this property applies to or null if * the property has no specific scope (and should apply to all * object types). */ public static EnumSet getPropertyScope(Property prop) { return PROPERTY_SCOPE.get(prop); } public static Property getDynamicProperty(String key) { return DYNAMIC_PROPERTIES.get(key); } /** * Gets the order of the given Property. If not is found, {@link Integer#MAX_VALUE} is returned. */ public static int getPropertyOrder(Property prop) { return loadPreference(prop).getOrder(); } /** * Sets the order of the given Property. */ public static void setPropertyOrder(Property prop, Integer order) { loadPreference(prop).setOrder(order); } /** * Gets whether the given Property should be visible. Default is visible. */ public static boolean isVisible(Property prop) { return loadPreference(prop).isVisible(); } /** * Sets whether the given Property should be visible. */ public static void setVisible(Property prop, boolean isVisible) { loadPreference(prop).setVisible(isVisible); } /** * Gets whether visibility and order of properties is stored as a preference. */ public static boolean isStorePreferences() { return STORE_PREFERENCES; } /** * Sets whether visibility and order of properties is stored as a preference. */ public static void setStorePreferences(boolean storePreferences) { if (storePreferences && !STORE_PREFERENCES) { // store all existing preferences for (PropPreference pref : PROPERTY_PREFERENCES.values()) { pref.store(); } } STORE_PREFERENCES = storePreferences; } private static PropPreference loadPreference(Property prop) { PropPreference pref = PROPERTY_PREFERENCES.get(prop); if (pref == null) { pref = new PropPreference(prop); if (STORE_PREFERENCES) { pref.parsePrefString(PreferenceManager.getCurrent().get(pref)); } PROPERTY_PREFERENCES.put(prop, pref); } return pref; } /** * Preference for property display information. Knows how to handle StaticProperty property. */ public static class PropPreference implements Preference { private static final Integer DEFAULT_ORDER = Integer.MAX_VALUE - 100; private Property prop; private Integer order = DEFAULT_ORDER; private boolean isVisible = true; public PropPreference(Property aProp) { prop = aProp; if (prop instanceof StaticProperty) { StaticProperty sp = (StaticProperty)prop; order = sp.getOrder(); if (sp.isHidden() || (sp.isAdvanced() && !PreferenceManager.getCurrent().getBoolean(GlobalPreference.SHOW_ADVANCED_PROPERTIES))) { isVisible = false; } } } public void store() { PreferenceManager.getCurrent().set(this, buildPrefString()); } public void parsePrefString(String value) { if (value != null) { String[] rez = value.split(":"); order = new Integer(rez[0]); isVisible = Boolean.parseBoolean(rez[1]); } } public String buildPrefString() { return order.toString() + ":" + isVisible; } public Integer getOrder() { return order; } public void setOrder(int aOrder) { order = aOrder; if (STORE_PREFERENCES) { PreferenceManager.getCurrent().set(this, buildPrefString()); } } public boolean isVisible() { return isVisible; } public void setVisible(boolean visible) { isVisible = visible; if (STORE_PREFERENCES) { PreferenceManager.getCurrent().set(this, buildPrefString()); } } public String name() { return "propertyDisplayManager." + prop.getId(); } public String getDefault() { Integer defaultOrder = DEFAULT_ORDER; if (prop instanceof StaticProperty) { defaultOrder = ((StaticProperty)prop).getOrder(); } return defaultOrder + ":" + Boolean.TRUE; } } }