001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 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.store;
017:
018: import java.io.IOException;
019: import java.util.HashMap;
020: import java.util.List;
021: import java.util.Map;
022: import java.util.logging.Logger;
023:
024: import com.vividsolutions.jts.geom.GeometryFactory;
025: import org.opengis.feature.simple.SimpleFeatureFactory;
026: import org.opengis.feature.simple.SimpleTypeFactory;
027: import org.opengis.feature.type.TypeName;
028: import org.opengis.filter.Filter;
029: import org.opengis.filter.FilterFactory;
030: import org.geotools.data.DataStore;
031: import org.geotools.data.FeatureReader;
032: import org.geotools.data.FeatureSource;
033: import org.geotools.data.FeatureStore;
034: import org.geotools.data.FeatureWriter;
035: import org.geotools.data.LockingManager;
036: import org.geotools.data.Query;
037: import org.geotools.data.Transaction;
038: import org.geotools.data.collection.DelegateFeatureReader;
039: import org.geotools.feature.FeatureCollection;
040: import org.geotools.feature.FeatureType;
041: import org.geotools.feature.SchemaException;
042:
043: /**
044: * Abstract implementation of DataStore.
045: * <p>
046: * Subclasses must implement the following methods.
047: * <ul>
048: * <li>{@link #createTypeNames()}
049: * <li>{@link #createFeatureSource(ContentEntry)}
050: * </ul>
051: *
052: * @author Jody Garnett, Refractions Research Inc.
053: * @author Justin Deoliveira, The Open Planning Project
054: */
055: public abstract class ContentDataStore implements DataStore {
056: /**
057: * logging instance
058: */
059: static final Logger LOGGER = org.geotools.util.logging.Logging
060: .getLogger("org.geotools.data");
061:
062: /**
063: * Map<TypeName,ContentEntry> one for each kind of content served up.
064: */
065: final Map entries;
066:
067: /**
068: * Factory used to create feature types
069: */
070: protected SimpleTypeFactory typeFactory;
071:
072: /**
073: * Factory used to create features
074: */
075: protected SimpleFeatureFactory featureFactory;
076:
077: /**
078: * Factory used to create filters
079: */
080: protected FilterFactory filterFactory;
081:
082: /**
083: * Factory used to create geometries
084: */
085: protected GeometryFactory geometryFactory;
086:
087: /**
088: * Application namespace uri of the datastore
089: */
090: protected String namespaceURI;
091:
092: public ContentDataStore() {
093: this .entries = new HashMap();
094: }
095:
096: //
097: // Property accessors
098: //
099: public void setTypeFactory(SimpleTypeFactory typeFactory) {
100: this .typeFactory = typeFactory;
101: }
102:
103: public SimpleTypeFactory getTypeFactory() {
104: return typeFactory;
105: }
106:
107: public void setFeatureFactory(SimpleFeatureFactory featureFactory) {
108: this .featureFactory = featureFactory;
109: }
110:
111: public SimpleFeatureFactory getFeatureFactory() {
112: return featureFactory;
113: }
114:
115: public void setFilterFactory(FilterFactory filterFactory) {
116: this .filterFactory = filterFactory;
117: }
118:
119: public FilterFactory getFilterFactory() {
120: return filterFactory;
121: }
122:
123: public GeometryFactory getGeometryFactory() {
124: return geometryFactory;
125: }
126:
127: public void setGeometryFactory(GeometryFactory geometryFactory) {
128: this .geometryFactory = geometryFactory;
129: }
130:
131: public void setNamespaceURI(String namespaceURI) {
132: this .namespaceURI = namespaceURI;
133: }
134:
135: public String getNamespaceURI() {
136: return namespaceURI;
137: }
138:
139: //
140: // DataStore API
141: //
142:
143: /**
144: * This method delegates to {@link #createTypeNames()}.
145: *
146: * @see DataStore#getTypeNames()
147: */
148: public final String[] getTypeNames() throws IOException {
149: List typeNames = createTypeNames();
150: String[] names = new String[typeNames.size()];
151:
152: for (int i = 0; i < typeNames.size(); i++) {
153: TypeName typeName = (TypeName) typeNames.get(i);
154: names[i] = typeName.getLocalPart();
155: }
156:
157: return names;
158: }
159:
160: /**
161: * Calls through to <code>getFeatureSource(typeName).getSchema()</code>
162: *
163: * @see DataStore#getSchema(String)
164: */
165: public final FeatureType getSchema(String typeName)
166: throws IOException {
167: return getFeatureSource(typeName).getSchema();
168: }
169:
170: /**
171: * Delegates to {@link #getFeatureSource(TypeName, Transaction)}.
172: *
173: * @see DataStore#getFeatureSource(String)
174: */
175: public final FeatureSource getFeatureSource(String typeName)
176: throws IOException {
177: return getFeatureSource(name(typeName), Transaction.AUTO_COMMIT);
178: }
179:
180: /**
181: * Returns a feature source for a feature type and transaction.
182: * <p>
183: * The resulting feature source is cached in the state of the entry for
184: * the type.
185: * </p>
186: *
187: * @param typeName The entry name.
188: * @param tx A transaction.
189: *
190: * @return The feature source for the name and transaction.
191: *
192: */
193: public final FeatureSource getFeatureSource(TypeName typeName,
194: Transaction tx) throws IOException {
195:
196: ContentEntry entry = ensureEntry(typeName);
197:
198: ContentFeatureSource featureSource = createFeatureSource(entry);
199: featureSource.setTransaction(tx);
200:
201: return featureSource;
202: }
203:
204: /**
205: * Delegates to {@link #query(Query, Transaction)} and wraps the result in
206: * a {@link DelegateFeatureReader}.
207: *
208: * @see DataStore#getFeatureReader(Query, Transaction)
209: */
210: public final FeatureReader getFeatureReader(Query query,
211: Transaction transaction) throws IOException {
212: FeatureCollection collection = query(query, transaction);
213:
214: return new DelegateFeatureReader(collection.getSchema(),
215: collection.features());
216: }
217:
218: /**
219: * The default implementation of this method throws a
220: * {@link UnsupportedOperationException}, subclasses should implement to
221: * support schema creation.
222: *
223: * @see DataStore#createSchema(FeatureType)
224: */
225: public void createSchema(FeatureType featureType)
226: throws IOException {
227: throw new UnsupportedOperationException();
228: }
229:
230: public final FeatureWriter getFeatureWriter(String typeName,
231: Filter filter, Transaction transaction) throws IOException {
232: return null;
233: }
234:
235: public final FeatureWriter getFeatureWriterAppend(String typeName,
236: Transaction transaction) throws IOException {
237: return null;
238: }
239:
240: public final LockingManager getLockingManager() {
241: return null;
242: }
243:
244: public final FeatureSource getView(Query query) throws IOException,
245: SchemaException {
246: return null;
247: }
248:
249: public final void updateSchema(String typeName,
250: FeatureType featureType) throws IOException {
251: }
252:
253: public final FeatureWriter getFeatureWriter(String typeName,
254: Transaction transaction) throws IOException {
255: return null;
256: }
257:
258: //
259: // Internal API
260: //
261: /**
262: * Returns the logger for the datastore.
263: * <p>
264: * Subclasses should override to provide a differnt logging instance. This
265: * implementation uses the logger "org.geotools.data".
266: * </p>
267: */
268: public Logger getLogger() {
269: return LOGGER;
270: }
271:
272: /**
273: * Creates a set of qualified names corresponding to the types that the
274: * datastore provides.
275: * <p>
276: * Namespaces may be left <code>null</code> for data stores which do not
277: * support namespace qualified type names.
278: * </p>
279: *
280: * @return A list of {@link TypeName}.
281: *
282: * @throws IOException Any errors occuring connecting to data.
283: */
284: protected abstract List /*<TypeName>*/createTypeNames()
285: throws IOException;
286:
287: /**
288: * Instantiates new feature source for the entry.
289: * <p>
290: * Subclasses should override this method to return a specific subclass of
291: * {@link ContentFeatureSource}.
292: * </p>
293: * @param entry The entry.
294: *
295: * @return An new instance of {@link ContentFeatureSource} for the entry.
296: */
297: protected abstract ContentFeatureSource createFeatureSource(
298: ContentEntry entry) throws IOException;
299:
300: /**
301: * Instantiates a new conent state for the entry.
302: * <p>
303: * Subclasses may override this method to return a specific subclass of
304: * {@link ContentState}.
305: * </p>
306: * @param entry The entry.
307: *
308: * @return A new instance of {@link ContentState} for the entry.
309: *
310: */
311: protected ContentState createContentState(ContentEntry entry) {
312: return new ContentState(entry);
313: }
314:
315: /**
316: * Helper method to wrap a non-qualified name.
317: */
318: final protected TypeName name(String typeName) {
319: return new org.geotools.feature.type.TypeName(typeName);
320: }
321:
322: /**
323: * Helper method to look up an entry in the datastore.
324: * <p>
325: * This method will create a new instance of {@link ContentEntry} if one
326: * does not exist.
327: * </p>
328: * <p>
329: * In the event that the name does not map to an entry
330: * and one cannot be created <code>null</code> will be returned. Note that
331: * {@link #ensureEntry(TypeName)} will throw an exception in this case.
332: * </p>
333: *
334: * @param The name of the entry.
335: *
336: * @return The entry, or <code>null</code> if it does not exist.
337: */
338: final protected ContentEntry entry(TypeName name)
339: throws IOException {
340: ContentEntry entry = null;
341:
342: //do we already know about the entry
343: if (!entries.containsKey(name)) {
344: //is this type available?
345: List typeNames = createTypeNames();
346:
347: if (typeNames.contains(name)) {
348: //yes, create an entry for it
349: synchronized (this ) {
350: if (!entries.containsKey(name)) {
351: entry = new ContentEntry(this , name);
352: entries.put(name, entry);
353: }
354: }
355:
356: entry = (ContentEntry) entries.get(name);
357: }
358: }
359:
360: return (ContentEntry) entries.get(name);
361: }
362:
363: /**
364: * Helper method to look up an entry in the datastore which throws an
365: * {@link IOException} in the event that the entry does not exist.
366: *
367: * @param name The name of the entry.
368: *
369: * @return The entry.
370: *
371: * @throws IOException If hte entry does not exist, or if there was an error
372: * looking it up.
373: */
374: final protected ContentEntry ensureEntry(TypeName name)
375: throws IOException {
376: ContentEntry entry = entry(name);
377:
378: if (entry == null) {
379: throw new IOException("Schema '" + name
380: + "' does not exist.");
381: }
382:
383: return entry;
384: }
385:
386: /**
387: * Helper method for returning the feature collection for a
388: * query / transaction pair.
389: * <p>
390: * The implementation of this method delegates to
391: * {@link ContentFeatureSource#getFeatures(Query)}
392: * </p>
393: *
394: * @param query The query to make against the datastore.
395: * @param transaction A transaction
396: *
397: * @return A FeatureCollection matching the query.
398: *
399: * @throws IOException Any errors that occur when interacting with the data.
400: */
401: final protected FeatureCollection query(Query query,
402: Transaction transaction) throws IOException {
403: FeatureSource source = getFeatureSource(query.getTypeName());
404:
405: //TODO: transaction should be moved up to FeatureSource
406: if (source instanceof FeatureStore) {
407: ((FeatureStore) source).setTransaction(transaction);
408: }
409:
410: return source.getFeatures(query);
411: }
412: }
|