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;
017:
018: import java.util.Collections;
019: import java.util.HashMap;
020: import java.util.Iterator;
021: import java.util.List;
022: import java.util.Map;
023: import java.util.Map.Entry;
024:
025: import org.geotools.feature.Feature;
026:
027: // TODO: replace by java.util.concurrent.ConcurrentHashMap when we will be allowed to target J2SE 1.5
028: import EDU.oswego.cs.dl.util.concurrent.ConcurrentHashMap;
029:
030: import com.vividsolutions.jts.geom.Envelope;
031: import com.vividsolutions.jts.index.SpatialIndex;
032: import com.vividsolutions.jts.index.quadtree.Quadtree;
033:
034: public class Diff {
035: private final Map modifiedFeatures;
036: private final Map addedFeatures;
037:
038: /**
039: * Unmodifiable view of modified features.
040: * It is imperative that the user manually synchronize on the
041: * map when iterating over any of its collection views:
042: * <pre>
043: * Set s = diff.modified2.keySet(); // Needn't be in synchronized block
044: * ...
045: * synchronized(diff) { // Synchronizing on diff, not diff.modified2 or s!
046: * Iterator i = s.iterator(); // Must be in synchronized block
047: * while (i.hasNext())
048: * foo(i.next());
049: * }
050: * </pre>
051: * Failure to follow this advice may result in non-deterministic behavior.
052: *
053: * <p>The returned map will be serializable if the specified map is
054: * serializable.
055: */
056: public final Map modified2;
057: /**
058: * Unmodifiable view of added features.
059: * It is imperative that the user manually synchronize on the
060: * map when iterating over any of its collection views:
061: * <pre>
062: * Set s = diff.added.keySet(); // Needn't be in synchronized block
063: * ...
064: * synchronized(diff) { // Synchronizing on m, not diff.added or s!
065: * Iterator i = s.iterator(); // Must be in synchronized block
066: * while (i.hasNext())
067: * foo(i.next());
068: * }
069: * </pre>
070: * Failure to follow this advice may result in non-deterministic behavior.
071: *
072: * <p>The returned map will be serializable if the specified map is
073: * serializable.
074: */
075: public final Map added;
076:
077: public int nextFID = 0;
078: private SpatialIndex spatialIndex;
079: Object mutex;
080:
081: public Diff() {
082: modifiedFeatures = new ConcurrentHashMap();
083: addedFeatures = new ConcurrentHashMap();
084: modified2 = Collections.unmodifiableMap(modifiedFeatures);
085: added = Collections.unmodifiableMap(addedFeatures);
086: spatialIndex = new Quadtree();
087: mutex = this ;
088: }
089:
090: public boolean isEmpty() {
091: synchronized (mutex) {
092: return modifiedFeatures.isEmpty()
093: && addedFeatures.isEmpty();
094: }
095: }
096:
097: public void clear() {
098: synchronized (mutex) {
099: nextFID = 0;
100: addedFeatures.clear();
101: modifiedFeatures.clear();
102: spatialIndex = new Quadtree();
103: }
104: }
105:
106: public Diff(Diff other) {
107: modifiedFeatures = Collections.synchronizedMap(new HashMap(
108: other.modifiedFeatures));
109: addedFeatures = Collections.synchronizedMap(new HashMap(
110: other.addedFeatures));
111: modified2 = Collections.unmodifiableMap(modifiedFeatures);
112: added = Collections.unmodifiableMap(addedFeatures);
113: spatialIndex = copySTRtreeFrom(other);
114: nextFID = other.nextFID;
115: mutex = this ;
116: }
117:
118: public void modify(String fid, Feature f) {
119: synchronized (mutex) {
120: Feature old;
121: if (addedFeatures.containsKey(fid)) {
122: old = (Feature) addedFeatures.get(fid);
123: addedFeatures.put(fid, f);
124: } else {
125: old = (Feature) modifiedFeatures.get(fid);
126: modifiedFeatures.put(fid, f);
127: }
128: if (old != null) {
129: spatialIndex.remove(old.getBounds(), old);
130: }
131: addToSpatialIndex(f);
132: }
133: }
134:
135: public void add(String fid, Feature f) {
136: synchronized (mutex) {
137: addedFeatures.put(fid, f);
138: addToSpatialIndex(f);
139: }
140: }
141:
142: protected void addToSpatialIndex(Feature f) {
143: if (f.getDefaultGeometry() != null) {
144: Envelope bounds = f.getBounds();
145: if (!bounds.isNull())
146: spatialIndex.insert(bounds, f);
147: }
148: }
149:
150: public void remove(String fid) {
151: synchronized (mutex) {
152: Feature old = null;
153:
154: if (addedFeatures.containsKey(fid)) {
155: old = (Feature) addedFeatures.get(fid);
156: addedFeatures.remove(fid);
157: } else {
158: old = (Feature) modifiedFeatures.get(fid);
159: modifiedFeatures.put(fid, TransactionStateDiff.NULL);
160: }
161: if (old != null) {
162: spatialIndex.remove(old.getBounds(), old);
163: }
164: }
165: }
166:
167: public List queryIndex(Envelope env) {
168: synchronized (mutex) {
169: return spatialIndex.query(env);
170: }
171: }
172:
173: protected Quadtree copySTRtreeFrom(Diff diff) {
174: Quadtree tree = new Quadtree();
175:
176: synchronized (diff) {
177: Iterator i = diff.added.entrySet().iterator();
178: while (i.hasNext()) {
179: Entry e = (Map.Entry) i.next();
180: Feature f = (Feature) e.getValue();
181: if (!diff.modifiedFeatures.containsKey(f.getID())) {
182: tree.insert(f.getBounds(), f);
183: }
184: }
185: Iterator j = diff.modified2.entrySet().iterator();
186: while (j.hasNext()) {
187: Entry e = (Map.Entry) j.next();
188: Feature f = (Feature) e.getValue();
189: tree.insert(f.getBounds(), f);
190: }
191: }
192:
193: return tree;
194: }
195: }
|