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;
017:
018: import java.io.IOException;
019: import java.util.Collections;
020: import java.util.HashSet;
021: import java.util.Iterator;
022: import java.util.NoSuchElementException;
023: import java.util.Set;
024: import java.util.logging.Logger;
025:
026: import org.geotools.data.crs.ForceCoordinateSystemFeatureResults;
027: import org.geotools.data.crs.ReprojectFeatureResults;
028: import org.geotools.data.store.EmptyFeatureCollection;
029: import org.geotools.feature.FeatureCollection;
030: import org.geotools.feature.FeatureType;
031: import org.geotools.feature.SchemaException;
032: import org.opengis.filter.Filter;
033: import org.opengis.referencing.FactoryException;
034: import org.opengis.referencing.operation.OperationNotFoundException;
035: import org.opengis.referencing.operation.TransformException;
036:
037: import com.vividsolutions.jts.geom.Envelope;
038:
039: /**
040: * This is a starting point for providing your own FeatureSource implementation.
041: *
042: * <p>
043: * Subclasses must implement:
044: * </p>
045: *
046: * <ul>
047: * <li>
048: * getDataStore()
049: * </li>
050: * <li>
051: * getSchema()
052: * </li>
053: * <li>
054: * addFeatureListener()
055: * </li>
056: * <li>
057: * removeFeatureListener()
058: * </li>
059: * </ul>
060: *
061: * <p>
062: * You may find a FeatureSource implementations that is more specific to your needs - such as
063: * JDBCFeatureSource.
064: * </p>
065: *
066: * <p>
067: * For an example of this class customized for use please see MemoryDataStore.
068: * </p>
069: *
070: * @author Jody Garnett, Refractions Research Inc
071: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/main/src/main/java/org/geotools/data/AbstractFeatureSource.java $
072: */
073: public abstract class AbstractFeatureSource implements FeatureSource {
074: /** The logger for the filter module. */
075: private static final Logger LOGGER = org.geotools.util.logging.Logging
076: .getLogger("org.geotools.data");
077:
078: protected Set hints = Collections.EMPTY_SET;
079:
080: public AbstractFeatureSource() {
081: // just to keep the default constructor around
082: }
083:
084: /**
085: * This constructors allows to set the supported hints
086: * @param hints
087: */
088: public AbstractFeatureSource(Set hints) {
089: this .hints = Collections.unmodifiableSet(new HashSet(hints));
090: }
091:
092: /**
093: * Retrieve the Transaction this FeatureSource is operating against.
094: *
095: * <p>
096: * For a plain FeatureSource that cannot modify this will always be Transaction.AUTO_COMMIT.
097: * </p>
098: *
099: * @return Transacstion FeatureSource is operating against
100: */
101: public Transaction getTransaction() {
102: return Transaction.AUTO_COMMIT;
103: }
104:
105: /**
106: * Provides an interface to for the Results of a Query.
107: *
108: * <p>
109: * Various queries can be made against the results, the most basic being to retrieve Features.
110: * </p>
111: *
112: * @param query
113: *
114: *
115: * @see org.geotools.data.FeatureSource#getFeatures(org.geotools.data.Query)
116: */
117: public FeatureCollection getFeatures(Query query)
118: throws IOException {
119: FeatureType schema = getSchema();
120: String typeName = schema.getTypeName();
121:
122: if (query.getTypeName() == null) { // typeName unspecified we will "any" use a default
123: DefaultQuery defaultQuery = new DefaultQuery(query);
124: defaultQuery.setTypeName(typeName);
125: } else if (!typeName.equals(query.getTypeName())) {
126: return new EmptyFeatureCollection(schema);
127: }
128:
129: FeatureCollection collection = new DefaultFeatureResults(this ,
130: query);
131: if (collection.getDefaultGeometry() == null) {
132: return collection; // no geometry no reprojection needed
133: }
134:
135: if (false) { // we need to have our CRS forced
136: if (query.getCoordinateSystem() != null) {
137: try {
138: collection = new ForceCoordinateSystemFeatureResults(
139: collection, query.getCoordinateSystem());
140: } catch (SchemaException e) {
141: throw new IOException("Could not force CRS "
142: + query.getCoordinateSystem());
143: }
144: }
145: }
146: if (false) { // we need our data reprojected
147: if (query.getCoordinateSystemReproject() != null) {
148: try {
149: collection = new ReprojectFeatureResults(
150: collection, query
151: .getCoordinateSystemReproject());
152: } catch (Exception e) {
153: throw new IOException("Could not reproject to "
154: + query.getCoordinateSystemReproject());
155: }
156: }
157: }
158: return collection;
159: }
160:
161: /**
162: * Retrieve all Feature matching the Filter.
163: *
164: * @param filter Indicates features to retrieve
165: *
166: * @return FeatureResults indicating features matching filter
167: *
168: * @throws IOException If results could not be obtained
169: */
170: public FeatureCollection getFeatures(Filter filter)
171: throws IOException {
172: return getFeatures(new DefaultQuery(getSchema().getTypeName(),
173: filter));
174: }
175:
176: /**
177: * Retrieve all Features.
178: *
179: * @return FeatureResults of all Features in FeatureSource
180: *
181: * @throws IOException If features could not be obtained
182: */
183: public FeatureCollection getFeatures() throws IOException {
184: return getFeatures(Filter.INCLUDE);
185: }
186:
187: /**
188: * Retrieve Bounds of all Features.
189: *
190: * <p>
191: * Currently returns null, consider getFeatures().getBounds() instead.
192: * </p>
193: *
194: * <p>
195: * Subclasses may override this method to perform the appropriate optimization for this result.
196: * </p>
197: *
198: * @return null representing the lack of an optimization
199: *
200: * @throws IOException DOCUMENT ME!
201: */
202: public Envelope getBounds() throws IOException {
203: // return getBounds(Query.ALL); // DZ should this not return just the bounds for this type?
204: return getBounds(getSchema() == null ? Query.ALL
205: : new DefaultQuery(getSchema().getTypeName()));
206: }
207:
208: /**
209: * Retrieve Bounds of Query results.
210: *
211: * <p>
212: * Currently returns null, consider getFeatures( query ).getBounds() instead.
213: * </p>
214: *
215: * <p>
216: * Subclasses may override this method to perform the appropriate optimization for this result.
217: * </p>
218: *
219: * @param query Query we are requesting the bounds of
220: *
221: * @return null representing the lack of an optimization
222: *
223: * @throws IOException DOCUMENT ME!
224: */
225: public Envelope getBounds(Query query) throws IOException {
226: if (query.getFilter() == Filter.EXCLUDE) {
227: return new Envelope();
228: }
229:
230: DataStore dataStore = getDataStore();
231:
232: if ((dataStore == null)
233: || !(dataStore instanceof AbstractDataStore)) {
234: // too expensive
235: return null;
236: } else {
237: // ask the abstract data store
238: return ((AbstractDataStore) dataStore)
239: .getBounds(namedQuery(query));
240: }
241: }
242:
243: /**
244: * Ensure query modified with typeName.
245: * <p>
246: * This method will make copy of the provided query, using
247: * DefaultQuery, if query.getTypeName is not equal to
248: * getSchema().getTypeName().
249: * </p>
250: * @param query Original query
251: * @return Query with getTypeName() equal to getSchema().getTypeName()
252: */
253: protected Query namedQuery(Query query) {
254: String typeName = getSchema().getTypeName();
255: if (query.getTypeName() == null
256: || !query.getTypeName().equals(typeName)) {
257:
258: return new DefaultQuery(typeName, query.getFilter(), query
259: .getMaxFeatures(), query.getPropertyNames(), query
260: .getHandle());
261: }
262: return query;
263: }
264:
265: /**
266: * Retrieve total number of Query results.
267: *
268: * <p>
269: * Currently returns -1, consider getFeatures( query ).getCount() instead.
270: * </p>
271: *
272: * <p>
273: * Subclasses may override this method to perform the appropriate optimization for this result.
274: * </p>
275: *
276: * @param query Query we are requesting the count of
277: *
278: * @return -1 representing the lack of an optimization
279: */
280: public int getCount(Query query) throws IOException {
281: if (query.getFilter() == Filter.EXCLUDE) {
282: return 0;
283: }
284:
285: DataStore dataStore = getDataStore();
286: if ((dataStore == null)
287: || !(dataStore instanceof AbstractDataStore)) {
288: // too expensive
289: return -1;
290: } else {
291: // ask the abstract data store
292: Transaction t = getTransaction();
293: //State state = t.getState(dataStore);
294: int delta = 0;
295: if (t != Transaction.AUTO_COMMIT) {
296: if (!(t.getState(dataStore) instanceof TransactionStateDiff)) {
297: //we cannot proceed; abort!
298: return -1;
299: }
300: Diff diff = ((AbstractDataStore) dataStore).state(t)
301: .diff(namedQuery(query).getTypeName());
302: synchronized (diff) {
303: Iterator it = diff.added.values().iterator();
304: while (it.hasNext()) {
305: Object feature = it.next();
306: if (query.getFilter().evaluate(feature))
307: delta++;
308: }
309:
310: it = diff.modified2.values().iterator();
311: while (it.hasNext()) {
312: Object feature = it.next();
313:
314: if (feature == TransactionStateDiff.NULL) {
315: delta--;
316: }
317: }
318: }
319: }
320: return ((AbstractDataStore) dataStore)
321: .getCount(namedQuery(query))
322: + delta;
323: }
324: }
325:
326: /**
327: * By default, no Hints are supported
328: */
329: public Set getSupportedHints() {
330: return hints;
331: }
332: }
|