0001: /*
0002: * GeoTools - OpenSource mapping toolkit
0003: * http://geotools.org
0004: * (C) 2003-2006, Geotools Project Managment Committee (PMC)
0005: *
0006: * This library is free software; you can redistribute it and/or
0007: * modify it under the terms of the GNU Lesser General Public
0008: * License as published by the Free Software Foundation; either
0009: * version 2.1 of the License, or (at your option) any later version.
0010: *
0011: * This library is distributed in the hope that it will be useful,
0012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0014: * Lesser General Public License for more details.
0015: */
0016: package org.geotools.data.vpf.file;
0017:
0018: import java.io.EOFException;
0019: import java.io.File;
0020: import java.io.IOException;
0021: import java.io.RandomAccessFile;
0022: import java.net.URI;
0023: import java.util.AbstractList;
0024: import java.util.Iterator;
0025: import java.util.LinkedList;
0026: import java.util.List;
0027: import java.util.Vector;
0028:
0029: import org.geotools.data.vpf.VPFColumn;
0030: import org.geotools.data.vpf.exc.VPFHeaderFormatException;
0031: import org.geotools.data.vpf.ifc.DataTypesDefinition;
0032: import org.geotools.data.vpf.ifc.FileConstants;
0033: import org.geotools.data.vpf.io.TripletId;
0034: import org.geotools.data.vpf.io.VPFInputStream;
0035: import org.geotools.data.vpf.io.VariableIndexInputStream;
0036: import org.geotools.data.vpf.io.VariableIndexRow;
0037: import org.geotools.data.vpf.util.DataUtils;
0038: import org.geotools.feature.AttributeType;
0039: import org.geotools.feature.DefaultFeatureType;
0040: import org.geotools.feature.Feature;
0041: import org.geotools.feature.FeatureType;
0042: import org.geotools.feature.GeometryAttributeType;
0043: import org.geotools.feature.IllegalAttributeException;
0044: import org.geotools.feature.SchemaException;
0045: import org.geotools.feature.type.AnnotationFeatureType;
0046: import org.geotools.filter.CompareFilter;
0047: import org.geotools.filter.Filter;
0048: import org.geotools.filter.LengthFunction;
0049: import org.geotools.filter.LiteralExpression;
0050:
0051: import com.vividsolutions.jts.geom.Coordinate;
0052: import com.vividsolutions.jts.geom.CoordinateList;
0053: import com.vividsolutions.jts.geom.DefaultCoordinateSequenceFactory;
0054: import com.vividsolutions.jts.geom.GeometryFactory;
0055:
0056: /**
0057: * This class encapsulates VPF files. By implementing the <code>FeatureType</code> interface,
0058: * it serves as a factory for VPFColumns. Instances of this class should
0059: * be created by VPFFileFactory.
0060: *
0061: * @author <a href="mailto:jeff@ionicenterprise.com">Jeff Yutzler</a>
0062: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/unsupported/vpf/src/main/java/org/geotools/data/vpf/file/VPFFile.java $
0063: */
0064: public class VPFFile implements FeatureType, FileConstants,
0065: DataTypesDefinition {
0066: // private final TableInputStream stream;
0067: private static String ACCESS_MODE = "r";
0068:
0069: /**
0070: * Variable <code>byteOrder</code> keeps value of byte order in which
0071: * table is written:
0072: *
0073: * <ul>
0074: * <li>
0075: * <b>L</b> - least-significant-first
0076: * </li>
0077: * <li>
0078: * <b>M</b> - most-significant-first
0079: * </li>
0080: * </ul>
0081: */
0082: private char byteOrder = LEAST_SIGNIF_FIRST;
0083: /**
0084: * The columns of the file. This list shall contain objects of type <code>VPFColumn</code>
0085: */
0086: private final List columns = new Vector();
0087:
0088: /**
0089: * Variable <code>description</code> keeps value of text description of the
0090: * table's contents.
0091: */
0092: private String description = null;
0093: /**
0094: * The contained Feature Type
0095: */
0096: private final FeatureType featureType;
0097:
0098: /**
0099: * Keeps value of length of ASCII header
0100: * string (i.e., the remaining information after this field)
0101: */
0102: private int headerLength = -0;
0103:
0104: /** The associated stream */
0105: private RandomAccessFile inputStream = null;
0106:
0107: /**
0108: * Variable <code>narrativeTable</code> keeps value of an optional
0109: * narrative file which contains miscellaneous information about the
0110: * table.
0111: */
0112: private String narrativeTable = null;
0113:
0114: /** The path name */
0115: private final String pathName;
0116:
0117: /** Describe variable <code>variableIndex</code> here. */
0118: private VPFInputStream variableIndex = null;
0119:
0120: /**
0121: * Constructor.
0122: *
0123: * @param cPathName The path to this file
0124: *
0125: * @throws IOException if the path or the file are invalid
0126: * @throws SchemaException if the contained feature type can not be
0127: * constructed
0128: */
0129: public VPFFile(String cPathName) throws IOException,
0130: SchemaException {
0131: pathName = cPathName;
0132: inputStream = new RandomAccessFile(cPathName, ACCESS_MODE);
0133: readHeader();
0134:
0135: GeometryAttributeType gat = null;
0136: VPFColumn geometryColumn = null;
0137:
0138: Iterator iter = columns.iterator();
0139: while (iter.hasNext()) {
0140: geometryColumn = (VPFColumn) iter.next();
0141:
0142: if (geometryColumn.isGeometry()) {
0143: gat = geometryColumn.getGeometryAttributeType();
0144:
0145: break;
0146: }
0147: }
0148:
0149: Vector super Types = new Vector();
0150: // if it's a text geometry feature type add annotation as a super type
0151: if (pathName.endsWith(TEXT_PRIMITIVE)) {
0152: super Types.add(AnnotationFeatureType.ANNOTATION);
0153: }
0154:
0155: featureType = new DefaultFeatureType(cPathName, "VPF", columns,
0156: super Types, gat);
0157: }
0158:
0159: /**
0160: * Create a new feature from the provided attributes It is unclear why one
0161: * would want to use this method.
0162: *
0163: * @param attributes the attributes to used
0164: *
0165: * @return the created feature
0166: *
0167: * @throws IllegalAttributeException if any of the attributes is invalid
0168: */
0169: public Feature create(Object[] attributes)
0170: throws IllegalAttributeException {
0171: return featureType.create(attributes);
0172: }
0173:
0174: /*
0175: * (non-Javadoc)
0176: * @see org.geotools.feature.FeatureFactory#create(java.lang.Object[], java.lang.String)
0177: */
0178: public Feature create(Object[] attributes, String featureID)
0179: throws IllegalAttributeException {
0180: return featureType.create(attributes, featureID);
0181: }
0182:
0183: /*
0184: * (non-Javadoc)
0185: * @see org.geotools.feature.FeatureType#duplicate(org.geotools.feature.Feature)
0186: */
0187: public Feature duplicate(Feature feature)
0188: throws IllegalAttributeException {
0189: return featureType.duplicate(feature);
0190: }
0191:
0192: /*
0193: * (non-Javadoc)
0194: * @see org.geotools.feature.FeatureType#find(org.geotools.feature.AttributeType)
0195: */
0196: public int find(AttributeType type) {
0197: return featureType.find(type);
0198: }
0199:
0200: /**
0201: * Gets the value of full length of ASCII header string including
0202: * <code>headerLength</code> field.
0203: *
0204: * @return the value of headerLength
0205: */
0206: private int getAdjustedHeaderLength() {
0207: return this .headerLength + 4;
0208: }
0209:
0210: /*
0211: * (non-Javadoc)
0212: * @see org.geotools.feature.FeatureType#getAncestors()
0213: */
0214: public FeatureType[] getAncestors() {
0215: return featureType.getAncestors();
0216: }
0217:
0218: /*
0219: * (non-Javadoc)
0220: * @see org.geotools.feature.FeatureType#getAttributeCount()
0221: */
0222: public int getAttributeCount() {
0223: return featureType.getAttributeCount();
0224: }
0225:
0226: /*
0227: * (non-Javadoc)
0228: * @see org.geotools.feature.FeatureType#getAttributeType(int)
0229: */
0230: public AttributeType getAttributeType(int position) {
0231: return featureType.getAttributeType(position);
0232: }
0233:
0234: /*
0235: * (non-Javadoc)
0236: * @see org.geotools.feature.FeatureType#getAttributeType(java.lang.String)
0237: */
0238: public AttributeType getAttributeType(String xPath) {
0239: return featureType.getAttributeType(xPath);
0240: }
0241:
0242: /*
0243: * (non-Javadoc)
0244: * @see org.geotools.feature.FeatureType#getAttributeTypes()
0245: */
0246: public AttributeType[] getAttributeTypes() {
0247: return featureType.getAttributeTypes();
0248: }
0249:
0250: /**
0251: * Gets the value of byteOrder variable. Byte order in which table is
0252: * written:
0253: *
0254: * <ul>
0255: * <li>
0256: * <b>L</b> - least-significant-first
0257: * </li>
0258: * <li>
0259: * <b>M</b> - most-significant-first
0260: * </li>
0261: * </ul>
0262: *
0263: *
0264: * @return the value of byteOrder
0265: */
0266: public char getByteOrder() {
0267: return byteOrder;
0268: }
0269:
0270: /*
0271: * (non-Javadoc)
0272: * @see org.geotools.feature.FeatureType#getDefaultGeometry()
0273: */
0274: public GeometryAttributeType getDefaultGeometry() {
0275: return featureType.getDefaultGeometry();
0276: }
0277:
0278: /**
0279: * Gets the value of the description of table content. This is nice to
0280: * have, but I don't know how to make use of it.
0281: *
0282: * @return the value of description
0283: */
0284: public String getDescription() {
0285: return description;
0286: }
0287:
0288: /**
0289: * Returns the directory name for this file by chopping off the file name
0290: * and the separator.
0291: *
0292: * @return the directory name for this file
0293: */
0294: public String getDirectoryName() {
0295: String result = new String();
0296: int index = pathName.lastIndexOf(File.separator);
0297:
0298: if (index >= 0) {
0299: result = pathName.substring(0, index);
0300: }
0301:
0302: return result;
0303: }
0304:
0305: /**
0306: * Returns the file name (without path) for the file
0307: *
0308: * @return the file name for this file
0309: */
0310: public String getFileName() {
0311: String result = pathName.substring(pathName
0312: .lastIndexOf(File.separator) + 1);
0313:
0314: return result;
0315: }
0316:
0317: /*
0318: * (non-Javadoc)
0319: * @see org.geotools.feature.FeatureType#getNamespace()
0320: */
0321: public URI getNamespace() {
0322: return featureType.getNamespace();
0323: }
0324:
0325: // /*
0326: // * (non-Javadoc)
0327: // * @see org.geotools.feature.FeatureType#getNamespace()
0328: // */
0329: // public URI getNamespaceURI() {
0330: // return featureType.getNamespaceURI();
0331: // }
0332:
0333: /**
0334: * Gets the value of narrativeTable variable file name.
0335: *
0336: * @return the value of narrativeTable
0337: */
0338: public String getNarrativeTable() {
0339: return narrativeTable;
0340: }
0341:
0342: /**
0343: * Gets the full path name for this file
0344: *
0345: * @return the path name for this file
0346: */
0347: public String getPathName() {
0348: return pathName;
0349: }
0350:
0351: /**
0352: * Method <code><code>getRecordSize</code></code> is used to return size in
0353: * bytes of records stored in this table. If table keeps variable length
0354: * records <code>-1</code> should be returned.
0355: *
0356: * @return an <code><code>int</code></code> value
0357: */
0358: protected int getRecordSize() {
0359: int size = 0;
0360: // int currentSize;
0361:
0362: Iterator iter = columns.iterator();
0363:
0364: while (iter.hasNext()) {
0365: VPFColumn column = (VPFColumn) iter.next();
0366: // currentSize = column.getFieldLength();
0367: if (column.getRestriction() == Filter.INCLUDE
0368: || column.getRestriction() == null) {
0369: // if (currentSize < 0) {
0370: return -1;
0371: } else {
0372: int currentSize = 0;
0373: if (column.getRestriction() instanceof CompareFilter) {
0374: try {
0375: CompareFilter cf = (CompareFilter) column
0376: .getRestriction();
0377: if (cf.getLeftValue() instanceof LengthFunction) {
0378: currentSize = Integer
0379: .parseInt(((LiteralExpression) cf
0380: .getRightValue())
0381: .getLiteral().toString());
0382: } else {
0383: if (cf.getRightValue() instanceof LengthFunction)
0384: currentSize = Integer
0385: .parseInt(((LiteralExpression) cf
0386: .getLeftValue())
0387: .getLiteral()
0388: .toString());
0389: }
0390: } catch (Throwable t) {
0391: }
0392: }
0393: size += currentSize;
0394: }
0395: }
0396:
0397: return size;
0398: }
0399:
0400: /**
0401: * Returns a row with a matching value for the provided column
0402: *
0403: * @param idName The name of the column to look for, such as "id"
0404: * @param id An identifier for the requested row
0405: *
0406: * @return The first row which matches the ID
0407: *
0408: * @throws IllegalAttributeException The feature can not be created due to
0409: * illegal attributes in the source file
0410: */
0411: public Feature getRowFromId(String idName, int id)
0412: throws IllegalAttributeException {
0413: Feature result = null;
0414:
0415: try {
0416: // This speeds things up mightily
0417: String firstColumnName = ((VPFColumn) columns.get(0))
0418: .getName();
0419:
0420: if (idName.equals(firstColumnName)) {
0421: setPosition(id);
0422: result = readFeature();
0423:
0424: Number value = (Number) result.getAttribute(idName);
0425:
0426: // Check to make sure we got a primary key match
0427: if ((value == null) || (value.intValue() != id)) {
0428: result = null;
0429: }
0430: }
0431:
0432: if (result == null) {
0433: Iterator joinedIter = readAllRows().iterator();
0434: result = getRowFromIterator(joinedIter, idName, id);
0435: }
0436: } catch (IOException exc) {
0437: exc.printStackTrace();
0438: }
0439:
0440: return result;
0441: }
0442:
0443: /**
0444: * Returns a single matching row from the Iterator, ignoring rows that do
0445: * not match a particular id
0446: *
0447: * @param iter the iterator to examine
0448: * @param idName The name of the column to check
0449: * @param id The value of the column to check
0450: *
0451: * @return a TableRow that matches the other parameters
0452: */
0453: private Feature getRowFromIterator(Iterator iter, String idName,
0454: int id) {
0455: Feature result = null;
0456: Feature currentFeature;
0457: int value = -1;
0458:
0459: while (iter.hasNext()) {
0460: currentFeature = (Feature) iter.next();
0461: try {
0462: value = Integer.parseInt(currentFeature.getAttribute(
0463: idName).toString());
0464:
0465: if (id == value) {
0466: result = currentFeature;
0467:
0468: break;
0469: }
0470: } catch (NumberFormatException exc) {
0471: // If this happens, the data is invalid so dumping a stack trace seems reasonable
0472: exc.printStackTrace();
0473: }
0474: }
0475:
0476: return result;
0477: }
0478:
0479: /*
0480: * (non-Javadoc)
0481: * @see org.geotools.feature.FeatureType#getTypeName()
0482: */
0483: public String getTypeName() {
0484: return featureType.getTypeName();
0485: }
0486:
0487: /*
0488: * (non-Javadoc)
0489: * @see org.geotools.feature.FeatureType#hasAttributeType(java.lang.String)
0490: */
0491: public boolean hasAttributeType(String xPath) {
0492: return featureType.hasAttributeType(xPath);
0493: }
0494:
0495: /**
0496: * Determines if the stream contains storage for another object. Who knows
0497: * how well this will work on variable length objects?
0498: *
0499: * @return a <code>boolean</code>
0500: */
0501: public boolean hasNext() {
0502: boolean result = false;
0503:
0504: try {
0505: int recordSize = getRecordSize();
0506:
0507: if (recordSize > 0) {
0508: result = inputStream.length() >= (inputStream
0509: .getFilePointer() + recordSize);
0510: } else {
0511: result = inputStream.length() >= (inputStream
0512: .getFilePointer() + 1);
0513: }
0514: } catch (IOException exc) {
0515: // No idea what to do if this happens
0516: exc.printStackTrace();
0517: }
0518:
0519: return result;
0520: }
0521:
0522: /*
0523: * (non-Javadoc)
0524: * @see org.geotools.feature.FeatureType#isAbstract()
0525: */
0526: public boolean isAbstract() {
0527: return featureType.isAbstract();
0528: }
0529:
0530: /*
0531: * (non-Javadoc)
0532: * @see org.geotools.feature.FeatureType#isDescendedFrom(org.geotools.feature.FeatureType)
0533: */
0534: public boolean isDescendedFrom(FeatureType type) {
0535: return featureType.isDescendedFrom(type);
0536: }
0537:
0538: /*
0539: * (non-Javadoc)
0540: * @see org.geotools.feature.FeatureType#isDescendedFrom(java.lang.String, java.lang.String)
0541: */
0542: public boolean isDescendedFrom(URI nsURI, String typeName) {
0543: return featureType.isDescendedFrom(nsURI, typeName);
0544: }
0545:
0546: /**
0547: * Generates a list containing all of the features in the file
0548: *
0549: * @return a <code>List</code> value containing Feature objects
0550: *
0551: * @exception IOException if an error occurs
0552: */
0553: public AbstractList readAllRows() throws IOException {
0554: AbstractList list = new LinkedList();
0555:
0556: try {
0557: setPosition(1);
0558: } catch (IOException exc) {
0559: // This indicates that there are no rows
0560: return list;
0561: }
0562:
0563: try {
0564: Feature row = readFeature();
0565:
0566: while (row != null) {
0567: list.add(row);
0568: if (hasNext()) {
0569: row = readFeature();
0570: } else {
0571: row = null;
0572: }
0573: }
0574: } catch (IllegalAttributeException exc1) {
0575: throw new IOException(exc1.getMessage());
0576: }
0577:
0578: return list;
0579: }
0580:
0581: /**
0582: * Reads a single byte as a character value
0583: *
0584: * @return a <code>char</code> value
0585: *
0586: * @exception IOException if an error occurs
0587: */
0588: protected char readChar() throws IOException {
0589: return (char) inputStream.read();
0590: }
0591:
0592: /**
0593: * Reads a column definition from the file header
0594: *
0595: * @return a <code>VPFColumn</code> value
0596: *
0597: * @exception VPFHeaderFormatException if an error occurs
0598: * @exception IOException if an error occurs
0599: * @exception NumberFormatException if an error occurs
0600: */
0601: private VPFColumn readColumn() throws VPFHeaderFormatException,
0602: IOException, NumberFormatException {
0603: char ctrl = readChar();
0604:
0605: if (ctrl == VPF_RECORD_SEPARATOR) {
0606: return null;
0607: }
0608:
0609: String name = ctrl + readString("=");
0610: char type = readChar();
0611: ctrl = readChar();
0612:
0613: if (ctrl != VPF_ELEMENT_SEPARATOR) {
0614: throw new VPFHeaderFormatException(
0615: "Header format does not fit VPF file definition.");
0616: }
0617:
0618: String elemStr = readString(
0619: new String() + VPF_ELEMENT_SEPARATOR).trim();
0620:
0621: if (elemStr.equals("*")) {
0622: elemStr = "-1";
0623: }
0624:
0625: int elements = Integer.parseInt(elemStr);
0626: char key = readChar();
0627: ctrl = readChar();
0628:
0629: if (ctrl != VPF_ELEMENT_SEPARATOR) {
0630: throw new VPFHeaderFormatException(
0631: "Header format does not fit VPF file definition.");
0632: }
0633:
0634: String colDesc = readString(new String()
0635: + VPF_ELEMENT_SEPARATOR + VPF_FIELD_SEPARATOR);
0636: String descTableName = readString(new String()
0637: + VPF_ELEMENT_SEPARATOR + VPF_FIELD_SEPARATOR);
0638: String indexFile = readString(new String()
0639: + VPF_ELEMENT_SEPARATOR + VPF_FIELD_SEPARATOR);
0640: String narrTable = readString(new String()
0641: + VPF_ELEMENT_SEPARATOR + VPF_FIELD_SEPARATOR);
0642:
0643: return new VPFColumn(name, type, elements, key, colDesc,
0644: descTableName, indexFile, narrTable);
0645: }
0646:
0647: /**
0648: * Constructs an object which is an instance of Geometry
0649: * by reading values from the file.
0650: * @param instancesCount number of coordinates to read
0651: * @param dimensionality either 2 or 3
0652: * @param readDoubles true: read a double value; false: read a float value
0653: * @return the constructed object
0654: * @throws IOException on any file IO errors
0655: */
0656: protected Object readGeometry(int instancesCount,
0657: int dimensionality, boolean readDoubles) throws IOException {
0658: Object result = null;
0659: Coordinate coordinate = null;
0660: CoordinateList coordinates = new CoordinateList();
0661: GeometryFactory factory = new GeometryFactory();
0662:
0663: for (int inx = 0; inx < instancesCount; inx++) {
0664: switch (dimensionality) {
0665: case 2:
0666: coordinate = new Coordinate(readDoubles ? readDouble()
0667: : readFloat(), readDoubles ? readDouble()
0668: : readFloat());
0669:
0670: break;
0671:
0672: case 3:
0673: coordinate = new Coordinate(readDoubles ? readDouble()
0674: : readFloat(), readDoubles ? readDouble()
0675: : readFloat(), readDoubles ? readDouble()
0676: : readFloat());
0677:
0678: break;
0679:
0680: default:
0681: //WTF???
0682: }
0683:
0684: coordinates.add(coordinate);
0685: }
0686:
0687: // Special handling for text primitives per the VPF spec.
0688: // The first 2 points are the endpoints of the line, the following
0689: // points fill in between the first 2 points.
0690: if (pathName.endsWith(TEXT_PRIMITIVE) && coordinates.size() > 2) {
0691: Object o = coordinates.remove(1);
0692: coordinates.add(o);
0693: }
0694:
0695: if (instancesCount == 1) {
0696: result = factory.createPoint(coordinate);
0697: } else {
0698: result = factory
0699: .createLineString(DefaultCoordinateSequenceFactory
0700: .instance().create(
0701: coordinates.toCoordinateArray()));
0702: }
0703:
0704: return result;
0705: }
0706:
0707: /**
0708: * Retrieves a double from the file
0709: *
0710: * @return a <code>double</code> value
0711: *
0712: * @exception IOException if an error occurs
0713: */
0714: protected double readDouble() throws IOException {
0715: return DataUtils.decodeDouble(readNumber(DATA_LONG_FLOAT_LEN));
0716: }
0717:
0718: /**
0719: * Retrieves a feature from the file
0720: *
0721: * @return the retieved feature
0722: *
0723: * @throws IOException on any file IO errors
0724: * @throws IllegalAttributeException if any of the attributes retrieved are
0725: * illegal
0726: */
0727: public Feature readFeature() throws IOException,
0728: IllegalAttributeException {
0729: Feature result = null;
0730: Iterator iter = columns.iterator();
0731: VPFColumn column;
0732: boolean textPrimitive = pathName.endsWith(TEXT_PRIMITIVE);
0733: int size = columns.size();
0734: if (textPrimitive)
0735: size++;
0736: Object[] values = new Object[size];
0737:
0738: try {
0739: for (int inx = 0; inx < columns.size(); inx++) {
0740: column = (VPFColumn) columns.get(inx);
0741:
0742: if (column.getRestriction() == Filter.INCLUDE
0743: || column.getRestriction() == null) {
0744: // if (column.getFieldLength() < 0) {
0745: values[inx] = readVariableSizeData(column
0746: .getTypeChar());
0747: } else {
0748: values[inx] = readFixedSizeData(column
0749: .getTypeChar(), column.getElementsNumber());
0750: }
0751: }
0752: if (textPrimitive) {
0753: values[size - 1] = "nam";
0754: }
0755:
0756: result = featureType.create(values);
0757: } catch (EOFException exp) {
0758: // Should we be throwing an exception instead of eating it?
0759: exp.printStackTrace();
0760: }
0761:
0762: return result;
0763: }
0764:
0765: /**
0766: * Retrieves a fixed amount of data from the file
0767: *
0768: * @param dataType a <code>char</code> value indicating the data type
0769: * @param instancesCount an <code>int</code> value indicating the number
0770: * of instances to retrieve.
0771: *
0772: * @return an <code>Object</code> value
0773: *
0774: * @exception IOException if an error occurs
0775: */
0776: protected Object readFixedSizeData(char dataType, int instancesCount)
0777: throws IOException {
0778: Object result = null;
0779:
0780: switch (dataType) {
0781: case DATA_TEXT:
0782: case DATA_LEVEL1_TEXT:
0783: case DATA_LEVEL2_TEXT:
0784: case DATA_LEVEL3_TEXT:
0785:
0786: byte[] dataBytes = new byte[instancesCount
0787: * DataUtils.getDataTypeSize(dataType)];
0788: inputStream.readFully(dataBytes);
0789: result = DataUtils.decodeData(dataBytes, dataType);
0790:
0791: break;
0792:
0793: case DATA_SHORT_FLOAT:
0794: result = new Float(readFloat());
0795:
0796: break;
0797:
0798: case DATA_LONG_FLOAT:
0799: result = new Double(readDouble());
0800:
0801: break;
0802:
0803: case DATA_SHORT_INTEGER:
0804: result = new Short(readShort());
0805:
0806: break;
0807:
0808: case DATA_LONG_INTEGER:
0809: result = new Integer(readInteger());
0810:
0811: break;
0812:
0813: case DATA_NULL_FIELD:
0814: result = "NULL";
0815:
0816: break;
0817:
0818: case DATA_TRIPLET_ID:
0819: result = readTripletId();
0820:
0821: break;
0822:
0823: case DATA_2_COORD_F:
0824: result = readGeometry(instancesCount, 2, false);
0825:
0826: break;
0827:
0828: case DATA_2_COORD_R:
0829: result = readGeometry(instancesCount, 2, true);
0830:
0831: break;
0832:
0833: case DATA_3_COORD_F:
0834: result = readGeometry(instancesCount, 3, false);
0835:
0836: break;
0837:
0838: case DATA_3_COORD_R:
0839: result = readGeometry(instancesCount, 3, true);
0840:
0841: break;
0842:
0843: default:
0844: break;
0845: } // end of switch (dataType)
0846:
0847: return result;
0848: }
0849:
0850: /**
0851: * Retrieves a floating point number from the file.
0852: *
0853: * @return a <code>float</code> value
0854: *
0855: * @exception IOException if an error occurs
0856: */
0857: protected float readFloat() throws IOException {
0858: return DataUtils.decodeFloat(readNumber(DATA_SHORT_FLOAT_LEN));
0859: }
0860:
0861: /**
0862: * Retrieves a number of attributes from the file header
0863: *
0864: * @exception VPFHeaderFormatException if an error occurs
0865: * @exception IOException if an error occurs
0866: */
0867: protected void readHeader() throws VPFHeaderFormatException,
0868: IOException {
0869: byte[] fourBytes = new byte[4];
0870: inputStream.readFully(fourBytes);
0871:
0872: byteOrder = readChar();
0873:
0874: char ctrl = byteOrder;
0875:
0876: if (byteOrder == VPF_RECORD_SEPARATOR) {
0877: byteOrder = LITTLE_ENDIAN_ORDER;
0878: } else {
0879: ctrl = readChar();
0880: }
0881:
0882: if (byteOrder == LITTLE_ENDIAN_ORDER) {
0883: fourBytes = DataUtils.toBigEndian(fourBytes);
0884: }
0885:
0886: headerLength = DataUtils.decodeInt(fourBytes);
0887:
0888: if (ctrl != VPF_RECORD_SEPARATOR) {
0889: throw new VPFHeaderFormatException(
0890: "Header format does not fit VPF file definition.");
0891: }
0892:
0893: description = readString(new String() + VPF_RECORD_SEPARATOR);
0894: narrativeTable = readString(new String() + VPF_RECORD_SEPARATOR);
0895:
0896: VPFColumn column = readColumn();
0897:
0898: while (column != null) {
0899: columns.add(column);
0900: ctrl = readChar();
0901:
0902: if (ctrl != VPF_FIELD_SEPARATOR) {
0903: throw new VPFHeaderFormatException(
0904: "Header format does not fit VPF file definition.");
0905: }
0906:
0907: column = readColumn();
0908: }
0909:
0910: if (getRecordSize() < 0) {
0911: variableIndex = new VariableIndexInputStream(
0912: getVariableIndexFileName(), getByteOrder());
0913: }
0914: }
0915:
0916: /**
0917: * Retrieves an integer value from the file
0918: *
0919: * @return an <code>int</code> value
0920: *
0921: * @exception IOException if an error occurs
0922: */
0923: protected int readInteger() throws IOException {
0924: return DataUtils.decodeInt(readNumber(DATA_LONG_INTEGER_LEN));
0925: }
0926:
0927: /**
0928: * Reads some byte data from the file
0929: *
0930: * @param cnt an <code>int</code> value indicating the number of bytes to retrieve
0931: *
0932: * @return a <code>byte[]</code> value
0933: *
0934: * @throws IOException if an error occurs
0935: */
0936: protected byte[] readNumber(int cnt) throws IOException {
0937: byte[] dataBytes = new byte[cnt];
0938: inputStream.readFully(dataBytes);
0939:
0940: if (byteOrder == LITTLE_ENDIAN_ORDER) {
0941: dataBytes = DataUtils.toBigEndian(dataBytes);
0942: }
0943:
0944: return dataBytes;
0945: }
0946:
0947: /**
0948: * Retrieves a short value from the file
0949: *
0950: * @return a <code>short</code> value
0951: *
0952: * @exception IOException if an error occurs
0953: */
0954: protected short readShort() throws IOException {
0955: return DataUtils
0956: .decodeShort(readNumber(DATA_SHORT_INTEGER_LEN));
0957: }
0958:
0959: /**
0960: * Reads a string value from the file
0961: *
0962: * @param terminators a <code>String</code> value indicating the terminators to look for
0963: *
0964: * @return a <code>String</code> value
0965: *
0966: * @exception IOException if an error occurs
0967: */
0968: protected String readString(String terminators) throws IOException {
0969: StringBuffer text = new StringBuffer();
0970: char ctrl = readChar();
0971:
0972: if (terminators.indexOf(ctrl) != -1) {
0973: if (ctrl == VPF_FIELD_SEPARATOR) {
0974: unread(1);
0975: }
0976:
0977: return null;
0978: }
0979:
0980: while (terminators.indexOf(ctrl) == -1) {
0981: text.append(ctrl);
0982: ctrl = readChar();
0983: }
0984:
0985: if (text.toString().equals(STRING_NULL_VALUE)) {
0986: return null;
0987: } else {
0988: return text.toString();
0989: }
0990: }
0991:
0992: /**
0993: * Retrieves a triplet object from the file
0994: *
0995: * @return a <code>TripletId</code> value
0996: *
0997: * @throws IOException on any IO errors
0998: */
0999: protected TripletId readTripletId() throws IOException {
1000: // TODO: does this take into account byte order properly?
1001: byte tripletDef = (byte) inputStream.read();
1002: int dataSize = TripletId.calculateDataSize(tripletDef);
1003: byte[] tripletData = new byte[dataSize + 1];
1004: tripletData[0] = tripletDef;
1005:
1006: if (dataSize > 0) {
1007: inputStream.readFully(tripletData, 1, dataSize);
1008: }
1009:
1010: return new TripletId(tripletData);
1011: }
1012:
1013: /**
1014: * Retrieves variable sized data from the file by first reading an integer
1015: * which indicates how many instances of the data type to retrieve
1016: *
1017: * @param dataType a <code>char</code> value indicating the data type
1018: *
1019: * @return an <code>Object</code> value
1020: *
1021: * @exception IOException if an error occurs
1022: */
1023: protected Object readVariableSizeData(char dataType)
1024: throws IOException {
1025: int instances = readInteger();
1026:
1027: return readFixedSizeData(dataType, instances);
1028: }
1029:
1030: /**
1031: * Resets the file stream by setting its pointer
1032: * to the first position after the header.
1033: *
1034: */
1035: public void reset() {
1036: try {
1037: setPosition(1);
1038: } catch (IOException exc) {
1039: // This just means there is nothing in the table
1040: }
1041: }
1042:
1043: /**
1044: * Close the input stream pointed to by the object
1045: * @throws IOException in some unlikely situation
1046: */
1047: public void close() throws IOException {
1048: inputStream.close();
1049: if (variableIndex != null) {
1050: variableIndex.close();
1051: }
1052: }
1053:
1054: /**
1055: * Sets the position in the stream
1056: *
1057: * @param pos A 1-indexed position
1058: *
1059: * @throws IOException on any IO failures
1060: */
1061: protected void setPosition(long pos) throws IOException {
1062: if (getRecordSize() < 0) {
1063: VariableIndexRow varRow = (VariableIndexRow) variableIndex
1064: .readRow((int) pos);
1065: inputStream.seek(varRow.getOffset());
1066: } else {
1067: inputStream.seek(getAdjustedHeaderLength()
1068: + ((pos - 1) * getRecordSize()));
1069: }
1070: }
1071:
1072: /* (non-Javadoc)
1073: * @see java.lang.Object#toString()
1074: */
1075: public String toString() {
1076: return featureType.toString();
1077: }
1078:
1079: /**
1080: * Back up a specified number of bytes in the file stream
1081: *
1082: * @param bytes a <code>long</code> value
1083: *
1084: * @exception IOException if an error occurs
1085: */
1086: protected void unread(long bytes) throws IOException {
1087: inputStream.seek(inputStream.getFilePointer() - bytes);
1088: }
1089:
1090: /**
1091: * Returns the full path of the variable index associated with the current file
1092: *
1093: * @return a <code>String</code> value
1094: */
1095: private String getVariableIndexFileName() {
1096: String result = null;
1097: String fileName = getFileName();
1098:
1099: if (fileName.toLowerCase().equals(FEATURE_CLASS_SCHEMA_TABLE)) {
1100: result = getDirectoryName().concat(File.separator).concat(
1101: "fcz");
1102: } else {
1103: result = getDirectoryName().concat(File.separator).concat(
1104: fileName.substring(0, fileName.length() - 1) + "x");
1105: }
1106:
1107: return result;
1108: }
1109:
1110: /* (non-Javadoc)
1111: * @see org.geotools.feature.FeatureType#find(java.lang.String)
1112: */
1113: public int find(String attName) {
1114: return featureType.find(attName);
1115: }
1116: }
|