/* * <copyright> * Copyright 2015 BBN Technologies * </copyright> */ package com.bbn.openmap.omGraphics.rule; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.logging.Level; import java.util.logging.Logger; import com.bbn.openmap.OMComponent; import com.bbn.openmap.omGraphics.DrawingAttributes; import com.bbn.openmap.omGraphics.OMGraphic; import com.bbn.openmap.omGraphics.OMGraphicConstants; import com.bbn.openmap.omGraphics.OMText; import com.bbn.openmap.omGraphics.OMTextLabeler; import com.bbn.openmap.proj.Projection; import com.bbn.openmap.util.Debug; import com.bbn.openmap.util.PropUtils; /** * A Rule is an attribute inspector that makes decisions about rendering * attributes, information line contents, tooltips and visibility based on * scale. * * @author dietrick */ public abstract class Rule<T> extends OMComponent { /** * The property name where the testing value can be found for the rule to * compare against the value. */ protected String keyField; protected List<String> tooltipFields; protected List<String> infolineFields; protected List<String> labelFields; /** * The value that the query runs the operation against. */ protected Object val; protected RuleOp op = RuleOp.NONE; protected DrawingAttributes drawingAttributes = new DrawingAttributes(); protected float displayMinScale = Float.MIN_VALUE; protected float displayMaxScale = Float.MAX_VALUE; protected float labelMinScale = Float.MIN_VALUE; protected float labelMaxScale = Float.MAX_VALUE; /** * <pre> * layer.rules=rule1 rule2 rule3 * layer.rule1.key=CAPITAL * layer.rule1.op=equals * layer.rule1.val=Y * layer.rule1.actions=render tooltip infoline * layer.rule1.lineColor=FFFF0000 * layer.rule1.minScale=10000 * layer.rule1.maxScale=50000 * layer.rule1.infoline=CITY_NAME * layer.rule1.tooltip=ELEVATION * </pre> */ public final static String RuleListProperty = "rules"; public final static String RuleKeyProperty = "key"; public final static String RuleOperatorProperty = "op"; public final static String RuleValueProperty = "val"; public final static String RuleActionRender = "render"; public final static String RuleActionTooltip = "tooltip"; public final static String RuleActionInfoline = "infoline"; public final static String RuleActionLabel = "label"; public final static String RuleActionMinScale = "minScale"; public final static String RuleActionMaxScale = "maxScale"; /** * Asks the Op class to evaluate the retrieved value against the Rules * value. The implementation will use the key to pull the testing value out * of the record. * * @param record object to evaluate * @return true of the operation passed */ public abstract boolean evaluate(T record); /** * Returns a String of concatenated record values. * * @param fieldNames a list of string keys for fields to be used. * @param record The record object to look up values for the list of keys. * @return String of content */ public abstract String getContent(List<String> fieldNames, T record); public void setProperties(String prefix, Properties props) { super.setProperties(prefix, props); prefix = PropUtils.getScopedPropertyPrefix(prefix); keyField = props.getProperty(prefix + RuleKeyProperty, keyField); tooltipFields = getStringFromFields(props.getProperty(prefix + RuleActionTooltip)); infolineFields = getStringFromFields(props.getProperty(prefix + RuleActionInfoline)); labelFields = getStringFromFields(props.getProperty(prefix + RuleActionLabel)); RuleOp op = RuleOp.resolve(props.getProperty(prefix + RuleOperatorProperty)); if (op != null) { this.op = op; } Object newVal = props.getProperty(prefix + RuleValueProperty); if (newVal != null) { val = newVal; } if (keyField == null) { Debug.output("No key for rule (" + prefix + ") found in properties."); } displayMinScale = PropUtils.floatFromProperties(props, prefix + RuleActionRender + "." + RuleActionMinScale, displayMinScale); displayMaxScale = PropUtils.floatFromProperties(props, prefix + RuleActionRender + "." + RuleActionMaxScale, displayMaxScale); labelMinScale = PropUtils.floatFromProperties(props, prefix + RuleActionLabel + "." + RuleActionMinScale, labelMinScale); labelMaxScale = PropUtils.floatFromProperties(props, prefix + RuleActionLabel + "." + RuleActionMaxScale, labelMaxScale); // Assume that the OMGraphic will be rendered, with defaults if not // specified. render has to be set to false to hide OMGraphic boolean renderProperties = PropUtils.booleanFromProperties(props, prefix + RuleActionRender, drawingAttributes != null); if (renderProperties) { if (drawingAttributes == null) { drawingAttributes = new DrawingAttributes(); } drawingAttributes.setProperties(prefix, props); } else { drawingAttributes = null; } } public Properties getProperties(Properties props) { props = super.getProperties(props); String prefix = PropUtils.getScopedPropertyPrefix(this); props.put(prefix + RuleKeyProperty, PropUtils.unnull(keyField)); if (tooltipFields != null && !tooltipFields.isEmpty()) { props.put(prefix + RuleActionTooltip, getFieldsAsString(tooltipFields)); } if (infolineFields != null && !infolineFields.isEmpty()) { props.put(prefix + RuleActionInfoline, getFieldsAsString(infolineFields)); } if (labelFields != null && !labelFields.isEmpty()) { props.put(prefix + RuleActionLabel, getFieldsAsString(labelFields)); } if (this.op != null) { props.put(prefix + RuleOperatorProperty, this.op.getPropertyNotation()); } if (val != null) { props.put(prefix + RuleValueProperty, PropUtils.unnull(val.toString())); } if (displayMinScale != Float.MIN_VALUE) { props.put(prefix + RuleActionRender + "." + RuleActionMinScale, Float.toString(displayMinScale)); } if (displayMaxScale != Float.MAX_VALUE) { props.put(prefix + RuleActionRender + "." + RuleActionMaxScale, Float.toString(displayMaxScale)); } if (labelMinScale != Float.MIN_VALUE) { props.put(prefix + RuleActionLabel + "." + RuleActionMinScale, Float.toString(labelMinScale)); } if (labelMaxScale != Float.MAX_VALUE) { props.put(prefix + RuleActionLabel + "." + RuleActionMaxScale, Float.toString(labelMaxScale)); } if (drawingAttributes != null) { props.put(prefix + RuleActionRender, Boolean.toString(true)); drawingAttributes.getProperties(props); } return props; } /** * Evaluate the record against this rule. * * @param record A Map of attributes for a particular OMGraphic/map object. * The indices for the rule are indexes into this record. * @param omg The OMGraphic to evaluate. * @param proj The current map projection. * @return the OMGraphic if it should be drawn, null if it shouldn't. */ public OMGraphic evaluate(T record, OMGraphic omg, Projection proj) { if (evaluate(record)) { float scale = 0f; if (proj != null) { scale = proj.getScale(); if (scale < displayMinScale || scale > displayMaxScale) { // We met the rule, it's telling us not to display. return null; } } if (infolineFields != null) { omg.putAttribute(OMGraphicConstants.INFOLINE, getContent(infolineFields, record)); } if (tooltipFields != null) { omg.putAttribute(OMGraphicConstants.TOOLTIP, getContent(tooltipFields, record)); } if (labelFields != null && scale >= labelMinScale && scale <= labelMaxScale) { String curLabel = getContent(labelFields, record); OMTextLabeler label = new OMTextLabeler(curLabel, OMText.JUSTIFY_CENTER); // Needs to get added to the OMGraphic so it gets // generated with the projection at the right point. omg.putAttribute(OMGraphicConstants.LABEL, label); } if (drawingAttributes != null) { drawingAttributes.setTo(omg); } omg.setVisible(drawingAttributes != null); if (getLogger().isLoggable(Level.FINE)) { getLogger().fine(this.getPropertyPrefix() + " being assigned to " + op.getClass().getName() + " " + keyField + " " + val + " vs " + ((Map) record).get(keyField)); omg.putAttribute("RULE", getPropertyPrefix()); } return omg; } return null; } /** * Returns a String of concatenated record values. * * @param fieldNames names of field properties * @return fields as single string */ public String getFieldsAsString(List<String> fieldNames) { StringBuffer buf = new StringBuffer(); if (fieldNames != null) { for (String field : fieldNames) { buf.append(PropUtils.unnull(field)).append(" "); } } // Might be more than just that last "" return buf.toString().trim(); } /** * Create a List of Strings from a list of space separated strings. * * @param fieldString * @return List if fieldString can be parsed, null if fieldString is null. */ public List<String> getStringFromFields(String fieldString) { if (fieldString != null && !fieldString.isEmpty()) { return PropUtils.parseSpacedMarkers(fieldString); } return null; } public DrawingAttributes getDrawingAttribtues() { return drawingAttributes; } public void setDrawingAttributes(DrawingAttributes da) { this.drawingAttributes = da; } public float getDisplayMaxScale() { return displayMaxScale; } public void setDisplayMaxScale(float displayMaxScale) { this.displayMaxScale = displayMaxScale; } public float getDisplayMinScale() { return displayMinScale; } public void setDisplayMinScale(float displayMinScale) { this.displayMinScale = displayMinScale; } public String getKeyName() { return keyField; } public void setKeyName(String keyName) { this.keyField = keyName; } public List<String> getLabelFields() { return labelFields; } public void setLabelFields(List<String> labelFields) { this.labelFields = labelFields; } public List<String> getInfolineFields() { return infolineFields; } public void setInfolineFields(List<String> infolineFields) { this.infolineFields = infolineFields; } public List<String> getTooltipFields() { return tooltipFields; } public void setTooltipFields(List<String> tooltipFields) { this.tooltipFields = tooltipFields; } public float getLabelMaxScale() { return labelMaxScale; } public void setLabelMaxScale(float labelMaxScale) { this.labelMaxScale = labelMaxScale; } public float getLabelMinScale() { return labelMinScale; } public void setLabelMinScale(float labelMinScale) { this.labelMinScale = labelMinScale; } public RuleOp getOp() { return op; } public void setOp(RuleOp op) { this.op = op; } public Object getVal() { return val; } public void setVal(Object val) { this.val = val; } /** * Holder for this class's Logger. This allows for lazy initialization of * the logger. */ private static final class LoggerHolder { /** * The logger for this class */ private static final Logger LOGGER = Logger.getLogger(Rule.class.getName()); /** * Prevent instantiation */ private LoggerHolder() { throw new AssertionError("This should never be instantiated"); } } /** * Get the logger for this class. * * @return logger for this class */ private static Logger getLogger() { return LoggerHolder.LOGGER; } }