/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2002-2008, Open Source Geospatial Foundation (OSGeo) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package org.geotools.data.property.ng; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.util.NoSuchElementException; import java.util.Properties; import java.util.logging.Logger; import org.geotools.data.DataSourceException; import org.geotools.data.DataUtilities; import org.geotools.data.FeatureReader; import org.geotools.feature.SchemaException; import org.geotools.feature.simple.SimpleFeatureBuilder; import org.geotools.util.Converters; import org.geotools.util.logging.Logging; import org.opengis.feature.simple.SimpleFeature; 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; /** * Read a property file directly. * <p> * This implementation does not perform any filtering or processing; it leaves that up to wrappers to manipulate the content into the format or * projection requested by the user. * <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> * Many 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 (LISAsoft) * * @source $URL$ * @version $Id * @since 8.0 */ public class PropertyFeatureReader implements FeatureReader<SimpleFeatureType, SimpleFeature> { private static final Logger LOGGER = Logging.getLogger("org.geotools.data.property"); BufferedReader reader; SimpleFeatureType type; String line; String next; String[] text; String fid; public PropertyFeatureReader(String namespace, File file) throws IOException { 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("Property file schema not available found"); } String typeSpec = line.substring(2); String name = file.getName(); String typeName = name.substring(0,name.lastIndexOf('.')); try { type = DataUtilities.createType(namespace, typeName, typeSpec); } catch (SchemaException e) { throw new DataSourceException(typeName + " schema not available", e); } line = null; next = null; } public SimpleFeatureType getFeatureType() { return type; } /** * Grab the next feature from the property file. * * @return feature * * @throws IOException * @throws NoSuchElementException Check hasNext() to avoid reading off the end of the file */ public SimpleFeature next() throws IOException, NoSuchElementException { if (hasNext()) { line = next; next = null; int split = line.indexOf('='); fid = line.substring(0, split); text = line.substring(split + 1).split("\\|", -1);// use -1 as limit to include empty trailing spaces if (type.getAttributeCount() != text.length) throw new DataSourceException("Format error: expected " + type.getAttributeCount() + " attributes, but found " + text.length + ". [" + line + "]"); } else { throw new NoSuchElementException(); } Object[] values = new Object[type.getAttributeCount()]; for (int i = 0; i < type.getAttributeCount(); i++) { try { values[i] = read(i); } catch (RuntimeException e) { values[i] = null; } catch (IOException e) { throw e; } } return SimpleFeatureBuilder.build(type, values, fid); } /** * 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]; } 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; // it was an explicit "<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; } /** * DOCUMENT ME! * * @return DOCUMENT ME! * * @throws IOException DOCUMENT ME! */ public boolean hasNext() throws IOException { if (next != null) { return true; } next = readLine(); return next != null; } String readLine() throws IOException { StringBuilder buffer = new StringBuilder(); while (true) { String txt = reader.readLine(); if (txt == null) { break; } if (txt.startsWith("#") || txt.startsWith("!")) { continue; // skip content } txt = trimLeft(txt); if (txt.endsWith("\\")) { buffer.append(txt.substring(0, txt.length() - 1)); buffer.append("\n"); continue; } else { buffer.append(txt); break; } } if (buffer.length() == 0) { return null; // there is no line } String raw = buffer.toString(); raw = raw.replace("\\n", "\n"); raw = raw.replace("\\r", "\r"); raw = raw.replace("\\t", "\t"); return raw; } /** * Trim leading white space as described Properties. * * @see Properties#load(java.io.Reader) * @param txt * @return txt leading whitespace removed */ String trimLeft(String txt) { // trim int start = 0; WHITESPACE: for (int i = 0; i < txt.length(); i++) { char ch = txt.charAt(i); if (Character.isWhitespace(ch)) { continue; } else { start = i; break WHITESPACE; } } return txt.substring(start); } /** * Be sure to call close when you are finished with this reader; as it must close the file it has open. * * @throws IOException */ public void close() throws IOException { if (reader == null) { LOGGER.warning("Stream seems to be already closed."); } else { reader.close(); } reader = null; } }