// ********************************************************************** // // <copyright> // // BBN Technologies // 10 Moulton Street // Cambridge, MA 02138 // (617) 873-8000 // // Copyright (C) BBNT Solutions LLC. All rights reserved. // // </copyright> // ********************************************************************** // // $Source: /cvs/distapps/openmap/src/openmap/com/bbn/openmap/tools/symbology/milStd2525/SymbolPart.java,v $ // $RCSfile: SymbolPart.java,v $ // $Revision: 1.12 $ // $Date: 2005/08/11 20:39:17 $ // $Author: dietrick $ // // ********************************************************************** package com.bbn.openmap.tools.symbology.milStd2525; import java.awt.Dimension; import java.awt.Graphics2D; import java.util.Iterator; import java.util.List; import java.util.Properties; import com.bbn.openmap.dataAccess.cgm.CGM; import com.bbn.openmap.util.Debug; /** * The SymbolPart class represents one part in a hierarchy of pieces * needed to represent an actual symbol. A symbol may require * geometries from its parents, as each piece further down the * hierarchy makes each symbol's meaning more specific, or scoped for * a particular task. The top-level SymbolPart represents the entire * symbology tree. Descending down through the tree, a SymbolPart * representing one of the 5 Scheme sections is next, with the lower * levels dependent on the Scheme definitions. * <P> * * The SymbolPart is smart enough to use the hierarchy.properties file * that defines the symbol set and create the symbol tree using the * appropriate Code classes. Not all Code classes help define the * tree, because some aspects of a symbol are flexible, like the * Affiliation (enemy, friend, etc). The SymbolPart tree only defines * some aspects of the symbol. Other parts of the symbol are dependent * on these flexible variations that are provided to the SymbolPart at * the time icons are created. */ public class SymbolPart { public final static String DEFAULT_SYMBOL_CODE = " "; /** Property file property for pretty name 'name' */ public final static String NameProperty = "name"; /** Property file property for cgm file too represent the symbol. */ public final static String CGMProperty = "cgm"; /** * The Object that describes the location of this symbol part in * the symbol hierarchy as defined by the 15 digit symbol code. */ protected CodePosition codePosition; /** * The part of the symbol code unique to this symbol part. Parents * and children will make up the other parts of the code. */ protected String code; /** * The pretty name for a symbol represented by this SymbolPart at * this point in the hierarchy. */ protected String prettyName; /** * The file containing the symbol geometry for this SymbolPart. */ protected String cgmName; /** * The symbol geometry object for this SymbolPart. */ protected CGM cgm; /** * A list of children SymbolParts relative to this one. */ protected List subs; /** * The parent SymbolPart to this one. */ protected SymbolPart parent; /** * Some positions need to shift for entries that don't follow the * conventions on the specification. */ protected int positionShift = 0; protected static boolean DEBUG = false; public final static char UNUSED = '-'; public final static char WILD = '*'; protected SymbolPart() { DEBUG = Debug.debugging("symbolpart"); } /** * The most-used constructor, used by CodePosition objects to * create the different levels of the SymbolPart tree. The * SymbolPart uses the parameters and the Properties to define its * name and get the cgm file holding the geometry for the symbol. * This constructor focuses on the Scheme, Dimension and * FunctionID symbol parts. * * @param codePosition CodePosition object that corresponds to the * SymbolPart. CodePosition object with lower position * numbers tend to define more general symbols. * @param symbolCode the 15 character symbol string that defines * this SymbolPart. This string is associated with a * hierarchy number in the Properties. * @param props the Properties object contains all the information * about the symbol tree. * @param parent the SymbolPart that is above this one in the * SymbolPart tree. */ public SymbolPart(CodePosition codePosition, String symbolCode, Properties props, SymbolPart parent) { this(codePosition, symbolCode, props, parent, codePosition.getStartIndex(), codePosition.getEndIndex(), true); } /** * A different constructor used by OptionPositions. The SymbolPart * uses the parameters and the Properties to define its name and * get the cgm file holding the geometry for the symbol. This * constructor focuses on the Scheme, Dimension and FunctionID * symbol parts. * * @param codePosition CodePosition object that corresponds to the * SymbolPart. CodePosition object with lower position * numbers tend to define more general symbols. * @param symbolCode the 15 character symbol string that defines * this SymbolPart. This string is associated with a * hierarchy number in the Properties. * @param props the Properties object contains all the information * about the symbol tree. * @param parent the SymbolPart that is above this one in the * SymbolPart tree. */ public SymbolPart(CodePosition codePosition, String symbolCode, Properties props, SymbolPart parent, int start, int end, boolean shiftIfNecessary) { this.code = symbolCode.substring(start, end); this.codePosition = codePosition; // For OptionPositions, we need to have a version where the // start and end aren't used for parsing, because the // properties are especially designed for them. We just need // the indexes for placement into the symbol code later. The // new code just needs to read symbolCode from the beginning // for the length between the indexes. boolean debug = DEBUG; // This corrects the situation where the symbol code is // shorter in the specification than it would seem // appropriate for its place in the hierarchy. while (code.charAt(0) == UNUSED && start > 1 && shiftIfNecessary) { code = symbolCode.substring(--start, end); this.positionShift--; } this.prettyName = props.getProperty(symbolCode + "." + NameProperty); this.cgmName = props.getProperty(symbolCode + "." + CGMProperty); this.parent = parent; String sc = getSymbolCode(); if (Debug.debugging("errors") && !symbolCode.equals(sc)) { debug = true; } if (debug) { Debug.output("SymbolPart(" + codePosition.getPrettyName() + "): read " + start + " of [" + symbolCode + "] as [" + sc + "] : " + this.prettyName + " (" + code + ")"); } } /** * Sets the part of the SymbolCode that is unique to this * SymbolPart. */ public void setCode(String c) { code = c; } /** * Gets the part of the SymbolCode that is unique to this * SymbolPart. */ public String getCode() { return code; } /** * Sets the descriptive name if this SymbolPart. */ public void setPrettyName(String pn) { prettyName = pn; } /** * Sets the descriptive name if this SymbolPart. */ public String getPrettyName() { return prettyName; } /** * Sets the SymbolPart's parent in the SymbolPart tree. */ public void setParent(SymbolPart par) { parent = par; } /** * Retrieves the SymbolPart's parent in the SymbolPart tree. */ public SymbolPart getParent() { return parent; } /** * Sets a list of SymbolPart tree for more specific * representations of what this SymbolPart represents. */ public void setSubs(List set) { subs = set; } /** * Gets a list of SymbolPart tree for more specific * representations of what this SymbolPart represents. */ public List getSubs() { return subs; } /** * Get a simple string representation of this SymbolPart, * including the 15 digit code and the pretty name. */ public String toString() { // return " [" + getSymbolCode() + "] " + prettyName; return prettyName; } /** * A method used by the tree to provide a string representation of * how all the SymbolParts are connected. */ public String getDescription(int level) { StringBuffer sb = new StringBuffer(); String indicator = "|--> "; if (level > 0) { sb.append(indicator); } List subs = getSubs(); int subSize = 0; if (subs != null) { subSize = subs.size(); } sb.append(toString()); if (subSize > 0) { sb.append(" with ").append(subSize).append(" subcategor" ).append((subSize == 1 ? "y\n" : "ies\n")); } else { sb.append("\n"); } if (subs != null) { synchronized (this) { StringBuffer sb1 = new StringBuffer(); for (int i = 0; i < level; i++) { sb1.append(" "); } String spacer = sb1.toString(); for (Iterator it = subs.iterator(); it.hasNext();) { sb.append(spacer) .append(((SymbolPart) it.next()).getDescription(level + 1)); } } } return sb.toString(); } /** * The starting command for retrieving the description with this * SymbolPart being the top of the tree. */ public String getDescription() { return getDescription(0); } /** * Retrieves the 15 character symbol code for this SymbolPart. * Calling this method will cause the SymbolPart to ask all of * it's parents to contribute their part of the code as well. */ public String getSymbolCode() { return getSymbolCode(null).toString(); } /** * A 15 character string of spaces, where spaces won't overwrite * the current character when this symbol writes to a * getSymbolCode() string. */ public StringBuffer getSymbolCodeMask() { return new StringBuffer(DEFAULT_SYMBOL_CODE); } /** * A SymbolPart tree method that gets the SymbolPart's parents * contribution for the symbol code. */ protected StringBuffer getSymbolCode(StringBuffer symbolCode) { if (codePosition instanceof CodeScheme) { symbolCode = ((CodeScheme) codePosition).getDefaultSymbolCode(); } else if (parent != null) { symbolCode = parent.getSymbolCode(symbolCode); } else { symbolCode = getSymbolCodeMask(); } if (codePosition != null) { int key = codePosition.getStartIndex() + positionShift; symbolCode = symbolCode.replace(key, codePosition.getEndIndex(), code); } return symbolCode; } public CodePosition getCodePosition() { return codePosition; } public CodeOptions getCodeOptions() { CodeScheme cs = getCodeScheme(); if (cs != null) { return cs.getCodeOptions(this); } else { return null; } } public CodeScheme getCodeScheme() { CodeScheme cs = null; if (codePosition instanceof CodeScheme) { cs = (CodeScheme) codePosition; } else if (parent != null) { cs = parent.getCodeScheme(); } return cs; } /** * A query method that answers if the given 15 digit code applies * to this symbol part. * * @param queryCode * @return true if the code applies to this position. */ public boolean codeMatches(String queryCode) { if (codePosition != null && code != null) { int startIndex = codePosition.startIndex; int length = code.indexOf('-'); if (length == -1) { length = code.length(); } if (Debug.debugging("symbology.detail")) { Debug.output("Checking " + queryCode + " against |" + code + "| starting at " + startIndex + " for " + length); } return queryCode.regionMatches(true, startIndex, code, 0, length); } // Nothing set, must be the head and we want a match. return true; } public void paintIcon(Graphics2D g, CodeOptions co, Dimension di) { } }