package org.geotools.data.property;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.NoSuchElementException;
import org.geotools.data.AttributeReader;
import org.geotools.data.DataSourceException;
import org.geotools.data.DataUtilities;
import org.geotools.feature.SchemaException;
import org.geotools.util.Converters;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.AttributeDescriptor;
import org.opengis.feature.type.GeometryType;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import com.vividsolutions.jts.geom.Geometry;
/**
* Simple AttributeReader that works against Java properties files.
* <p>
* This AttributeReader is part of the GeoTools AbstractDataStore tutorial, and
* should be considered a Toy.
* </p>
* <p>
* The content of this file should start with a the property "_" with the value
* being the typeSpec describing the featureType. Thereafter each line will
* should have a FeatureID as the property and the attribtues as the value
* separated by | characters.
* </p>
*
* <pre>
* <code>
* _=id:Integer|name:String|geom:Geometry
* fid1=1|Jody|<i>well known text</i>
* fid2=2|Brent|<i>well known text</i>
* fid3=3|Dave|<i>well known text</i>
* </code>
* </pre>
* <p>
* May values may be represented by a special tag: <code><null></code>. An empty
* element: <code>||</code> is interpreted as the empty string:
* </p>
*
* <pre>
* <code>
* fid4=4||<null> -> Feature( id=2, name="", geom=null )
* </code>
* </pre>
*
* @author Jody Garnett
*/
public class PropertyAttributeReader implements AttributeReader {
BufferedReader reader;
SimpleFeatureType type;
String line;
String next;
String[] text;
String fid;
/**
* Creates a new PropertyAttributeReader object.
*
* @param file File being read
* @throws IOException
* @throws DataSourceException
*/
public PropertyAttributeReader(File file) throws IOException {
String typeName = typeName(file);
String namespace = namespace(file);
reader = new BufferedReader(new FileReader(file));
// read until "_=";
while ((line = reader.readLine()) != null) {
if (line.startsWith("_="))
break;
}
if ((line == null) || !line.startsWith("_=")) {
throw new IOException(typeName + " schema not available");
}
String typeSpec = line.substring(2);
try {
type = DataUtilities.createType(namespace, typeName, typeSpec);
} catch (SchemaException e) {
throw new DataSourceException(typeName + " schema not available", e);
}
line = null;
next = null;
}
/**
* TypeName for the provided file.
*
* @param file File being read
* @return suitable typeName
*/
private static String typeName(File file) {
String name = file.getName();
int split = name.lastIndexOf('.');
return (split == -1) ? name : name.substring(0, split);
}
/**
* Namespace for the provided file
*
* @param file File being read
* @return suitable namespace
*/
private static String namespace(File file) {
File parent = file.getParentFile();
return (parent == null) ? "" : (parent.getName() + ".");
}
// class definition end
// implementation start
/**
* Number of attributes to expect based on header information.
*
* @return number of attribtues
*/
public int getAttributeCount() {
return type.getAttributeCount();
}
/**
* AttribtueDescriptor (name and type) for position marked by index.
*
* @param index
* @return AttributeDescriptor describing attribute name and type
* @throws ArrayIndexOutOfBoundsException
*/
public AttributeDescriptor getAttributeType(int index)
throws ArrayIndexOutOfBoundsException {
return type.getDescriptor(index);
}
/**
* Close the internal reader accessing the file.
*
* @throws IOException
*/
public void close() throws IOException {
reader.close();
reader = null;
}
/**
* Check if the file has another line.
*
* @return <code>true</code> if the file has another line
* @throws IOException
*/
public boolean hasNext() throws IOException {
if (next != null) {
return true;
}
next = reader.readLine();
return next != null;
}
/**
* Read the next line from the reader.
*
* @return line
* @throws IOException
*/
String readLine() throws IOException {
while( true ){
String line = reader.readLine();
if( line == null ){
return null; // no more content
}
if( line.startsWith("#") || line.startsWith("!")){
continue; // skip comments
}
else {
return line;
}
}
}
/**
* Retrieve the next line.
*
* @throws IOException
* @throws NoSuchElementException
*/
public void next() throws IOException {
if (hasNext()) {
line = next;
next = null;
int split = line.indexOf('=');
fid = line.substring(0, split);
text = line.substring(split + 1).split("\\|");
if (type.getAttributeCount() != text.length)
throw new DataSourceException("format error: expected "
+ type.getAttributeCount() + " attributes, but found "
+ text.length + ". [" + line + "]");
} else {
throw new NoSuchElementException();
}
}
/**
* Read attribute in position marked by <code>index</code>.
*
* @param index Attribute position to read
* @return Value for the attribtue in position <code>index</code>
* @throws IOException
* @throws ArrayIndexOutOfBoundsException
*/
public Object read(int index) throws IOException,
ArrayIndexOutOfBoundsException {
if (line == null) {
throw new IOException(
"No content available - did you remeber to call next?");
}
AttributeDescriptor attType = type.getDescriptor(index);
String stringValue = null;
try {
// read the value
stringValue = text[index];
// trim off any whitespace
if (stringValue != null) {
stringValue = stringValue.trim();
}
if ("".equals(stringValue)) {
stringValue = null;
}
} catch (RuntimeException e1) {
e1.printStackTrace();
stringValue = null;
}
// check for special <null> flag
if ("<null>".equals(stringValue)) {
stringValue = null;
}
if (stringValue == null) {
if (attType.isNillable()) {
return null;
}
}
// Use of Converters to convert from String to requested java binding
Object value = Converters.convert(stringValue, attType.getType()
.getBinding());
if (attType.getType() instanceof GeometryType) {
// this is to be passed on in the geometry objects so the srs name
// gets encoded
CoordinateReferenceSystem crs = ((GeometryType) attType.getType())
.getCoordinateReferenceSystem();
if (crs != null) {
// must be geometry, but check anyway
if (value != null && value instanceof Geometry) {
((Geometry) value).setUserData(crs);
}
}
}
return value;
}
// implementation end
// getFeatureID start
/**
* Retrieve the FeatureId identifying the current line.
*
* @return FeatureID for the current line.
*/
public String getFeatureID() {
if (line == null) {
return null;
}
return fid;
}
// getFeatureID end
}