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.Arrays;
020: import java.util.HashMap;
021: import java.util.Iterator;
022: import java.util.Map;
023: import java.util.Map.Entry;
024:
025: import org.geotools.data.Transaction.State;
026: import org.geotools.feature.Feature;
027: import org.geotools.feature.FeatureCollection;
028: import org.geotools.feature.FeatureType;
029: import org.geotools.feature.IllegalAttributeException;
030: import org.geotools.feature.SimpleFeature;
031: import org.geotools.geometry.jts.ReferencedEnvelope;
032: import org.opengis.filter.Filter;
033:
034: import com.vividsolutions.jts.geom.Envelope;
035: import com.vividsolutions.jts.geom.Geometry;
036:
037: /**
038: * A Transaction.State that keeps a difference table for use with
039: * AbstractDataStore.
040: *
041: * @author Jody Garnett, Refractions Research
042: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/main/src/main/java/org/geotools/data/TransactionStateDiff.java $
043: */
044: public class TransactionStateDiff implements State {
045: /**
046: * DataStore used to commit() results of this transaction.
047: *
048: * @see TransactionStateDiff.commit();
049: */
050: AbstractDataStore store;
051:
052: /** Tranasction this State is opperating against. */
053: Transaction transaction;
054:
055: /**
056: * Map of differences by typeName.
057: *
058: * <p>
059: * Differences are stored as a Map of Feature by fid, and are reset during
060: * a commit() or rollback().
061: * </p>
062: */
063: Map typeNameDiff = new HashMap();
064:
065: public TransactionStateDiff(AbstractDataStore dataStore) {
066: store = dataStore;
067: }
068:
069: public synchronized void setTransaction(Transaction transaction) {
070: if (transaction != null) {
071: // configure
072: this .transaction = transaction;
073: } else {
074: this .transaction = null;
075:
076: if (typeNameDiff != null) {
077: for (Iterator i = typeNameDiff.values().iterator(); i
078: .hasNext();) {
079: Diff diff = (Diff) i.next();
080: diff.clear();
081: }
082:
083: typeNameDiff.clear();
084: }
085:
086: store = null;
087: }
088: }
089:
090: public synchronized Diff diff(String typeName) throws IOException {
091: if (!exists(typeName)) {
092: throw new IOException(typeName + " not defined");
093: }
094:
095: if (typeNameDiff.containsKey(typeName)) {
096: return (Diff) typeNameDiff.get(typeName);
097: } else {
098: Diff diff = new Diff();
099: typeNameDiff.put(typeName, diff);
100:
101: return diff;
102: }
103: }
104:
105: boolean exists(String typeName) {
106: String[] types;
107: try {
108: types = store.getTypeNames();
109: } catch (IOException e) {
110: return false;
111: }
112: Arrays.sort(types);
113:
114: return Arrays.binarySearch(types, typeName) != -1;
115: }
116:
117: /**
118: * @see org.geotools.data.Transaction.State#addAuthorization(java.lang.String)
119: */
120: public synchronized void addAuthorization(String AuthID)
121: throws IOException {
122: // not required for TransactionStateDiff
123: }
124:
125: /**
126: * Will apply differences to store.
127: *
128: * @see org.geotools.data.Transaction.State#commit()
129: */
130: public synchronized void commit() throws IOException {
131: Map.Entry entry;
132:
133: for (Iterator i = typeNameDiff.entrySet().iterator(); i
134: .hasNext();) {
135: entry = (Entry) i.next();
136:
137: String typeName = (String) entry.getKey();
138: Diff diff = (Diff) entry.getValue();
139: applyDiff(typeName, diff);
140: }
141: }
142:
143: /**
144: * Called by commit() to apply one set of diff
145: *
146: * <p>
147: * diff will be modified as the differneces are applied, If the opperations
148: * is successful diff will be empty at the end of this process.
149: * </p>
150: *
151: * <p>
152: * diff can be used to represent the following operations:
153: * </p>
154: *
155: * <ul>
156: * <li>
157: * fid|null: represents a fid being removed
158: * </li>
159: * <li>
160: * fid|feature: where fid exists, represents feature modification
161: * </li>
162: * <li>
163: * fid|feature: where fid does not exist, represents feature being modified
164: * </li>
165: * </ul>
166: *
167: *
168: * @param typeName typeName being updated
169: * @param diff differences to apply to FeatureWriter
170: *
171: * @throws IOException If the entire diff cannot be writen out
172: * @throws DataSourceException If the entire diff cannot be writen out
173: */
174: void applyDiff(String typeName, Diff diff) throws IOException {
175: if (diff.isEmpty()) {
176: return;
177: }
178: FeatureWriter writer;
179: try {
180: writer = store.createFeatureWriter(typeName, transaction);
181: } catch (UnsupportedOperationException e) {
182: // backwards compatibility
183: writer = store.getFeatureWriter(typeName);
184: }
185: SimpleFeature feature;
186: Feature update;
187: String fid;
188:
189: try {
190: while (writer.hasNext()) {
191: feature = (SimpleFeature) writer.next();
192: fid = feature.getID();
193:
194: if (diff.modified2.containsKey(fid)) {
195: update = (Feature) diff.modified2.get(fid);
196:
197: if (update == NULL) {
198: writer.remove();
199:
200: // notify
201: store.listenerManager.fireFeaturesRemoved(
202: typeName, transaction, feature
203: .getBounds(), true);
204: } else {
205: try {
206: feature.setAttributes(update
207: .getAttributes(null));
208: writer.write();
209:
210: // notify
211: Envelope bounds = new Envelope();
212: bounds.expandToInclude(feature.getBounds());
213: bounds.expandToInclude(update.getBounds());
214: store.listenerManager
215: .fireFeaturesChanged(typeName,
216: transaction, bounds, true);
217: } catch (IllegalAttributeException e) {
218: throw new DataSourceException(
219: "Could update " + fid, e);
220: }
221: }
222: }
223: }
224:
225: Feature addedFeature;
226: SimpleFeature nextFeature;
227:
228: synchronized (diff) {
229: for (Iterator i = diff.added.values().iterator(); i
230: .hasNext();) {
231: addedFeature = (Feature) i.next();
232:
233: fid = addedFeature.getID();
234:
235: nextFeature = (SimpleFeature) writer.next();
236:
237: if (nextFeature == null) {
238: throw new DataSourceException("Could not add "
239: + fid);
240: } else {
241: try {
242: nextFeature.setAttributes(addedFeature
243: .getAttributes(null));
244: writer.write();
245:
246: // notify
247: store.listenerManager.fireFeaturesAdded(
248: typeName, transaction, nextFeature
249: .getBounds(), true);
250: } catch (IllegalAttributeException e) {
251: throw new DataSourceException(
252: "Could update " + fid, e);
253: }
254: }
255: }
256: }
257: } finally {
258: writer.close();
259: store.listenerManager.fireChanged(typeName, transaction,
260: true);
261: diff.clear();
262: }
263: }
264:
265: /**
266: * @see org.geotools.data.Transaction.State#rollback()
267: */
268: public synchronized void rollback() throws IOException {
269: Map.Entry entry;
270:
271: for (Iterator i = typeNameDiff.entrySet().iterator(); i
272: .hasNext();) {
273: entry = (Entry) i.next();
274:
275: String typeName = (String) entry.getKey();
276: Diff diff = (Diff) entry.getValue();
277:
278: diff.clear(); // rollback differences
279: store.listenerManager.fireChanged(typeName, transaction,
280: false);
281: }
282: }
283:
284: /**
285: * Convience Method for a Transaction based FeatureReader.
286: *
287: * <p>
288: * Constructs a DiffFeatureReader that works against this Transaction.
289: * </p>
290: *
291: * @param typeName TypeName to aquire a Reader on
292: *
293: * @return FeatureReader the mask orgional contents with against the
294: * current Differences recorded by the Tansasction State
295: *
296: * @throws IOException If typeName is not Manged by this Tansaction State
297: */
298: public synchronized FeatureReader reader(String typeName)
299: throws IOException {
300: Diff diff = diff(typeName);
301: FeatureReader reader = store.getFeatureReader(typeName);
302:
303: return new DiffFeatureReader(reader, diff);
304: }
305:
306: /**
307: * Convience Method for a Transaction based FeatureWriter
308: *
309: * <p>
310: * Constructs a DiffFeatureWriter that works against this Transaction.
311: * </p>
312: *
313: * @param typeName Type Name to record differences against
314: * @param filter
315: *
316: * @return A FeatureWriter that records Differences against a FeatureReader
317: *
318: * @throws IOException If a FeatureRader could not be constucted to record
319: * differences against
320: */
321: public synchronized FeatureWriter writer(final String typeName,
322: Filter filter) throws IOException {
323: Diff diff = diff(typeName);
324: FeatureReader reader = new FilteringFeatureReader(store
325: .getFeatureReader(typeName, new DefaultQuery(typeName,
326: filter)), filter);
327:
328: return new DiffFeatureWriter(reader, diff, filter) {
329: public void fireNotification(int eventType, Envelope bounds) {
330: switch (eventType) {
331: case FeatureEvent.FEATURES_ADDED:
332: store.listenerManager.fireFeaturesAdded(typeName,
333: transaction, bounds, false);
334:
335: break;
336:
337: case FeatureEvent.FEATURES_CHANGED:
338: store.listenerManager.fireFeaturesChanged(typeName,
339: transaction, bounds, false);
340:
341: break;
342:
343: case FeatureEvent.FEATURES_REMOVED:
344: store.listenerManager.fireFeaturesRemoved(typeName,
345: transaction, bounds, false);
346:
347: break;
348: }
349: }
350: };
351: }
352:
353: public static final Feature NULL = new Feature() {
354:
355: public Object getAttribute(String arg0) {
356: // TODO Auto-generated method stub
357: return null;
358: }
359:
360: public Object getAttribute(int arg0) {
361: // TODO Auto-generated method stub
362: return null;
363: }
364:
365: public Object[] getAttributes(Object[] arg0) {
366: // TODO Auto-generated method stub
367: return null;
368: }
369:
370: public ReferencedEnvelope getBounds() {
371: // TODO Auto-generated method stub
372: return null;
373: }
374:
375: public Geometry getDefaultGeometry() {
376: // TODO Auto-generated method stub
377: return null;
378: }
379:
380: public FeatureType getFeatureType() {
381: // TODO Auto-generated method stub
382: return null;
383: }
384:
385: public String getID() {
386: // TODO Auto-generated method stub
387: return null;
388: }
389:
390: public int getNumberOfAttributes() {
391: // TODO Auto-generated method stub
392: return 0;
393: }
394:
395: public FeatureCollection getParent() {
396: // TODO Auto-generated method stub
397: return null;
398: }
399:
400: public void setAttribute(int arg0, Object arg1)
401: throws IllegalAttributeException,
402: ArrayIndexOutOfBoundsException {
403: // TODO Auto-generated method stub
404:
405: }
406:
407: public void setAttribute(String arg0, Object arg1)
408: throws IllegalAttributeException {
409: // TODO Auto-generated method stub
410:
411: }
412:
413: public void setDefaultGeometry(Geometry arg0)
414: throws IllegalAttributeException {
415: // TODO Auto-generated method stub
416:
417: }
418:
419: public void setParent(FeatureCollection arg0) {
420: // TODO Auto-generated method stub
421:
422: }
423:
424: };
425:
426: }
|