001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2003-2006, GeoTools Project Managment Committee (PMC)
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation;
009: * version 2.1 of the License.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: */
016: package org.geotools.data.jdbc;
017:
018: import java.io.IOException;
019: import java.sql.Connection;
020: import java.sql.ResultSet;
021: import java.sql.SQLException;
022: import java.sql.Statement;
023: import java.util.Collections;
024: import java.util.NoSuchElementException;
025: import java.util.logging.Logger;
026:
027: import org.geotools.data.AttributeReader;
028: import org.geotools.data.AttributeWriter;
029: import org.geotools.data.DataSourceException;
030: import org.geotools.data.FeatureListenerManager;
031: import org.geotools.data.Transaction;
032: import org.geotools.data.jdbc.attributeio.AttributeIO;
033: import org.geotools.data.jdbc.fidmapper.FIDMapper;
034: import org.geotools.factory.Hints;
035: import org.geotools.feature.AttributeType;
036: import org.geotools.feature.FeatureType;
037: import org.geotools.feature.GeometryAttributeType;
038:
039: import com.vividsolutions.jts.geom.Envelope;
040:
041: /**
042: * QueryData holds the ResultSet obtained from the sql query and has the following responsibilities:
043: *
044: * <ul>
045: * <li> acts as the attribute reader by using the AttributeIO objects </li>
046: * <li> acts as the attribute writer by using the AttributeIO objects </li>
047: * <li> manages the resulset, statement and transaction and closes them cleanly if needed </li>
048: * <li> provides methods for creating a new row, as well as inserting new ones, that are used by the
049: * JDBCFeatureWriter </li>
050: * <li> holds the FIDMapper for feature reader and writer to use when building new features </li>
051: * </ul>
052: *
053: *
054: * @author aaime
055: * @source $URL:
056: * http://svn.geotools.org/geotools/trunk/gt/modules/library/jdbc/src/main/java/org/geotools/data/jdbc/QueryData.java $
057: */
058: public class QueryData implements AttributeReader, AttributeWriter {
059: /** The logger for the data module. */
060: protected static final Logger LOGGER = org.geotools.util.logging.Logging
061: .getLogger("org.geotools.data.jdbc");
062:
063: protected Object[] fidAttributes;
064:
065: protected FeatureTypeInfo featureTypeInfo;
066:
067: protected ResultSet resultSet;
068:
069: protected Connection connection;
070:
071: protected Transaction transaction;
072:
073: protected Statement statement;
074:
075: protected FIDMapper mapper;
076:
077: protected AttributeIO[] attributeHandlers;
078:
079: protected int baseIndex;
080:
081: boolean hasNextCalled = false;
082:
083: boolean lastNext;
084:
085: protected FeatureListenerManager listenerManager;
086:
087: protected Hints hints;
088:
089: /**
090: * Creates a new QueryData object.
091: *
092: * @param featureTypeInfo
093: * @param parentDataStore
094: * @param connection
095: * @param statement
096: * @param resultSet
097: * @param transaction
098: */
099: public QueryData(FeatureTypeInfo featureTypeInfo,
100: JDBC1DataStore parentDataStore, Connection connection,
101: Statement statement, ResultSet resultSet,
102: Transaction transaction) throws IOException {
103: this (featureTypeInfo, parentDataStore, connection, statement,
104: resultSet, transaction, null);
105: }
106:
107: /**
108: * Creates a new QueryData object.
109: *
110: * @param featureTypeInfo
111: * @param parentDataStore
112: * @param connection
113: * @param statement
114: * @param resultSet
115: * @param transaction
116: */
117: public QueryData(FeatureTypeInfo featureTypeInfo,
118: JDBC1DataStore parentDataStore, Connection connection,
119: Statement statement, ResultSet resultSet,
120: Transaction transaction, Hints hints) throws IOException {
121: this .featureTypeInfo = featureTypeInfo;
122: this .mapper = featureTypeInfo.getFIDMapper();
123: this .baseIndex = mapper.getColumnCount() + 1;
124: this .resultSet = resultSet;
125: this .statement = statement;
126: this .connection = connection;
127: this .transaction = transaction;
128: this .fidAttributes = new Object[mapper.getColumnCount()];
129: this .listenerManager = parentDataStore.listenerManager;
130: this .hints = hints;
131:
132: AttributeType[] attributeTypes = featureTypeInfo.getSchema()
133: .getAttributeTypes();
134:
135: this .attributeHandlers = new AttributeIO[attributeTypes.length];
136:
137: for (int i = 0; i < attributeHandlers.length; i++) {
138: if (attributeTypes[i] instanceof GeometryAttributeType) {
139: attributeHandlers[i] = parentDataStore
140: .getGeometryAttributeIO(attributeTypes[i], this );
141: } else {
142: attributeHandlers[i] = parentDataStore
143: .getAttributeIO(attributeTypes[i]);
144: }
145: }
146: }
147:
148: /**
149: *
150: * @see org.geotools.data.AttributeWriter#getAttributeCount()
151: */
152: public int getAttributeCount() {
153: return attributeHandlers.length;
154: }
155:
156: /**
157: * Returns the AttributeIO objects used to parse and encode the column values stored in the
158: * database
159: *
160: */
161: public AttributeIO[] getAttributeHandlers() {
162: return attributeHandlers;
163: }
164:
165: /**
166: * DOCUMENT ME!
167: *
168: */
169: public Connection getConnection() {
170: return connection;
171: }
172:
173: /**
174: * Returns the FID mapper to be used when reading/writing features
175: *
176: */
177: public FIDMapper getMapper() {
178: return mapper;
179: }
180:
181: /**
182: * Returns the current transation
183: *
184: */
185: public Transaction getTransaction() {
186: return transaction;
187: }
188:
189: /**
190: *
191: * @see org.geotools.data.AttributeWriter#close()
192: */
193: public void close() {
194: close(null);
195: }
196:
197: /**
198: * Closes the JDBC objects associated to the queryData and reports the sqlException on the LOG
199: *
200: * @param sqlException
201: */
202: public void close(SQLException sqlException) {
203: JDBCUtils.close(resultSet);
204: JDBCUtils.close(statement);
205: JDBCUtils.close(connection, transaction, sqlException);
206: resultSet = null;
207: statement = null;
208: connection = null;
209: transaction = null;
210: }
211:
212: /**
213: * @see org.geotools.data.AttributeReader#read(int)
214: */
215: public Object read(int index) throws IOException,
216: ArrayIndexOutOfBoundsException {
217: AttributeIO reader = attributeHandlers[index];
218:
219: return reader.read(resultSet, index + baseIndex);
220: }
221:
222: /**
223: * @see org.geotools.data.AttributeWriter#write(int, java.lang.Object)
224: */
225: public void write(int i, Object currAtt) throws IOException {
226: AttributeIO attributeHandler = attributeHandlers[i];
227: attributeHandler.write(resultSet, baseIndex + i, currAtt);
228: }
229:
230: /**
231: * Reads a column of the primary key
232: *
233: * @param index
234: * the column index among the primary key columns (as reported by the FIDMapper)
235: *
236: * @return fid value
237: *
238: * @throws IOException
239: * @throws DataSourceException
240: */
241: public Object readFidColumn(int index) throws IOException {
242: try {
243: return resultSet.getString(index + 1); // we turn it into a string anyhow...
244: } catch (SQLException e) {
245: throw new DataSourceException("Error reading fid column "
246: + index, e);
247: }
248: }
249:
250: /**
251: * Writes a column of the primary key
252: *
253: * @param index
254: * the FID column index among the primary key columns (as reported by the FIDMapper)
255: * @param value
256: * the column value
257: *
258: * @throws IOException
259: * @throws DataSourceException
260: */
261: public void writeFidColumn(int index, Object value)
262: throws IOException {
263: try {
264: if (value == null) {
265: resultSet.updateNull(index + 1);
266: } else {
267: resultSet.updateObject(index + 1, value);
268: }
269: } catch (SQLException e) {
270: throw new DataSourceException("Error writing fid column "
271: + index, e);
272: }
273: }
274:
275: /**
276: * Returns the current feature type
277: *
278: */
279: public FeatureType getFeatureType() {
280: return featureTypeInfo.getSchema();
281: }
282:
283: /**
284: * Moves the result set to the insert row. Must be called before writing the attribute values
285: * for the new Feature
286: *
287: * @throws SQLException
288: */
289: public void startInsert() throws SQLException {
290: resultSet.moveToInsertRow();
291: }
292:
293: /**
294: * Deletes the current record in the result set
295: *
296: * @throws SQLException
297: */
298: public void deleteCurrentRow() throws SQLException {
299: this .resultSet.deleteRow();
300: }
301:
302: /**
303: * Update the current record
304: *
305: * @throws SQLException
306: */
307: public void updateRow() throws SQLException {
308: resultSet.updateRow();
309: }
310:
311: /**
312: * Insert a record in the current result set
313: *
314: * @throws SQLException
315: */
316: public void doInsert() throws SQLException {
317: resultSet.insertRow();
318: }
319:
320: /**
321: * DOCUMENT ME!
322: *
323: */
324: public FeatureTypeInfo getFeatureTypeInfo() {
325: return featureTypeInfo;
326: }
327:
328: /**
329: * @return true if the QueryData has been closed, false otherwise
330: */
331: public boolean isClosed() {
332: return resultSet == null;
333: }
334:
335: /**
336: *
337: * @see org.geotools.data.AttributeWriter#next()
338: */
339: public void next() throws IOException {
340: if ((!hasNextCalled && !hasNext()) || !lastNext) {
341: throw new NoSuchElementException(
342: "No feature to read, hasNext did return false");
343: }
344:
345: hasNextCalled = false;
346: }
347:
348: /**
349: *
350: * @see org.geotools.data.AttributeWriter#hasNext()
351: */
352: public boolean hasNext() throws IOException {
353: try {
354: if (!hasNextCalled) {
355: hasNextCalled = true;
356: lastNext = resultSet.next();
357: }
358: } catch (Exception e) {
359: throw new DataSourceException(
360: "Problem moving on to the next attribute", e);
361: }
362:
363: return lastNext;
364: }
365:
366: /**
367: * @see org.geotools.data.AttributeReader#getAttributeType(int)
368: */
369: public AttributeType getAttributeType(int index)
370: throws ArrayIndexOutOfBoundsException {
371: return featureTypeInfo.getSchema().getAttributeType(index);
372: }
373:
374: public FeatureListenerManager getListenerManager() {
375: return listenerManager;
376: }
377:
378: /** Call after deleteCurrentRow() */
379: public void fireChangeRemoved(Envelope bounds, boolean isCommit) {
380: String typeName = featureTypeInfo.getFeatureTypeName();
381: listenerManager.fireFeaturesRemoved(typeName, transaction,
382: bounds, isCommit);
383: }
384:
385: /** Call after updateRow */
386: public void fireFeaturesChanged(Envelope bounds, boolean isCommit) {
387: String typeName = featureTypeInfo.getFeatureTypeName();
388: listenerManager.fireFeaturesChanged(typeName, transaction,
389: bounds, isCommit);
390: }
391:
392: /** Call after doUpdate */
393: public void fireFeaturesAdded(Envelope bounds, boolean isCommit) {
394: String typeName = featureTypeInfo.getFeatureTypeName();
395: listenerManager.fireFeaturesAdded(typeName, transaction,
396: bounds, isCommit);
397: }
398:
399: protected void finalize() throws Throwable {
400: if (!isClosed()) {
401: LOGGER
402: .severe("There's code leaving readers, writers or iterators unclosed "
403: + "(you got an unclosed QueryData object, which is usually "
404: + "held by a reader or a writer).\n"
405: + "Call reader/writer.close() or FeatureCollection.close(iterator) "
406: + "after using them to ensure "
407: + "they do not hold state such as JDCB connections.\n"
408: + "QueryData was open against feature type: "
409: + featureTypeInfo.getFeatureTypeName());
410: close();
411: }
412: super .finalize();
413: }
414:
415: public Hints getHints() {
416: if (hints == null) {
417: hints = new Hints(Collections.EMPTY_MAP);
418: }
419: return hints;
420: }
421: }
|