// **********************************************************************
//
// <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/CodePosition.java,v $
// $RCSfile: CodePosition.java,v $
// $Revision: 1.8.2.4 $
// $Date: 2005/08/11 21:03:18 $
// $Author: dietrick $
//
// **********************************************************************
package com.bbn.openmap.tools.symbology.milStd2525;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
import com.bbn.openmap.util.ComponentFactory;
import com.bbn.openmap.util.Debug;
import com.bbn.openmap.util.PropUtils;
/**
* The CodePosition class is a base class used to interpret and
* organize role settings for a symbol. The SymbolCode describes a
* symbol and its role, with different characters having defined
* meanings and optional values, depending on the symbol. The
* CodePosition object defines a character and meaning for a character
* in a certain place. A SymbolPart refers to a particlar CodePosition
* that uniquely defines it, giving it some organizational meaning.
* SymbolParts that share a parent can get to the parent's CodePositin
* to see that meaning as well.
* <P>
*
* CodePositions have some intelligence for parsing position
* properties and hierarchy properties, which allow the whole symbol
* tree to be defined.
* <P>
*
* CodePositions server a couple of different roles. Some CodePosition
* objects organize the kinds of set values that may be applicable for
* a certain character position, and can offer those choices. These
* organizational CodePositions won't have a SymbolPart to represent
* itself. Other CodePositions, including the choices and those tied
* directly to SymbolParts in the SymbolPart tree, don't offer choices
* but can provide SymbolParts to represent themselves in the symbol.
*/
public class CodePosition {
public final static char NO_CHAR = ' ';
public final static int NO_NUMBER = -1;
protected int hierarchyNumber;
protected String id;
protected String prettyName;
protected int startIndex;
protected int endIndex;
protected CodePosition nextPosition = null;
protected SymbolPart symbolPart = null;
public boolean DEBUG = false;
/** Property file property for pretty name 'name' */
public final static String NameProperty = "name";
/**
* Property file property for a classname representing the next
* position in the position tree 'next'.
*/
public final static String NextProperty = "next";
/**
* A list of CodePosition choices for this position. This is only
* used for a single instance of the CodePosition that in turn
* holds this list of possible versions.
*/
protected List choices;
public CodePosition() {
DEBUG = Debug.debugging("codeposition");
};
public CodePosition(String name, int start, int end) {
this();
startIndex = start - 1;
endIndex = end;
prettyName = name;
}
/**
* A query method that answers of 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) {
int length = id.length();
if (Debug.debugging("symbology.detail")) {
Debug.output("Checking " + queryCode + " against |" + id
+ "| starting at " + startIndex + " for " + length);
}
return queryCode.regionMatches(true, startIndex, id, 0, length);
}
/**
* Get the current list of CodePosition possibilies. Only returns
* a list for the CodePositions used to parse the position
* properties.
*/
public List getPositionChoices() {
return choices;
}
/**
* Get a CodePosition from this list of available possibilities
* given the hierarchy number for the position. Not all positions
* have a hierarchy number, but the number given in the positions
* properties will probably suffice.
*/
public CodePosition getFromChoices(int hierarchyNumber) {
List aList = getPositionChoices();
if (aList != null) {
for (Iterator it = aList.iterator(); it.hasNext();) {
CodePosition cp = (CodePosition) it.next();
if (hierarchyNumber == cp.getHierarchyNumber()) {
return cp;
}
}
}
return null;
}
/**
* Method to add a position to the choices for this particular
* code position.
*
* @param index the hierarhical index for this position choice.
* This really only becomes important for those
* CodePositions which are used for interpreting the
* hierarchy properties. Other positions can use them for
* convenience, and this value will probably be just an
* ordering number for this choice out of all the other
* choices for the position.
* @param entry this should be character or characters used in the
* symbol code for this particular position choice.
* @param prefix the scoping property prefix used for all the
* properties. The entry is discovered by looking in the
* properties for this 'prefix.index'. Then other
* properties are discovered by looking for
* 'prefix.entry.propertyKey' properties.
* @param props the position properties.
*/
protected CodePosition addPositionChoice(int index, String entry,
String prefix, Properties props) {
String className = this.getClass().getName();
CodePosition cp = (CodePosition) ComponentFactory.create(className);
if (cp != null) {
if (DEBUG) {
Debug.output("CodePosition: created position (" + className
+ ")");
}
// Before prefix is modified
cp.symbolPart = getSymbolPart(prefix + entry, prefix, props);
prefix = PropUtils.getScopedPropertyPrefix(prefix) + entry + ".";
// Might not mean anything for option-type positions
cp.hierarchyNumber = index;
//cp.id = entry.charAt(0); // ASSUMED, but breaks
// multi-character codes
cp.id = entry;
cp.prettyName = props.getProperty(prefix + NameProperty);
addPositionChoice(cp);
} else {
if (DEBUG) {
Debug.output("CodePosition: couldn't create position ("
+ className + ")");
}
}
return cp;
}
/**
* Add the CodePosition to the choices, creating the choices List
* if needed.
*/
public void addPositionChoice(CodePosition cp) {
if (choices == null) {
choices = new LinkedList();
}
choices.add(cp);
}
/**
* This method reads Properties to add choices to this class as
* options for what values are valid in this position.
*/
protected void parsePositions(String prefix, Properties props) {
int index = 1;
prefix = PropUtils.getScopedPropertyPrefix(prefix);
String entry = props.getProperty(prefix + Integer.toString(index));
while (entry != null) {
addPositionChoice(index, entry, prefix, props);
entry = props.getProperty(prefix + Integer.toString(++index));
}
}
/**
* A method called when parsing position properties.
*
* @param entry should be prefix of the overall position class
* along with the symbol representation for that position.
* @param prefix should just be the prefix for the overall
* position class, including the period before the symbol
* representation for that position.
* @param props the position properties.
*/
protected SymbolPart getSymbolPart(String entry, String prefix,
Properties props) {
int offset = prefix.length();
return new SymbolPart(this, entry, props, null, offset, offset
+ endIndex - startIndex, false);
}
protected void parseHierarchy(String hCode, Properties props,
SymbolPart parent) {
List parentList = null;
int levelCounter = 1;
while (levelCounter > 0) {
String hierarchyCode = hCode + "." + levelCounter;
if (DEBUG) {
Debug.output("CodePosition.parse: " + hierarchyCode + " with "
+ getPrettyName());
}
String entry = props.getProperty(hierarchyCode);
if (entry != null) {
CodeFunctionID cp = new CodeFunctionID();
SymbolPart sp = new SymbolPart(cp, entry, props, parent);
if (parentList == null) {
parentList = parent.getSubs();
if (parentList == null) {
parentList = new LinkedList();
parent.setSubs(parentList);
}
}
if (DEBUG) {
Debug.output("CodePosition.parse: adding "
+ sp.getPrettyName() + " to "
+ parent.getPrettyName());
}
parentList.add(sp);
if (DEBUG) {
Debug.output("CodePosition.parse: looking for children of "
+ sp.getPrettyName());
}
cp.parseHierarchy(hierarchyCode, props, sp);
levelCounter++;
} else {
levelCounter = -1; // Flag to punch out of loop
}
}
}
/**
* The SymbolPart tree can be represented by a hierarchy number
* system, and this system is what is used in the hierarchy
* properties file to build the symbol tree.
*/
public int getHierarchyNumber() {
return hierarchyNumber;
}
/**
* Return a string version of the hierarchy number.
*/
public String getHierarchyNumberString() {
return Integer.toString(hierarchyNumber);
}
/**
* Get the character, in the symbol code, that this position
* represents.
*/
public String getID() {
return id;
}
/**
* Get the pretty name that states what this position and
* character represents.
*/
public String getPrettyName() {
return prettyName;
}
/**
* Get the starting index of the span that this position
* represents. This value is a java index value starting at 0.
*/
public int getStartIndex() {
return startIndex;
}
/**
* Get the ending index of the span that this position represents.
* This value is a java index value starting at 0.
*/
public int getEndIndex() {
return endIndex;
}
/**
* Return the next CodePosition. An organizational tool to help
* build the SymbolPart tree when parsing the hierarchy
* properties.
*/
public CodePosition getNextPosition() {
return nextPosition;
}
public String toString() {
// return getPrettyName() + " [" + getID() + "] at " +
// getStartIndex() + ", " + getEndIndex();
return getPrettyName();
}
protected CodePosition getNULLCodePosition() {
String className = this.getClass().getName();
CodePosition cp = (CodePosition) ComponentFactory.create(className);
StringBuffer idbuf = new StringBuffer();
for (int i = startIndex; i < endIndex; i++) {
idbuf.append("*");
}
cp.id = idbuf.toString();
cp.prettyName = "- Unspecified -";
if (Debug.debugging("symbology")) {
Debug.output("CodePosition: creating *unspecified* version of (" + className + ") with "
+ cp.id + ", " + cp.prettyName);
}
return cp;
}
}