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