001: /* Copyright (c) 2001 - 2007 TOPP - www.openplans.org. All rights reserved.
002: * This code is licensed under the GPL 2.0 license, availible at the root
003: * application directory.
004: */
005: package org.geoserver.feature.retype;
006:
007: import java.io.IOException;
008: import java.util.HashMap;
009: import java.util.Map;
010:
011: import org.geoserver.feature.RetypingFeatureCollection;
012: import org.geotools.data.DataSourceException;
013: import org.geotools.data.DataStore;
014: import org.geotools.data.DefaultQuery;
015: import org.geotools.data.FeatureLocking;
016: import org.geotools.data.FeatureReader;
017: import org.geotools.data.FeatureSource;
018: import org.geotools.data.FeatureStore;
019: import org.geotools.data.FeatureWriter;
020: import org.geotools.data.LockingManager;
021: import org.geotools.data.Query;
022: import org.geotools.data.Transaction;
023: import org.geotools.feature.FeatureType;
024: import org.geotools.feature.FeatureTypeBuilder;
025: import org.geotools.feature.SchemaException;
026: import org.opengis.filter.Filter;
027:
028: /**
029: * A simple data store that can be used to rename feature types (despite the name, the only retyping
030: * considered is the name change, thought it would not be that hard to extend it so that it
031: * could shave off some attribute too)
032: */
033: public class RetypingDataStore implements DataStore {
034: DataStore wrapped;
035:
036: Map forwardMap = new HashMap();
037:
038: Map backwardsMap = new HashMap();
039:
040: public RetypingDataStore(DataStore wrapped) throws IOException {
041: init(wrapped);
042: }
043:
044: protected RetypingDataStore() {
045:
046: }
047:
048: protected void init(DataStore wrapped) throws IOException {
049: this .wrapped = wrapped;
050: // force update of type mapping maps
051: getTypeNames();
052: }
053:
054: public void createSchema(FeatureType featureType)
055: throws IOException {
056: throw new UnsupportedOperationException(
057: "GeoServer does not support schema creation at the moment");
058: }
059:
060: public void updateSchema(String typeName, FeatureType featureType)
061: throws IOException {
062: throw new UnsupportedOperationException(
063: "GeoServer does not support schema updates at the moment");
064: }
065:
066: public FeatureWriter getFeatureWriter(String typeName,
067: Filter filter, Transaction transaction) throws IOException {
068: FeatureTypeMap map = getTypeMapBackwards(typeName);
069: updateMap(map, false);
070: FeatureWriter writer = wrapped.getFeatureWriter(map
071: .getOriginalName(), filter, transaction);
072: if (map.isUnchanged())
073: return writer;
074: return new RetypingFeatureCollection.RetypingFeatureWriter(
075: writer, map.getFeatureType());
076: }
077:
078: public FeatureWriter getFeatureWriter(String typeName,
079: Transaction transaction) throws IOException {
080: FeatureTypeMap map = getTypeMapBackwards(typeName);
081: updateMap(map, false);
082: FeatureWriter writer = wrapped.getFeatureWriter(map
083: .getOriginalName(), transaction);
084: if (map.isUnchanged())
085: return writer;
086: return new RetypingFeatureCollection.RetypingFeatureWriter(
087: writer, map.getFeatureType());
088: }
089:
090: public FeatureWriter getFeatureWriterAppend(String typeName,
091: Transaction transaction) throws IOException {
092: FeatureTypeMap map = getTypeMapBackwards(typeName);
093: updateMap(map, false);
094: FeatureWriter writer = wrapped.getFeatureWriterAppend(map
095: .getOriginalName(), transaction);
096: if (map.isUnchanged())
097: return writer;
098: return new RetypingFeatureCollection.RetypingFeatureWriter(
099: writer, map.getFeatureType());
100: }
101:
102: public FeatureType getSchema(String typeName) throws IOException {
103: FeatureTypeMap map = getTypeMapBackwards(typeName);
104: updateMap(map, true);
105: return map.getFeatureType();
106: }
107:
108: public String[] getTypeNames() throws IOException {
109: // here we transform the names, and also refresh the type maps so that
110: // they
111: // don't contain stale elements
112: String[] names = wrapped.getTypeNames();
113: String[] transformedNames = new String[names.length];
114: Map backup = new HashMap(forwardMap);
115: forwardMap.clear();
116: backwardsMap.clear();
117: for (int i = 0; i < names.length; i++) {
118: String original = names[i];
119: transformedNames[i] = transformFeatureTypeName(original);
120:
121: FeatureTypeMap map = (FeatureTypeMap) backup.get(original);
122: if (map == null) {
123: map = new FeatureTypeMap(original, transformedNames[i]);
124: }
125: forwardMap.put(map.getOriginalName(), map);
126: backwardsMap.put(map.getName(), map);
127: }
128: return transformedNames;
129: }
130:
131: public FeatureReader getFeatureReader(Query query,
132: Transaction transaction) throws IOException {
133: FeatureTypeMap map = getTypeMapBackwards(query.getTypeName());
134: updateMap(map, false);
135: FeatureReader reader = wrapped.getFeatureReader(retypeQuery(
136: query, map), transaction);
137: if (map.isUnchanged())
138: return reader;
139: return new RetypingFeatureCollection.RetypingFeatureReader(
140: reader, map.getFeatureType());
141: }
142:
143: public FeatureSource getFeatureSource(String typeName)
144: throws IOException {
145: FeatureTypeMap map = getTypeMapBackwards(typeName);
146: updateMap(map, false);
147: FeatureSource source = wrapped.getFeatureSource(map
148: .getOriginalName());
149: if (map.isUnchanged())
150: return source;
151: if (source instanceof FeatureLocking) {
152: FeatureLocking locking = (FeatureLocking) source;
153: return new RetypingFeatureLocking(this , locking, map);
154: } else if (source instanceof FeatureStore) {
155: FeatureStore store = (FeatureStore) source;
156: return new RetypingFeatureStore(this , store, map);
157: }
158: return new RetypingFeatureSource(this , source, map);
159: }
160:
161: public LockingManager getLockingManager() {
162: return wrapped.getLockingManager();
163: }
164:
165: public FeatureSource getView(Query query) throws IOException,
166: SchemaException {
167: FeatureTypeMap map = getTypeMapBackwards(query.getTypeName());
168: updateMap(map, false);
169: FeatureSource view = wrapped.getView(query);
170: return new RetypingFeatureSource(this , view, map);
171: }
172:
173: /**
174: * Returns the type map given the external type name
175: *
176: * @param externalTypeName
177: * @return
178: * @throws IOException
179: */
180: FeatureTypeMap getTypeMapBackwards(String externalTypeName)
181: throws IOException {
182: FeatureTypeMap map = (FeatureTypeMap) backwardsMap
183: .get(externalTypeName);
184: if (map == null)
185: throw new IOException(
186: "Type mapping has not been established for type "
187: + externalTypeName
188: + ". "
189: + "Make sure you access types using getTypeNames() or getSchema() "
190: + "before trying to read/write onto them");
191: return map;
192: }
193:
194: /**
195: * Make sure the FeatureTypeMap is fully loaded
196: *
197: * @param map
198: * @throws IOException
199: */
200: void updateMap(FeatureTypeMap map, boolean forceUpdate)
201: throws IOException {
202: try {
203: if (map.getFeatureType() == null || forceUpdate) {
204: FeatureType original = wrapped.getSchema(map
205: .getOriginalName());
206: FeatureType transformed = transformFeatureType(original);
207: map.setFeatureTypes(original, transformed);
208: }
209: } catch (IOException e) {
210: // if the feature type cannot be found in the original data store,
211: // remove it from the map
212: backwardsMap.remove(map.getName());
213: forwardMap.remove(map.getOriginalName());
214: }
215: }
216:
217: /**
218: * Transforms the original feature type into a destination one according to
219: * the renaming rules. For the moment, it's just a feature type name
220: * replacement
221: *
222: * @param original
223: * @return
224: * @throws IOException
225: */
226: protected FeatureType transformFeatureType(FeatureType original)
227: throws IOException {
228: String transfomedName = transformFeatureTypeName(original
229: .getTypeName());
230: if (transfomedName.equals(original.getTypeName()))
231: return original;
232:
233: try {
234: return FeatureTypeBuilder.newFeatureType(original
235: .getAttributeTypes(), transfomedName, original
236: .getNamespace(), false, original.getAncestors(),
237: original.getDefaultGeometry());
238: } catch (Exception e) {
239: throw new DataSourceException(
240: "Could not build the renamed feature type.", e);
241: }
242: }
243:
244: /**
245: * Just transform the feature type name
246: *
247: * @param originalName
248: * @return
249: */
250: protected String transformFeatureTypeName(String originalName) {
251: // if(originalName.indexOf(":") >= 0) {
252: // return originalName.substring(originalName.indexOf(":") + 1);
253: // } else {
254: // return originalName;
255: // }
256: return originalName.replaceAll(":", "_");
257: }
258:
259: public void dispose() {
260: wrapped.dispose();
261: }
262:
263: /**
264: * Retypes a query from the extenal type to the internal one using the
265: * provided typemap
266: * @param q
267: * @param typeMap
268: * @return
269: * @throws IOException
270: */
271: Query retypeQuery(Query q, FeatureTypeMap typeMap) {
272: DefaultQuery modified = new DefaultQuery(q);
273: modified.setTypeName(typeMap.getOriginalName());
274: modified.setFilter(retypeFilter(q.getFilter(), typeMap));
275: return modified;
276: }
277:
278: /**
279: * Retypes a filter making sure the fids are using the internal typename prefix
280: * @param filter
281: * @param typeMap
282: * @return
283: */
284: Filter retypeFilter(Filter filter, FeatureTypeMap typeMap) {
285: FidTransformeVisitor visitor = new FidTransformeVisitor(typeMap);
286: return (Filter) filter.accept(visitor, null);
287: }
288:
289: }
|