/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2003-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.jdbc;
import java.io.IOException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Collections;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.logging.Logger;
import org.geotools.data.AttributeReader;
import org.geotools.data.AttributeWriter;
import org.geotools.data.DataSourceException;
import org.geotools.data.FeatureListenerManager;
import org.geotools.data.Transaction;
import org.geotools.data.jdbc.attributeio.AttributeIO;
import org.geotools.data.jdbc.fidmapper.FIDMapper;
import org.geotools.factory.Hints;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.AttributeDescriptor;
import org.opengis.feature.type.GeometryDescriptor;
/**
* QueryData holds the ResultSet obtained from the sql query and has the following responsibilities:
*
* <ul>
* <li> acts as the attribute reader by using the AttributeIO objects </li>
* <li> acts as the attribute writer by using the AttributeIO objects </li>
* <li> manages the resulset, statement and transaction and closes them cleanly if needed </li>
* <li> provides methods for creating a new row, as well as inserting new ones, that are used by the
* JDBCFeatureWriter </li>
* <li> holds the FIDMapper for feature reader and writer to use when building new features </li>
* </ul>
*
*
* @author aaime
*
* @source $URL$
* @deprecated scheduled for removal in 2.7, use classes in org.geotools.jdbc
*/
public class QueryData implements AttributeReader, AttributeWriter {
/** The logger for the data module. */
protected static final Logger LOGGER = org.geotools.util.logging.Logging.getLogger("org.geotools.data.jdbc");
protected Object[] fidAttributes;
protected FeatureTypeInfo featureTypeInfo;
protected ResultSet resultSet;
protected Connection connection;
protected Transaction transaction;
protected Statement statement;
protected FIDMapper mapper;
protected AttributeIO[] attributeHandlers;
protected int baseIndex;
boolean hasNextCalled = false;
boolean lastNext;
protected FeatureListenerManager listenerManager;
protected Hints hints;
/**
* Creates a new QueryData object.
*
* @param featureTypeInfo
* @param parentDataStore
* @param connection
* @param statement
* @param resultSet
* @param transaction
*/
public QueryData(FeatureTypeInfo featureTypeInfo, JDBC1DataStore parentDataStore,
Connection connection, Statement statement, ResultSet resultSet, Transaction transaction)
throws IOException {
this(featureTypeInfo, parentDataStore, connection, statement, resultSet, transaction, null);
}
/**
* Creates a new QueryData object.
*
* @param featureTypeInfo
* @param parentDataStore
* @param connection
* @param statement
* @param resultSet
* @param transaction
*/
public QueryData(FeatureTypeInfo featureTypeInfo, JDBC1DataStore parentDataStore,
Connection connection, Statement statement, ResultSet resultSet, Transaction transaction, Hints hints)
throws IOException {
this.featureTypeInfo = featureTypeInfo;
this.mapper = featureTypeInfo.getFIDMapper();
this.baseIndex = mapper.getColumnCount() + 1;
this.resultSet = resultSet;
this.statement = statement;
this.connection = connection;
this.transaction = transaction;
this.fidAttributes = new Object[mapper.getColumnCount()];
this.listenerManager = parentDataStore.listenerManager;
this.hints = hints;
List<AttributeDescriptor> attributeTypes = featureTypeInfo.getSchema().getAttributeDescriptors();
this.attributeHandlers = new AttributeIO[attributeTypes.size()];
for (int i = 0; i < attributeHandlers.length; i++) {
if (attributeTypes.get(i) instanceof GeometryDescriptor) {
attributeHandlers[i] = parentDataStore.getGeometryAttributeIO(attributeTypes.get(i),
this);
} else {
attributeHandlers[i] = parentDataStore.getAttributeIO(attributeTypes.get(i));
}
}
}
/**
*
* @see org.geotools.data.AttributeWriter#getAttributeCount()
*/
public int getAttributeCount() {
return attributeHandlers.length;
}
/**
* Returns the AttributeIO objects used to parse and encode the column values stored in the
* database
*
*/
public AttributeIO[] getAttributeHandlers() {
return attributeHandlers;
}
/**
* DOCUMENT ME!
*
*/
public Connection getConnection() {
return connection;
}
/**
* Returns the FID mapper to be used when reading/writing features
*
*/
public FIDMapper getMapper() {
return mapper;
}
/**
* Returns the current transation
*
*/
public Transaction getTransaction() {
return transaction;
}
/**
*
* @see org.geotools.data.AttributeWriter#close()
*/
public void close() {
close(null);
}
/**
* Closes the JDBC objects associated to the queryData and reports the sqlException on the LOG
*
* @param sqlException
*/
public void close(SQLException sqlException) {
JDBCUtils.close(resultSet);
JDBCUtils.close(statement);
JDBCUtils.close(connection, transaction, sqlException);
resultSet = null;
statement = null;
connection = null;
transaction = null;
}
/**
* @see org.geotools.data.AttributeReader#read(int)
*/
public Object read(int index) throws IOException, ArrayIndexOutOfBoundsException {
AttributeIO reader = attributeHandlers[index];
return reader.read(resultSet, index + baseIndex);
}
/**
* @see org.geotools.data.AttributeWriter#write(int, java.lang.Object)
*/
public void write(int i, Object currAtt) throws IOException {
AttributeIO attributeHandler = attributeHandlers[i];
attributeHandler.write(resultSet, baseIndex + i, currAtt);
}
/**
* Reads a column of the primary key
*
* @param index
* the column index among the primary key columns (as reported by the FIDMapper)
*
* @return fid value
*
* @throws IOException
* @throws DataSourceException
*/
public Object readFidColumn(int index) throws IOException {
try {
return resultSet.getString(index + 1); // we turn it into a string anyhow...
} catch (SQLException e) {
throw new DataSourceException("Error reading fid column " + index, e);
}
}
/**
* Writes a column of the primary key
*
* @param index
* the FID column index among the primary key columns (as reported by the FIDMapper)
* @param value
* the column value
*
* @throws IOException
* @throws DataSourceException
*/
public void writeFidColumn(int index, Object value) throws IOException {
try {
if (value == null) {
resultSet.updateNull(index + 1);
} else {
resultSet.updateObject(index + 1, value);
}
} catch (SQLException e) {
throw new DataSourceException("Error writing fid column " + index, e);
}
}
/**
* Returns the current feature type
*
*/
public SimpleFeatureType getFeatureType() {
return featureTypeInfo.getSchema();
}
/**
* Moves the result set to the insert row. Must be called before writing the attribute values
* for the new Feature
*
* @throws SQLException
*/
public void startInsert() throws SQLException {
resultSet.moveToInsertRow();
}
/**
* Deletes the current record in the result set
*
* @throws SQLException
*/
public void deleteCurrentRow() throws SQLException {
this.resultSet.deleteRow();
}
/**
* Update the current record
*
* @throws SQLException
*/
public void updateRow() throws SQLException {
resultSet.updateRow();
}
/**
* Insert a record in the current result set
*
* @throws SQLException
*/
public void doInsert() throws SQLException {
resultSet.insertRow();
}
/**
* DOCUMENT ME!
*
*/
public FeatureTypeInfo getFeatureTypeInfo() {
return featureTypeInfo;
}
/**
* @return true if the QueryData has been closed, false otherwise
*/
public boolean isClosed() {
return resultSet == null;
}
/**
*
* @see org.geotools.data.AttributeWriter#next()
*/
public void next() throws IOException {
if ((!hasNextCalled && !hasNext()) || !lastNext) {
throw new NoSuchElementException("No feature to read, hasNext did return false");
}
hasNextCalled = false;
}
/**
*
* @see org.geotools.data.AttributeWriter#hasNext()
*/
public boolean hasNext() throws IOException {
try {
if (!hasNextCalled) {
hasNextCalled = true;
lastNext = resultSet.next();
}
} catch (Exception e) {
throw new DataSourceException("Problem moving on to the next attribute", e);
}
return lastNext;
}
/**
* @see org.geotools.data.AttributeReader#getAttributeType(int)
*/
public AttributeDescriptor getAttributeType(int index) throws ArrayIndexOutOfBoundsException {
return featureTypeInfo.getSchema().getDescriptor(index);
}
public FeatureListenerManager getListenerManager() {
return listenerManager;
}
/** Call after deleteCurrentRow() */
public void fireChangeRemoved(ReferencedEnvelope bounds, boolean isCommit) {
String typeName = featureTypeInfo.getFeatureTypeName();
listenerManager.fireFeaturesRemoved(typeName, transaction, bounds, isCommit);
}
/** Call after updateRow */
public void fireFeaturesChanged(ReferencedEnvelope bounds, boolean isCommit) {
String typeName = featureTypeInfo.getFeatureTypeName();
listenerManager.fireFeaturesChanged(typeName, transaction, bounds, isCommit);
}
/** Call after doUpdate */
public void fireFeaturesAdded(ReferencedEnvelope bounds, boolean isCommit) {
String typeName = featureTypeInfo.getFeatureTypeName();
listenerManager.fireFeaturesAdded(typeName, transaction, bounds, isCommit);
}
protected void finalize() throws Throwable {
if (!isClosed()) {
LOGGER.severe("There's code leaving readers, writers or iterators unclosed "
+ "(you got an unclosed QueryData object, which is usually "
+ "held by a reader or a writer).\n"
+ "Call reader/writer.close() or FeatureCollection.close(iterator) "
+ "after using them to ensure "
+ "they do not hold state such as JDCB connections.\n"
+ "QueryData was open against feature type: "
+ featureTypeInfo.getFeatureTypeName());
close();
}
super.finalize();
}
public Hints getHints() {
if(hints == null) {
hints = new Hints(Collections.EMPTY_MAP);
}
return hints;
}
}