/******************************************************************************* * Mission Control Technologies, Copyright (c) 2009-2012, United States Government * as represented by the Administrator of the National Aeronautics and Space * Administration. All rights reserved. * * The MCT platform is 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. * * MCT includes source code licensed under additional open source licenses. See * the MCT Open Source Licenses file included with this distribution or the About * MCT Licenses dialog available at runtime from the MCT Help menu for additional * information. *******************************************************************************/ package gov.nasa.arc.mct.graphics.view; import gov.nasa.arc.mct.components.AbstractComponent; import gov.nasa.arc.mct.components.ExtendedProperties; import gov.nasa.arc.mct.evaluator.api.Evaluator; import gov.nasa.arc.mct.graphics.brush.Brush; import gov.nasa.arc.mct.graphics.brush.ClippedFill; import gov.nasa.arc.mct.graphics.brush.ConditionalBrush; import gov.nasa.arc.mct.graphics.brush.Fill; import gov.nasa.arc.mct.graphics.brush.Outline; import gov.nasa.arc.mct.graphics.brush.ScalingFill; import gov.nasa.arc.mct.graphics.clip.AxisClip; import gov.nasa.arc.mct.graphics.shape.RegularPolygon; import gov.nasa.arc.mct.graphics.state.StateSensitive; import java.awt.Color; import java.awt.Shape; import java.awt.geom.Ellipse2D; import java.awt.geom.Rectangle2D; import java.awt.geom.RoundRectangle2D; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.StringTokenizer; /** * Manages the settings for a GraphicalManifestation * @author vwoeltje */ public class GraphicalSettings { public static final String NO_EVALUATOR = "No Evaluator"; private GraphicalManifestation manifestation; private static Map<String, Shape> shapeMap; private static Map<String, Color> colorMap; private static Map<String, String> defaultMap; private static Map<String, FillProvider> fillMap; private Map<Object, String> reverseMap; private Map<String, Object> evaluatorMap; private Map<Object, List<String>> enumerationMap; //Maps eval name to possible states public GraphicalSettings (GraphicalManifestation manifestation) { this.manifestation = manifestation; initializeEvaluatorMap(); reverseMap = new HashMap<Object, String>(); addToReverseMap(shapeMap); addToReverseMap(colorMap); addToReverseMap(evaluatorMap); } /* Setting names for persistence */ //TODO shorten names public static final String GRAPHICAL_SHAPE = "GraphicalShapeSetting"; public static final String GRAPHICAL_BACKGROUND_COLOR = "GraphicalBackgroundColor"; public static final String GRAPHICAL_OUTLINE_COLOR = "GraphicalOutlineColor"; public static final String GRAPHICAL_OUTLINE_WEIGHT = "GraphicalOutlineWeight"; public static final String GRAPHICAL_FOREGROUND_FILL = "GraphicalFillStyle"; public static final String GRAPHICAL_FOREGROUND_COLOR = "GraphicalFillColor"; public static final String GRAPHICAL_FOREGROUND_MIN = "GraphicalFillMinimum"; public static final String GRAPHICAL_FOREGROUND_MAX = "GraphicalFillMaximum"; public static final String GRAPHICAL_EVALUATOR = "GraphicalEvaluator"; public static final String GRAPHICAL_EVALUATOR_MAP = "GraphicalEvaluatorMap"; public static final String DEFAULT_SHAPE = "Ellipse"; public static final String DEFAULT_BACKGROUND_COLOR = "Color0"; public static final String DEFAULT_OUTLINE_COLOR = "Color1"; public static final String DEFAULT_OUTLINE_WEIGHT = "10"; public static final String DEFAULT_FOREGROUND_FILL = "East"; public static final String DEFAULT_FOREGROUND_COLOR = "Color2"; public static final String DEFAULT_FOREGROUND_MIN = "0"; public static final String DEFAULT_FOREGROUND_MAX = "100"; public static final String DEFAULT_EVALUATOR = NO_EVALUATOR; public static final String DEFAULT_EVALUATOR_MAP = ""; /* Initialize maps */ static { shapeMap = new LinkedHashMap<String, Shape>(); shapeMap.put("Ellipse", new Ellipse2D.Float(0, 0, 1, 1)); shapeMap.put("Rectangle", new Rectangle2D.Float(1, 1, 2, 2) ); shapeMap.put("RoundRectangle", new RoundRectangle2D.Float(1.0f, 1.0f, 1,1, 0.5f, 0.5f) ); for (int i = 3; i <= 6; i++) { shapeMap.put("Polygon" + i, new RegularPolygon(i)); } /* TODO - this needs to be color schemed */ /* Most colors taken from canvas ControlareaFormattingConstants */ colorMap = new LinkedHashMap<String, Color>(); colorMap.put("Color0", Color.DARK_GRAY.darker()); colorMap.put("Color1", Color.DARK_GRAY); colorMap.put("Color2", Color.BLUE); colorMap.put("Color3", Color.RED); colorMap.put("Color4", Color.green); colorMap.put("Color5", new Color(000, 128, 000)); colorMap.put("Color6", new Color(032, 179, 170)); colorMap.put("Color7", new Color(152, 251, 152)); colorMap.put("Color8", new Color(255, 140, 000)); colorMap.put("Color9", new Color(255, 000, 255)); colorMap.put("Color10", new Color(255, 69, 000)); colorMap.put("Color11", new Color(255, 215, 000)); colorMap.put("Color12", new Color(047, 79, 79)); colorMap.put("Color13", new Color(128, 128, 128)); colorMap.put("Color14", new Color(100, 149, 237)); colorMap.put("Color15", new Color(000, 49, 042)); colorMap.put("Color16", new Color(000, 176, 176)); colorMap.put("Color17", new Color(102, 051, 255)); fillMap = new LinkedHashMap<String, FillProvider>(); fillMap.put("Static", new FillProvider()); fillMap.put("North", new ClippedFillProvider(AxisClip.Y_AXIS, AxisClip.DECREASING)); fillMap.put("South", new ClippedFillProvider(AxisClip.Y_AXIS, AxisClip.INCREASING)); fillMap.put("East", new ClippedFillProvider(AxisClip.X_AXIS, AxisClip.INCREASING)); fillMap.put("West", new ClippedFillProvider(AxisClip.X_AXIS, AxisClip.DECREASING)); fillMap.put("Expanding", new ExpandingFillProvider()); defaultMap = new HashMap<String, String>(); defaultMap.put(GRAPHICAL_SHAPE, DEFAULT_SHAPE); defaultMap.put(GRAPHICAL_BACKGROUND_COLOR, DEFAULT_BACKGROUND_COLOR); defaultMap.put(GRAPHICAL_OUTLINE_COLOR, DEFAULT_OUTLINE_COLOR); defaultMap.put(GRAPHICAL_OUTLINE_WEIGHT, DEFAULT_OUTLINE_WEIGHT); defaultMap.put(GRAPHICAL_FOREGROUND_FILL, DEFAULT_FOREGROUND_FILL); defaultMap.put(GRAPHICAL_FOREGROUND_COLOR, DEFAULT_FOREGROUND_COLOR); defaultMap.put(GRAPHICAL_FOREGROUND_MIN, DEFAULT_FOREGROUND_MIN); defaultMap.put(GRAPHICAL_FOREGROUND_MAX, DEFAULT_FOREGROUND_MAX); defaultMap.put(GRAPHICAL_EVALUATOR, DEFAULT_EVALUATOR); defaultMap.put(GRAPHICAL_EVALUATOR_MAP, DEFAULT_EVALUATOR_MAP); } private void initializeEvaluatorMap() { evaluatorMap = new LinkedHashMap<String, Object>(); evaluatorMap.put(NO_EVALUATOR, NO_EVALUATOR); enumerationMap = new HashMap<Object, List<String>>(); enumerationMap.put(NO_EVALUATOR, Collections.<String> emptyList()); AbstractComponent component = manifestation.getManifestedComponent(); addEvaluator(component); for (AbstractComponent parent : component.getReferencingComponents()) { addEvaluator(parent); } } private void addEvaluator(AbstractComponent comp) { Evaluator evaluator = comp.getCapability(Evaluator.class); if (evaluator == null) return; // Can only handle enum & imars/enum ... String language = evaluator.getLanguage(); if (!(language.equals("enum") || language.equals("imars/enum"))) { return; } // Track possible enumerations List<String> enumerations = new ArrayList<String> (); StringTokenizer codes = new StringTokenizer(evaluator.getCode(), "\t"); while (codes.hasMoreTokens()) { String code = codes.nextToken(); int skip = 0; if (language.equals("enum")) skip = 2; // Skip first two terms if (language.equals("imars/enum")) skip = 1; // Skip first token only while (skip-- > 0) { if (code.contains(" ")) code = code.substring(code.indexOf(" ") + 1); else code = ""; } if (code != "") { enumerations.add(code); } } evaluatorMap.put(comp.getDisplayName(), comp); enumerationMap.put(comp, enumerations); } private void addToReverseMap(Map<String, ?> map) { for (Entry<String, ?> e : map.entrySet()) { reverseMap.put(e.getValue(), e.getKey()); } } /** * Get a named object (for instance, a shape from the settings shape map, or * color from the settings color map) * @param name the name of the object * @return the object so named */ public Object getNamedObject(String name) { if (shapeMap.containsKey(name)) return shapeMap.get(name); if (colorMap.containsKey(name)) return colorMap.get(name); if (fillMap.containsKey(name)) return name; // Fill names are mapped only internally return null; } /** * Get the list of brushes which, when drawn, will reflect current settings. * @return a list of brushes */ public List<Brush> getLayers() { List<Brush> brushes = new ArrayList<Brush>(); brushes.add(new Fill((Color) getSetting(GRAPHICAL_BACKGROUND_COLOR))); FillProvider provider = fillMap.get((String) getSetting(GRAPHICAL_FOREGROUND_FILL)); Object evaluator = getSetting(GRAPHICAL_EVALUATOR); if (evaluator == NO_EVALUATOR) { Brush b = provider.getFill((Color) getSetting(GRAPHICAL_FOREGROUND_COLOR)); Double minimum = Double.parseDouble((String) getSetting(GRAPHICAL_FOREGROUND_MIN)); Double maximum = Double.parseDouble((String) getSetting(GRAPHICAL_FOREGROUND_MAX)); if (b instanceof StateSensitive) ((StateSensitive) b).setInterval(minimum, maximum); brushes.add(b); } else { // Build evaluator brushes Object evMapSetting = getSetting(GRAPHICAL_EVALUATOR_MAP); if (evMapSetting instanceof Map) { Map evMap = (Map) evMapSetting; for (Object entry : evMap.entrySet()) { if (entry instanceof Map.Entry) { Object key = ((Map.Entry) entry).getKey(); Object value = ((Map.Entry) entry).getValue(); if (value instanceof Color && key instanceof String) { Fill b = provider.getFill((Color) value); Double minimum = Double.parseDouble((String) getSetting(GRAPHICAL_FOREGROUND_MIN)); Double maximum = Double.parseDouble((String) getSetting(GRAPHICAL_FOREGROUND_MAX)); b.setInterval(minimum, maximum); brushes.add(new ConditionalBrush(b, (String) key)); } } } } } brushes.add(new Outline((Color) getSetting(GRAPHICAL_OUTLINE_COLOR))); return brushes; } /** * Apply the current settings to the managed view. */ public void updateManifestation() { manifestation.buildFromSettings(); manifestation.getManifestedComponent().save(); } /** * Get all shapes that are supported by settings * @return a collection of available shapes */ public Collection<Shape> getSupportedShapes() { return shapeMap.values(); } /** * Get all colors which are supported by settings * @return a collection of available colors */ public Collection<Color> getSupportedColors() { return colorMap.values(); } /** * Get all evaluators which are supported by these settings * @return a collection of evaluators available for the viewed component */ public Collection<Object> getSupportedEvaluators() { return evaluatorMap.values(); } /** * Get the names of all fill types which are supported by these settings * @return a set of names of available fills */ public Set<String> getSupportedFills() { return fillMap.keySet(); } /** * * @param component the component which provides the evaluator * @return a collection of known possible evaluation outputs */ public Collection<String> getSupportedEnumerations() { return enumerationMap.get(getSetting(GRAPHICAL_EVALUATOR)); } /** * Determine whether or not the specified key is the name of a setting * @param key the name to check * @return true if this is the name of a setting, false if not */ public boolean isValidKey(String key) { return defaultMap.containsKey(key); } /** * Gets the current setting associated with the specified key, in its * String form. If it has not been set in a manifestation, the default will * be applied and returned. * @param key the name of the setting * @return the current value, as a string */ private String get(String key) { String value = manifestation.getViewProperties().getProperty(key, String.class); if (value == null) { if (!isValidKey(key)) return null; set (key, defaultMap.get(key)); value = defaultMap.get(key); } return value; } /** * Set the value for a specific setting. (In its string form - direct * to the manifestation.) * @param key the name of the setting * @param value the new value */ private void set(String key, String value) { ExtendedProperties viewProperties = manifestation.getViewProperties(); viewProperties.setProperty(key, value); } /** * Get the object represented by current settings. * @param name the name of the settings * @return the object which represents the current choice */ public Object getSetting (String name) { String choice = get(name); if (shapeMap.containsKey(choice)) return shapeMap.get(choice); if (colorMap.containsKey(choice)) return colorMap.get(choice); if (fillMap.containsKey(choice)) return choice; //Not remapped if (evaluatorMap.containsKey(choice)) return evaluatorMap.get(choice); if (name.equals(GRAPHICAL_EVALUATOR_MAP)) return decodeMap(choice); else return choice; } /** * Set the value of a setting to correspond with the given Object. Note that * this does nothing if the Object is not supported * @param key the name of the setting * @param value the object to make the current setting */ public void setByObject (String key, Object value) { if (reverseMap.containsKey(value)) { set(key, reverseMap.get(value)); } else if (value instanceof String) { // Fill just publishes keys set(key, (String) value); } else if (value instanceof Map) { set(key, encodeMap((Map) value)); } } /** * Turns a string, as stored in settings ("OK\tColor0\nFAIL\tColor1\n", for example) * into a corresponding map. * @param str the string form of the map, as made by encodeMap * @return a Map object representing the string->color mappings */ private Map<String, Color> decodeMap (String str) { Map<String, Color> map = new LinkedHashMap<String, Color>(); StringTokenizer mapTokens = new StringTokenizer(str, "\n"); while (mapTokens.hasMoreTokens()) { String pair = mapTokens.nextToken(); if (!pair.isEmpty()) { StringTokenizer entryTokens = new StringTokenizer(pair, "\t"); if (entryTokens.countTokens() == 2) { String key = entryTokens.nextToken(); Color value = colorMap.get(entryTokens.nextToken()); map.put(key, value); } } } return map; } /** * Turns a map of Strings to Colors into a string representation * ("OK=>Color0;FAIL=>Color1;") for storage in settings * @param map the evaluation->color mappings * @return a string describing the same map */ private String encodeMap (Map map) { StringBuilder s = new StringBuilder(); Set keySet = map.keySet(); for (Object plainKey : keySet) { if (plainKey instanceof String) { String key = (String) plainKey; String value = reverseMap.get(map.get(key)); if (value != null) { s.append(key); s.append("\t"); s.append(value); s.append("\n"); } } } return s.toString(); } private static class FillProvider { public Fill getFill(Color color) { return new Fill(color); } } private static class ExpandingFillProvider extends FillProvider { @Override public Fill getFill(Color color) { return new ScalingFill(color); } } private static class ClippedFillProvider extends FillProvider { private int axis; private int direction; public ClippedFillProvider(int axis, int direction) { this.axis = axis; this.direction = direction; } @Override public Fill getFill(Color color) { return new ClippedFill(color, axis, direction); } } }