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.HashSet;
020: import java.util.Iterator;
021: import java.util.List;
022: import java.util.NoSuchElementException;
023: import java.util.Set;
024:
025: import org.geotools.feature.Feature;
026: import org.geotools.feature.FeatureType;
027: import org.geotools.feature.IllegalAttributeException;
028: import org.geotools.filter.AttributeExpression;
029: import org.geotools.filter.Expression;
030: import org.geotools.filter.FidFilter;
031: import org.geotools.filter.GeometryFilter;
032: import org.geotools.filter.LiteralExpression;
033: import org.opengis.filter.Filter;
034: import org.opengis.filter.spatial.BBOX;
035: import org.opengis.filter.spatial.Contains;
036: import org.opengis.filter.spatial.Crosses;
037: import org.opengis.filter.spatial.Overlaps;
038: import org.opengis.filter.spatial.Touches;
039: import org.opengis.filter.spatial.Within;
040:
041: import com.vividsolutions.jts.geom.Envelope;
042: import com.vividsolutions.jts.geom.Geometry;
043:
044: /**
045: * A FeatureReader that considers differences.
046: * <p>
047: * Used to implement In-Process Transaction support. This implementation will need to peek ahead in
048: * order to check for deletetions.
049: * </p>
050: *
051: * @author Jody Garnett, Refractions Research
052: * @source $URL:
053: * http://svn.geotools.org/geotools/branches/2.2.x/module/main/src/org/geotools/data/DiffFeatureReader.java $
054: */
055: public class DiffFeatureReader implements FeatureReader {
056: FeatureReader reader;
057: Diff diff;
058:
059: /** Next value as peeked by hasNext() */
060: Feature next = null;
061: private Filter filter;
062: private Set encounteredFids;
063:
064: private Iterator addedIterator;
065: private Iterator modifiedIterator;
066: private int fidIndex = 0;
067: private Iterator spatialIndexIterator;
068:
069: private boolean indexedGeometryFilter = false;
070: private boolean fidFilter = false;
071:
072: /**
073: * This constructor grabs a "copy" of the current diff.
074: * <p>
075: * This reader is not "live" to changes over the course of the Transaction. (Iterators are not
076: * always stable of the course of modifications)
077: * </p>
078: *
079: * @param reader
080: * @param diff2 Differences of Feature by FID
081: */
082: public DiffFeatureReader(FeatureReader reader, Diff diff2) {
083: this (reader, diff2, Filter.INCLUDE);
084: }
085:
086: /**
087: * This constructor grabs a "copy" of the current diff.
088: * <p>
089: * This reader is not "live" to changes over the course of the Transaction. (Iterators are not
090: * always stable of the course of modifications)
091: * </p>
092: *
093: * @param reader
094: * @param diff2 Differences of Feature by FID
095: */
096: public DiffFeatureReader(FeatureReader reader, Diff diff2,
097: Filter filter) {
098: this .reader = reader;
099: this .diff = diff2;
100: this .filter = filter;
101: encounteredFids = new HashSet();
102:
103: if (filter instanceof FidFilter) {
104: fidFilter = true;
105: } else if (isSubsetOfBboxFilter(filter)) {
106: indexedGeometryFilter = true;
107: }
108:
109: synchronized (diff) {
110: if (indexedGeometryFilter) {
111: spatialIndexIterator = getIndexedFeatures().iterator();
112: }
113: addedIterator = diff.added.values().iterator();
114: modifiedIterator = diff.modified2.values().iterator();
115: }
116: }
117:
118: /**
119: * @see org.geotools.data.FeatureReader#getFeatureType()
120: */
121: public FeatureType getFeatureType() {
122: return reader.getFeatureType();
123: }
124:
125: /**
126: * @see org.geotools.data.FeatureReader#next()
127: */
128: public Feature next() throws IOException,
129: IllegalAttributeException, NoSuchElementException {
130: if (hasNext()) {
131: Feature live = next;
132: next = null;
133:
134: return live;
135: }
136:
137: throw new NoSuchElementException("No more Feature exists");
138: }
139:
140: /**
141: * @see org.geotools.data.FeatureReader#hasNext()
142: */
143: public boolean hasNext() throws IOException {
144: if (next != null) {
145: // We found it already
146: return true;
147: }
148: Feature peek;
149:
150: if (filter == Filter.EXCLUDE)
151: return false;
152:
153: while ((reader != null) && reader.hasNext()) {
154:
155: try {
156: peek = reader.next();
157: } catch (NoSuchElementException e) {
158: throw new DataSourceException(
159: "Could not aquire the next Feature", e);
160: } catch (IllegalAttributeException e) {
161: throw new DataSourceException(
162: "Could not aquire the next Feature", e);
163: }
164:
165: String fid = peek.getID();
166: encounteredFids.add(fid);
167:
168: if (diff.modified2.containsKey(fid)) {
169: Feature changed = (Feature) diff.modified2.get(fid);
170: if (changed == TransactionStateDiff.NULL
171: || !filter.evaluate(changed)) {
172: continue;
173: } else {
174: next = changed;
175: return true;
176: }
177: } else {
178:
179: next = peek; // found feature
180: return true;
181: }
182: }
183:
184: queryDiff();
185: return next != null;
186: }
187:
188: /**
189: * @see org.geotools.data.FeatureReader#close()
190: */
191: public void close() throws IOException {
192: if (reader != null) {
193: reader.close();
194: reader = null;
195: }
196:
197: if (diff != null) {
198: diff = null;
199: addedIterator = null;
200: }
201: }
202:
203: protected void queryDiff() {
204: if (fidFilter) {
205: queryFidFilter();
206: } else if (indexedGeometryFilter) {
207: querySpatialIndex();
208: } else {
209: queryAdded();
210: queryModified();
211: }
212: }
213:
214: protected void querySpatialIndex() {
215: while (spatialIndexIterator.hasNext() && next == null) {
216: Feature f = (Feature) spatialIndexIterator.next();
217: if (encounteredFids.contains(f.getID())
218: || !filter.evaluate(f)) {
219: continue;
220: }
221: next = f;
222: }
223: }
224:
225: protected void queryAdded() {
226: while (addedIterator.hasNext() && next == null) {
227: next = (Feature) addedIterator.next();
228: if (encounteredFids.contains(next.getID())
229: || !filter.evaluate(next)) {
230: next = null;
231: }
232: }
233: }
234:
235: protected void queryModified() {
236: while (modifiedIterator.hasNext() && next == null) {
237: next = (Feature) modifiedIterator.next();
238: if (next == TransactionStateDiff.NULL
239: || encounteredFids.contains(next.getID())
240: || !filter.evaluate(next)) {
241: next = null;
242: }
243: }
244: }
245:
246: protected void queryFidFilter() {
247: FidFilter fidFilter = (FidFilter) filter;
248: if (fidIndex == -1) {
249: fidIndex = 0;
250: }
251: while (fidIndex < fidFilter.getFids().length && next == null) {
252: String fid = fidFilter.getFids()[fidIndex];
253: if (encounteredFids.contains(fid)) {
254: fidIndex++;
255: continue;
256: }
257: next = (Feature) diff.modified2.get(fid);
258: if (next == null) {
259: next = (Feature) diff.added.get(fid);
260: }
261: fidIndex++;
262: }
263: }
264:
265: protected List getIndexedFeatures() {
266: // TODO: check geom is default geom.
267: Envelope env = extractBboxForSpatialIndexQuery((GeometryFilter) filter);
268: return diff.queryIndex(env);
269: }
270:
271: protected Envelope extractBboxForSpatialIndexQuery(GeometryFilter f) {
272: GeometryFilter geomFilter = (GeometryFilter) f;
273: Expression leftGeometry = geomFilter.getLeftGeometry();
274: Expression rightGeometry = geomFilter.getRightGeometry();
275:
276: Geometry g;
277: if (leftGeometry instanceof LiteralExpression) {
278: g = (Geometry) ((LiteralExpression) leftGeometry)
279: .getLiteral();
280: } else {
281: g = (Geometry) ((LiteralExpression) rightGeometry)
282: .getLiteral();
283: }
284: return g.getEnvelopeInternal();
285: }
286:
287: protected boolean isDefaultGeometry(AttributeExpression ae) {
288: return reader.getFeatureType().getDefaultGeometry().getName()
289: .equals(ae.getAttributePath());
290: }
291:
292: protected boolean isSubsetOfBboxFilter(Filter f) {
293: return filter instanceof Contains || filter instanceof Crosses
294: || filter instanceof Overlaps
295: || filter instanceof Touches
296: || filter instanceof Within || filter instanceof BBOX;
297: }
298: }
|