// **********************************************************************
//
// <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/layer/vpf/FeatureClassInfo.java,v $
// $RCSfile: FeatureClassInfo.java,v $
// $Revision: 1.8 $
// $Date: 2006/06/20 20:14:47 $
// $Author: dietrick $
//
// **********************************************************************
package com.bbn.openmap.layer.vpf;
import java.util.ArrayList;
import java.util.List;
import com.bbn.openmap.io.FormatException;
import com.bbn.openmap.util.Debug;
/**
* This class wraps a feature type file (potext.tft, polbndl.lft, etc) from VPF.
* It maintains sufficient information about the table it is indexed from so
* that it can take a List of values, rather than a single value. It also knows
* about its containing CoverageTable so it can look up information in int.vdt
* and char.vdt.
*/
public class FeatureClassInfo extends DcwRecordFile implements TerminatingRunnable,
com.bbn.openmap.io.Closable {
/** the table to look up .vdt info from */
final private CoverageTable ctable;
/**
* The name of our column from the primitive table (e.g. potext.tft_id).
* This is the name of the column that will let you know, in the primitive
* file (like edg), what type of primitive (featurewise) is being
* represented on that row. This field does not always exist! If it doesn't,
* all the features in the file are rendered.
*/
final private String columnname;
/** the column number in the primitive table for columnname */
private int mycolumn = -1;
/** true means the object has run(), false otherwise */
private boolean fullInit = false;
/** things constructed with deferred initialization get queued here */
// private static RunQueue tq = new RunQueue(true, Thread.MIN_PRIORITY,
// true);
/** temporary list for use in getDescription() */
final private List<Object> tmpVec = new ArrayList<Object>();
// Feature Classes cross reference each other. For any feature
// class name, you can have:
//
// 1) a DcwRecordFile (EdgeTable, NodeTable, AreaTable,
// TextTable), with a certain column used as an ID, reference a
// FeatureTable with a column that uses that ID.
//
// 2) a FeatureTable (.pft, .aft, .lft, .tft) using a column
// holding an ID, referencing a DcwRecordFile with a column
// holding that ID.
//
// The FeatureTable shows, for a particular feature type, the
// individual primitive features for that particular feature type,
// their FACC code, what tile that feature resides in, and the
// feature ID number in that tile.
//
// The DcwRecordFile contains the actual data for the primitive,
// and each DceRecordFile contains like feature primitives (edges,
// areas, text, points). Each line in the DcwRecordFile contains
// the ID number of the primitive,
/**
* Construct a FeatureClassInfo.
*
* @param cthis the CoverageTable to use for vdt lookups
* @param colname the column name from the primitive table
* @param tablepath the directory of the feature table
* @param ftname the name of the feature type
* @exception FormatException some error was encountered
*/
public FeatureClassInfo(CoverageTable cthis, String colname, String tablepath, String ftname)
throws FormatException {
super(tablepath + ftname, true); // defer initialization
if (Debug.debugging("vpf.fci")) {
Debug.output("FCI: set to peruse (" + filename + ")\n\tcreated with colname ("
+ colname + ")\n\ttablepath (" + tablepath + ")\n\tftname (" + ftname + ")");
}
ctable = cthis;
columnname = colname.toLowerCase().intern();
}
/** the name of the primitive file: edg, fac, end, cnd */
protected String tileFileName;
/** the name of the column with the primitive id */
protected String tileFileColName;
/** the type of feature this table represents */
protected char featureType;
/**
* Construct a FeatureClassInfo that can be used for feature search
*
* @param cthis the CoverageTable to use for vdt lookups
* @param colname the column name from the primitive table
* @param tablepath the directory of the feature table
* @param ftname the name of the feature type
* @param tileDirFile the name of the primitive file
* @param tileDirFileColName the name of the primitive id column
* @exception FormatException some error was encountered
*/
public FeatureClassInfo(CoverageTable cthis, String colname, String tablepath, String ftname,
String tileDirFile, String tileDirFileColName) throws FormatException {
super(tablepath + ftname, false); // don't defer
// initialization
fullInit = true;
ctable = cthis;
columnname = colname.toLowerCase().intern();
tileFileName = tileDirFile;
tileFileColName = tileDirFileColName;
if ("fac".equals(tileFileName)) {
featureType = CoverageTable.AREA_FEATURETYPE;
} else if ("end".equals(tileFileName)) {
featureType = CoverageTable.EPOINT_FEATURETYPE;
} else if ("cnd".equals(tileFileName)) {
featureType = CoverageTable.CPOINT_FEATURETYPE;
} else if ("txt".equals(tileFileName)) {
featureType = CoverageTable.TEXT_FEATURETYPE;
} else if ("edg".equals(tileFileName)) {
featureType = CoverageTable.EDGE_FEATURETYPE;
} else {
featureType = CoverageTable.SKIP_FEATURETYPE;
}
if (Debug.debugging("vpf.fci")) {
Debug.output("FCI: set to peruse (" + filename + ")\n\tcreated with column name ("
+ colname + ")\n\ttile directory file (" + tileDirFile
+ ")\n\ttile id column (" + tileDirFileColName + ")");
}
}
/**
* Returns a TilingAdapter suitable for retrieving primitive ids from
* records in this feature table.
*
* @return a tilingadapter or null
*/
public TilingAdapter getTilingAdapter() {
return getTilingAdapter(TILE_ID_COLUMN_NAME, tileFileColName);
}
/** the name of the column where tiling information lives */
public final static String TILE_ID_COLUMN_NAME = "tile_id";
/**
* Returns the file name (no path info) of the thematic index for the
* tile_id column.
*/
public String getTileThematicFileName() {
if (columnInfo != null) {
int colId = getTileIdIndex();
if (colId != -1) {
return columnInfo[colId].getThematicIndexName();
}
}
return null;
}
/** the thematic index for the tile_id column */
protected DcwThematicIndex thematicIndex = null;
/**
* Causes the thematic index for the tile_id column to be initialized.
*
* @param path the path to the directory where the index lives
* @return true if a thematic index is available, false if not
*/
public synchronized boolean initThematicIndex(String path) {
try {
if (thematicIndex == null) {
// See if we can use the thematic index to see which
// tiles
// have the features we want.
String thematicIndexName = getTileThematicFileName();
if (thematicIndexName != null) {
thematicIndex = new DcwThematicIndex(path + thematicIndexName, byteorder);
}
}
} catch (FormatException fe) {
if (Debug.debugging("vpf.FormatException")) {
Debug.output("FeatureClassInfo.initTI: " + fe.getClass() + " " + fe.getMessage());
}
return false;
}
return (thematicIndex != null);
}
/**
* Returns the thematic index for the tile_id column, if it has been
* initialized.
*
* @return null or a themaitc index for the column
*/
public DcwThematicIndex getThematicIndex() {
return thematicIndex;
}
/**
* Returns the column position of the tile_id column.
*
* @see DcwRecordFile#whatColumn(String)
*/
public int getTileIdIndex() {
return whatColumn(TILE_ID_COLUMN_NAME);
}
/**
* Returns the column position of the f_code column.
*
* @see DcwRecordFile#whatColumn(String)
*/
public int getFaccIndex() {
return whatColumn("f_code");
}
/**
* Returns the column position of the primitive id column.
*
* @see DcwRecordFile#whatColumn(String)
*/
public int getTilePrimitiveIdColIndex() {
return whatColumn(tileFileColName);
}
/**
* Return the type of feature this table is for. Returns one of the
* featuretype codes in CoverageTable.
*
* @see CoverageTable#AREA_FEATURETYPE
*/
public char getFeatureType() {
return featureType;
}
/**
* Complete the initialization of the FeatureClassInfo. This function can be
* called more than once.
*/
public synchronized void run() {
if (fullInit == true) {// run already ran, or the file didn't
// exist
return;
}
try {
fullInit = true;
finishInitialization(); // finish initialization of table
// The list isn't be closed as it's supposed to, and this
// is causing a leak. We'll just avoid the list for now
// and just close the files after we've read them.
// BinaryFile.addClosable(this);
} catch (FormatException f) {
// close(); //invalidate some stuff
}
close();
}
/**
* Implement the Closable interface
*/
public boolean close(boolean done) {
close();
if (thematicIndex != null) {
try {
thematicIndex.close();
} catch (FormatException fe) {
// ignored
}
}
return true;
}
/**
* Probe the DcwRecordFile looking for what column we are in. (Info needed
* later to getDescription with the data list.)
*
* @param rf the primitive data table we'll get rows from
*/
public void findYourself(DcwRecordFile rf) {
mycolumn = rf.whatColumn(columnname);
}
/**
* Given a row from the primitive table, this function returns a full string
* description of the row
*
* @param l the record list from the primitive table
* @param type the first integral type
* @return the description string for the list
*/
public synchronized String getDescription(List<Object> l, MutableInt type) {
checkInit();
if (mycolumn == -1) {
return null;
}
int i = VPFUtil.objectToInt(l.get(mycolumn));
if (i <= 0) {
return null;
}
return getDescription(i, type);
}
/**
* Given a row from the primitive table, this function returns a full string
* description of the row
*
* @param ftid the record list from the primitive table
* @param colIndex column index for attribute to return
* @param type the first integral type
* @return the description string for the list
*/
// public synchronized String getAttribute(List l, int colIndex,
public synchronized String getAttribute(int ftid, int colIndex, MutableInt type) {
checkInit();
// if (mycolumn == -1) {
// return null;
// }
// int ftid = VPFUtil.objectToInt(l.get(mycolumn));
if (ftid <= 0) {
return null;
}
try {
if (!getRow(tmpVec, ftid)) {
return null;
}
} catch (FormatException fe) {
if (Debug.debugging("vpf")) {
fe.printStackTrace();
}
}
return getAttribute(columnInfo[colIndex], tmpVec.get(colIndex), type);
}
/**
* Check to see if the file has been fully initialized, call run() to do
* that if needed.
*/
public synchronized void checkInit() {
if (fullInit == false) {
if (Debug.debugging("vpf")) {
Debug.output("FCI.checkInit() forcing init " + columnname + " " + tablename);
}
run();
}
}
/**
* Given an primary key (row id) for the feature table, return the string
* description. If made public, this function would need to be synchronized
* and check for proper initialization. But since it is always called from a
* method that does that, its okay.
*
* @param ftid the row id for our feature table
* @param type the first integral type
* @return the description string for the list
*/
private synchronized String getDescription(int ftid, MutableInt type) {
StringBuffer retval = null;
try {
if (!getRow(tmpVec, ftid)) {
return null;
}
// boolean haveivdtindex = false;
for (int i = 0; i < columnInfo.length; i++) {
DcwColumnInfo dci = columnInfo[i];
String s = getAttribute(dci, tmpVec.get(i), type);
// ////////
// String s = null;
// String dciVDT = dci.getVDT();
// if (dciVDT == Constants.intVDTTableName) {
// int val = VPFUtil.objectToInt(tmpVec.get(i));
// if (val == Integer.MIN_VALUE) {//VPF null
// continue;
// }
// if (!haveivdtindex) {
// type.value = (short) val;
// haveivdtindex = true;
// }
// s = ctable.getDescription(tablename,
// dci.getColumnName(),
// val);
// if (s == null) {
// s = "[" + val + "]";
// }
// } else if (dciVDT == Constants.charVDTTableName) {
// String val = (String) tmpVec.get(i);
// s = ctable.getDescription(tablename,
// dci.getColumnName(),
// val);
// if (s == null) {
// s = "[" + val + "]";
// }
// } else if (dci.isNonKey()) {
// s = tmpVec.get(i).toString();
// }
// //////
if (s != null) {
if (retval == null) {
retval = new StringBuffer(s);
} else {
retval.append("; ").append(s);
}
}
}
} catch (FormatException e) {
if (Debug.debugging("vpf")) {
e.printStackTrace();
}
}
return ((retval == null) ? null : retval.toString());
}
protected String getAttribute(DcwColumnInfo dci, Object colObj, MutableInt type) {
if (colObj == null) {
return null;
}
String dciVDT = dci.getVDT();
if (Constants.intVDTTableName.equals(dciVDT)) {
int val = VPFUtil.objectToInt(colObj);
if (val == Integer.MIN_VALUE) {// VPF null
return null;
}
if (type != null) {
type.value = (short) val;
}
return ctable.getDescription(tablename, dci.getColumnName(), val);
} else if (Constants.charVDTTableName.equals(dciVDT)) {
return (String) colObj;
// } else if (dci.isNonKey()) {
// s = colObj.toString();
}
return colObj.toString();
}
/**
* @return space separated list of column names, used mostly for debugging
* feature attribute lookups.
*/
public String columnNameString() {
StringBuffer sBuf = new StringBuffer();
for (DcwColumnInfo dci : getColumnInfo()) {
sBuf.append(dci.getColumnName()).append(' ');
}
return sBuf.toString().trim();
}
}