/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2004-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.vpf;
import java.io.File;
import java.io.IOException;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import org.geotools.data.FeatureReader;
import org.geotools.data.vpf.file.VPFFile;
import org.geotools.data.vpf.file.VPFFileFactory;
import org.geotools.data.vpf.ifc.FCode;
import org.geotools.feature.IllegalAttributeException;
import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.geotools.feature.type.AnnotationFeatureType;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.AttributeDescriptor;
/**
*
* @author <a href="mailto:knuterik@onemap.org">Knut-Erik Johnsen</a>, Project OneMap
* @author Chris Holmes, Fulbright.
*
* @source $URL$
*/
public class VPFFeatureReader
implements FeatureReader<SimpleFeatureType, SimpleFeature>, FCode {
private boolean hasNext = true;
private boolean nextCalled = true;
private SimpleFeature currentFeature = null;
private final VPFFeatureType featureType;
/** Creates a new instance of VPFFeatureReader */
public VPFFeatureReader(VPFFeatureType type) {
this.featureType = type;
}
/* (non-Javadoc)
* @see org.geotools.data.FeatureReader#close()
*/
public void close() throws IOException {
reset();
}
/**
* Put together a map of VPF files and their corresponding
* TableRows
*
* @param file
* @param row
*/
private Map generateFileRowMap(VPFFile file, SimpleFeature row)
throws IOException{
String tileFileName = null;
Map rows = new HashMap();
rows.put(file, row);
Iterator joinIter = featureType.getFeatureClass().getJoinList().iterator();
while (joinIter.hasNext()) {
ColumnPair columnPair = (ColumnPair) joinIter.next();
VPFFile primaryFile = getVPFFile(columnPair.column1);
VPFFile joinFile = null;
joinFile = getVPFFile(columnPair.column2);
if (!rows.containsKey(joinFile) && rows.containsKey(primaryFile)) {
SimpleFeature joinRow = (SimpleFeature) rows.get(primaryFile);
try {
int joinID = Integer.parseInt(joinRow.getAttribute(columnPair.column1.getLocalName()).toString());
rows.put(joinFile, getVPFFile(columnPair.column2).getRowFromId(columnPair.column2.getLocalName(), joinID));
} catch (NullPointerException exc) {
// Non-matching joins - just put in a NULL
rows.put(joinFile, null);
} catch (IllegalAttributeException exc) {
// I really don't expect to see this one
exc.printStackTrace();
rows.put(joinFile, null);
}
}
}
return rows;
}
/* (non-Javadoc)
* @see org.geotools.data.FeatureReader#getFeatureType()
*/
public SimpleFeatureType getFeatureType() {
return featureType;
}
/* (non-Javadoc)
* @see org.geotools.data.FeatureReader#hasNext()
*/
public boolean hasNext() throws IOException {
if (nextCalled) {
while(readNext());
nextCalled = false;
}
return hasNext;
}
/* (non-Javadoc)
* @see org.geotools.data.FeatureReader#next()
*/
public SimpleFeature next() throws IOException, IllegalAttributeException,
NoSuchElementException {
nextCalled = true;
return currentFeature;
}
/**
* Read a row and determine if it matches the feature type
* Three possibilities here:
* row is null -- hasNext = false, do not try again
* row matches -- hasNext = true, do not try again
* row does not match -- hasNext is undefined because we must try again
* @return Whether we need to read again
*/
private boolean readNext() throws IOException {
boolean result = true;
VPFFile file = (VPFFile) featureType.getFeatureClass().getFileList().get(0);
hasNext = false;
SimpleFeature row = null;
try {
if(file.hasNext()){
row = file.readFeature();
}
} catch (IOException exc1) {
// TODO Auto-generated catch block
exc1.printStackTrace();
} catch (IllegalAttributeException exc1) {
// TODO Auto-generated catch block
exc1.printStackTrace();
}
if ((row == null)) {
hasNext = false;
result = false;
}
// Exclude objects with a different FACC Code
else if (featureType.getFaccCode() != null){
try {
Object temp = null;
for (int i = 0; temp == null && i < ALLOWED_FCODE_ATTRIBUTES.length; i++) {
temp = row.getAttribute( ALLOWED_FCODE_ATTRIBUTES[i] );
}
String faccCode = temp.toString().trim();
if(featureType.getFaccCode().equals(faccCode)){
retrieveObject(file, row);
hasNext = true;
result = false;
}
} catch (RuntimeException exc) {
// Ignore this case because it typically means the f_code is invalid
}
}
return result;
}
/**
* Get the values from all of the columns
* based on their presence (or absense) in the rows
*
* Potential cases:
* simple column
* join column
* non-matching join
* null value
* geometry
*
* @param file the file
* @param row the row
*
*/
private void retrieveObject(VPFFile file, SimpleFeature row) throws IOException{
VPFFile secondFile = null;
VPFColumn column = null;
Map rows = generateFileRowMap(file, row);
List<AttributeDescriptor> attributes = featureType.getFeatureClass().getAttributeDescriptors();
Object[] values = new Object[featureType.getAttributeCount()];
Object value = null;
String featureId = null;
// Pass 1 - identify the feature identifier
for(int inx = 0; inx < attributes.size(); inx++){
// I am thinking it is probably safer to look this up
// by column name than by position, but if it breaks,
// it is easy enough to change
if (attributes.get(inx).getLocalName().equals("id")) {
value = row.getAttribute(inx);
if(value != null) {
featureId = value.toString();
}
break;
}
}
try {
currentFeature = SimpleFeatureBuilder.build(featureType,values, featureId);
} catch (IllegalAttributeException exc) {
// This shouldn't happen since everything should be nillable
exc.printStackTrace();
}
// Pass 2 - get the attributes, including the geometry
for(int inx = 0; inx < attributes.size(); inx++){
try {
if (attributes.get(inx).getLocalName().equals(AnnotationFeatureType.ANNOTATION_ATTRIBUTE_NAME)) {
try{
//TODO: are we sure this is the intended action? Hard-coding an attribute to "nam"?
currentFeature.setAttribute(inx, "nam");
} catch (IllegalAttributeException exc) {
exc.printStackTrace();
}
continue;
}
column = (VPFColumn) attributes.get(inx);
value = null;
secondFile = getVPFFile(column);
SimpleFeature tempRow = (SimpleFeature) rows.get(secondFile);
if(tempRow != null){
value = tempRow.getAttribute(column.getLocalName());
if (column.isAttemptLookup()){
try {
// Attempt to perform a lookup and conversion
String featureClassName = getVPFFile(column).getFileName();
String intVdtFileName = featureType.getFeatureClass().getDirectoryName().concat(File.separator).concat("int.vdt");
VPFFile intVdtFile = VPFFileFactory.getInstance().getFile(intVdtFileName);
Iterator intVdtIter = intVdtFile.readAllRows().iterator();
while(intVdtIter.hasNext()){
SimpleFeature intVdtRow = (SimpleFeature)intVdtIter.next();
if(intVdtRow.getAttribute("table").toString().trim().equals(featureClassName) &&
(Short.parseShort(intVdtRow.getAttribute("value").toString()) == Short.parseShort(value.toString()) &&
(intVdtRow.getAttribute("attribute").toString().trim().equals(column.getLocalName())))){
value = intVdtRow.getAttribute("description").toString().trim();
break;
}
}
// If there is a problem, forget about mapping and continue
} catch (IOException exc) {
} catch (RuntimeException exc) {
}
}
}
try {
currentFeature.setAttribute(inx, value);
} catch (ArrayIndexOutOfBoundsException exc) {
// TODO Auto-generated catch block
exc.printStackTrace();
} catch (IllegalAttributeException exc) {
// TODO Auto-generated catch block
exc.printStackTrace();
}
} catch (ClassCastException exc2) {
try {
// This is the area geometry case
featureType.getFeatureClass().getGeometryFactory().createGeometry(featureType, currentFeature);
} catch (IllegalAttributeException exc) {
// TODO Auto-generated catch block
exc.printStackTrace();
} catch (SQLException exc) {
// TODO Auto-generated catch block
exc.printStackTrace();
}
}
}
}
/**
* Returns the VPFFile for a particular column.
* It will only find the first match, but that should be okay
* because duplicate columns will cause even bigger problems elsewhere.
* @param column the column to search for
* @return the VPFFile that owns this column
*/
private VPFFile getVPFFile(AttributeDescriptor column){
VPFFile result = null;
VPFFile temp;
Iterator iter = featureType.getFeatureClass().getFileList().iterator();
while(iter.hasNext()){
temp = (VPFFile)iter.next();
if((temp != null) && (temp.indexOf(column.getName()) >= 0)){
result = temp;
break;
}
}
return result;
}
/**
* Need to reset the stream for the next time Resets the iterator by
* resetting the stream.
*
*/
public void reset(){
VPFFile file = (VPFFile) featureType.getFeatureClass()
.getFileList().get(0);
file.reset();
VPFFileFactory.getInstance().reset();
}
}