// **********************************************************************
//
// <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/DcwThematicIndex.java,v $
// $RCSfile: DcwThematicIndex.java,v $
// $Revision: 1.5 $
// $Date: 2005/08/09 19:29:39 $
// $Author: dietrick $
//
// **********************************************************************
package com.bbn.openmap.layer.vpf;
import java.io.EOFException;
import java.io.IOException;
import java.util.Arrays;
import java.util.BitSet;
import com.bbn.openmap.io.BinaryBufferedFile;
import com.bbn.openmap.io.BinaryFile;
import com.bbn.openmap.io.FormatException;
import com.bbn.openmap.util.Debug;
/** Read a VPF thematic index file. (VPF *.?ti files) */
public class DcwThematicIndex {
/** the file we read from */
private BinaryFile inputFile = null;
/** read from file - length of header */
final private int headerSize;
/** read from file - number of indexes (codes) */
final private int numberOfCodes;
/** read from file - total number of rows indexed */
final private int numberOfRows;
/** read from file - type of index */
final private char typeOfIndex; // T hematic
/** read from file - field type of index */
final private char fieldTypeOfIndex;
/** read from file - number of elements composing the index value */
final private int numberOfDataElement;
/** read from file - the type of the index */
final private char dataTypeSpecifier;
/** read from file - the table indexed */
final private String tableIndexed;
/** read from file - the column indexed */
final private String columnIndexed;
/** read from file (vpf 2407 only) - are the codes sorted */
final private boolean sorted;
/** the list of index records */
private IndexRecord[] indexData;
/** the name of the file being read */
// final protected File filename;
final protected String filename;
/** the byte order of the file */
protected boolean byteOrder;
/**
* A utility class used to record index records.
*/
public static class IndexRecord
implements Comparable<Object> {
/** the index (code) */
final Object index;
/** the offset of the data */
final int offset;
/** the number of values - 0 means the offset is the only value */
final int numvals;
/**
* Construct an index record
*
* @param index the index object
* @param offset the offset of the data
* @param numvals the number of values
*/
public IndexRecord(Object index, int offset, int numvals) {
this.index = index;
this.offset = offset;
this.numvals = numvals;
}
@SuppressWarnings("unchecked")
public int compareTo(Object obj) {
Object realobj = (obj instanceof IndexRecord) ? ((IndexRecord) obj).index : obj;
return ((Comparable<Object>) index).compareTo(realobj);
}
}
/**
* Construct an index, assumes this is pre-VPF2407 format.
*
* @param filename the file to oped
* @param border the byteorder
*/
public DcwThematicIndex(String filename, boolean border)
throws FormatException {
this(filename, border, false);
}
/**
* Construct an index, assumes this is pre-VPF2407 format.
*
* @param filename the file to oped
* @param border the byteorder
* @param vpf2407 true for MILSTD-2407 format thematic index. false will
* properly read a VPF2407 format index, but will ignore one header
* field (sorted). true will improperly read old-style data.
*/
public DcwThematicIndex(String filename, boolean border, boolean vpf2407)
throws FormatException {
this.filename = filename;
byteOrder = border;
reopen(0);
if (Debug.debugging("vpfserver")) {
System.out.println("DTI: opened the file " + filename);
}
try {
headerSize = inputFile.readInteger();
numberOfCodes = inputFile.readInteger();
numberOfRows = inputFile.readInteger();
typeOfIndex = inputFile.readChar();
fieldTypeOfIndex = inputFile.readChar();
numberOfDataElement = inputFile.readInteger();
dataTypeSpecifier = inputFile.readChar();
tableIndexed = trim(inputFile.readFixedLengthString(12)).toLowerCase();
columnIndexed = trim(inputFile.readFixedLengthString(25)).toLowerCase();
sorted = (inputFile.readChar() == 'S') && vpf2407;
inputFile.seek(60); // skips 3 unused bytes
indexData = new IndexRecord[numberOfCodes];
if (Debug.debugging("vpfserver")) {
System.out.println("HeaderSize = " + headerSize);
System.out.println("Number of Codes = " + numberOfCodes);
System.out.println("Number of Rows = " + numberOfRows);
System.out.println("Type of Index = " + typeOfIndex);
// if (typeOfIndex != 'T')
// System.out.println(" *** Strange - dcw spec says it
// will be T ***");
System.out.println("Field Type of Index = " + fieldTypeOfIndex);
System.out.println("Number of Data Element = " + numberOfDataElement);
System.out.println("Data Type Specifier = " + dataTypeSpecifier);
System.out.println("Table Indexed = " + tableIndexed);
System.out.println("Column Indexed = " + columnIndexed);
System.out.println("Sorted = " + sorted);
}
StringBuffer pr = new StringBuffer();
for (int i = 0; i < numberOfCodes; i++) {
indexData[i] =
new IndexRecord(readIndexField(fieldTypeOfIndex, numberOfDataElement), inputFile.readInteger(),
inputFile.readInteger());
if (Debug.debugging("vpfserver")) {
pr = new StringBuffer("i = ").append(i);
pr.append("; val = ").append(indexData[i].index.toString());
pr.append("; offset = ").append(indexData[i].offset);
pr.append("; number of elts = ").append(indexData[i].numvals);
if (i < 40) {
System.out.println(pr.toString());
}
}
}
if (!sorted) {
Arrays.sort(indexData);
}
if (Debug.debugging("vpfserver") && (numberOfCodes > 40)) {
System.out.println(pr.toString());
}
Debug.message("vpfserver", "*** Finished Header Read ***");
if (Debug.debugging("vpfserver")) {
if ((typeOfIndex == 'T') || (typeOfIndex == 'I')) {
Debug.output("Normal Inverted Index Format");
} else if ((typeOfIndex == 'B') || (typeOfIndex == 'G')) {
Debug.output("Scary Bitmap Index Format");
} else {
throw new FormatException("Unidentified TMI format");
}
Object[] indexes = getValueIndexes();
// We just know that these values are tile IDs.
for (int j = 0; j < indexes.length; j++) {
// int[] row = get(indexes[j]);
// If you want to do some scary printout, code it
// up here.
}
}
close();
} catch (EOFException e) {
throw new FormatException("Hit Premature EOF in thematic index");
} catch (IOException i) {
throw new FormatException("Encountered IO Exception: " + i.getMessage());
}
}
/**
* Returns the set of values indexed by this thematic index.
*
* @return the set of values indexed
*/
public Object[] getValueIndexes() {
Object[] values = null;
if (indexData != null) {
values = new Object[indexData.length];
for (int i = 0; i < indexData.length; i++) {
values[i] = indexData[i].index;
}
}
return values;
}
/**
* Returns the list of rows listed for this index
*
* @return an array of rows
* @param valueIndex the value to look up
*/
public synchronized int[] get(Object valueIndex)
throws FormatException {
int[] values = null;
try {
int index = Arrays.binarySearch(indexData, valueIndex);
if (index >= 0) {
IndexRecord ir = indexData[index];
int offset = ir.offset;
int numvals = ir.numvals;
if ((typeOfIndex == 'T') || (typeOfIndex == 'I')) {
if (numvals == 0) {
values = new int[1];
values[0] = offset;
} else {
values = new int[numvals];
reopen(offset);
for (int j = 0; j < numvals; j++) {
values[j] = readIndexWithFieldType(dataTypeSpecifier);
}
}
return values;
} else if ((typeOfIndex == 'B') || (typeOfIndex == 'G')) {
// Don't really do anything with this type of
// index...
int shortread = numberOfRows / 16;
if ((numberOfRows % 16) != 0) {
shortread++;
}
if (Debug.debugging("vpfserver")) {
System.out.println("Reading a bunch of shorts: " + shortread);
System.out.println("Starting at offset: " + inputFile.getFilePointer());
}
BitSet bits = new BitSet(numberOfRows);
int cnt = 0;
for (int shortcnt = 0; shortcnt < shortread; shortcnt++) {
short s = inputFile.readShort();
for (int k = 0; k < 16; k++) {
cnt++;
if ((s & 0x1) == 1) {
bits.set(cnt);
}
s >>= 1;
}
}
StringBuffer prt = new StringBuffer();
for (int j = 1; j <= bits.size(); j++) {
if (bits.get(j)) {
prt.append(", ").append(j);
}
}
System.out.println(prt);
} else {
throw new FormatException("Unidentified TMI format");
}
}
} catch (EOFException e) {
throw new FormatException("Hit Premature EOF in thematic index");
} catch (IOException i) {
throw new FormatException("Encountered IO Exception: " + i.getMessage());
}
return values;
}
/**
* Utility method to read rows.
*
* @param ft the field type
* @returns the value read from the file
*/
private int readIndexWithFieldType(char ft)
throws EOFException, FormatException {
switch (ft) {
case 'S':
return (int) inputFile.readShort();
case 'I':
return inputFile.readInteger();
}
throw new FormatException("Unrecognized FieldTypeOfIndex");
}
private Object readIndexField(char dts, int textlen)
throws EOFException, FormatException {
switch (dts) {
case 'I':
return new Integer(inputFile.readInteger());
case 'T':
return inputFile.readFixedLengthString(textlen);
case 'S':
return new Short(inputFile.readShort());
case 'F':
return new Float(inputFile.readFloat());
case 'R':
return new Double(inputFile.readDouble());
}
throw new FormatException("Unrecognized field index type");
}
private String trim(String s) {
StringBuffer ns = new StringBuffer();
char foo[] = s.toCharArray();
for (int i = 0; i < foo.length; i++) {
if ((foo[i] == ' ') || (foo[i] == 0)) {
break;
}
ns.append(foo[i]);
}
return ns.toString();
}
/**
* Returns the number of distinct indexed values
*
* @return the number of distinct indexed values
*/
public int getNumberOfCodes() {
return numberOfCodes;
}
/**
* Returns the number of rows indexed
*
* @return the number of rows indexed
*/
public int getNumberOfRows() {
return numberOfRows;
}
/**
* Returns the type of index (refer to VPF spec for valid values)
*
* @return the type of index (refer to VPF spec for valid values)
*/
public char getTypeOfIndex() {
return typeOfIndex;
}
/**
* Returns the type of the field being indexed
*
* @return the type of the field being indexed
*/
public char getFieldTypeOfIndex() {
return fieldTypeOfIndex;
}
/**
* Returns the number of elements in the index field
*
* @return the number of elements in the index field
*/
public int getNumberOfDataElements() {
return numberOfDataElement;
}
/**
* Returns the datatype specifier
*
* @return the datatype specifier
*/
public char getDataTypeSpecifier() {
return dataTypeSpecifier;
}
/**
* Returns the name of the table being indexed
*
* @return the name of the table being indexed
*/
public String getTableIndexed() {
return tableIndexed;
}
/**
* Returns the name of the column being indexed
*
* @return the name of the column being indexed
*/
public String getColumnIndexed() {
return columnIndexed;
}
public boolean getSorted() {
return sorted;
}
/** Closes the associated input file. (may later get reopened) */
public synchronized void close()
throws FormatException {
try {
if (inputFile != null) {
inputFile.close();
}
inputFile = null;
} catch (IOException i) {
throw new FormatException("DcwThematicIndex: Can't close file " + filename + ": " + i.getMessage());
}
}
/**
* Reopen the associated input file.
*
* @param offset the byte offset to seek to upon reopening the file. If
* offset is invalid (less than 1), then the input stream is in an
* undefined location.
* @exception FormatException some error was encountered in reopening file or
* seeking to the desired row.
* @see #close()
*/
public synchronized void reopen(int offset)
throws FormatException {
try {
if (inputFile == null) {
inputFile = new BinaryBufferedFile(filename);
inputFile.byteOrder(byteOrder);
}
if (offset > 0) {
inputFile.seek(offset);
}
} catch (IOException i) {
throw new FormatException("DcwThematicIndex: Can't open file " + filename + ": " + i.getMessage());
}
}
}