001: package com.vividsolutions.jump.workbench.model.cache;
002:
003: import java.util.Collection;
004: import java.util.Iterator;
005: import java.util.List;
006:
007: import javax.swing.SwingUtilities;
008:
009: import com.vividsolutions.jts.geom.Envelope;
010: import com.vividsolutions.jump.feature.Feature;
011: import com.vividsolutions.jump.feature.FeatureCollection;
012: import com.vividsolutions.jump.feature.FeatureCollectionWrapper;
013: import com.vividsolutions.jump.feature.FeatureDataset;
014: import com.vividsolutions.jump.feature.FeatureSchema;
015: import com.vividsolutions.jump.util.Block;
016: import com.vividsolutions.jump.util.LazyList;
017: import com.vividsolutions.jump.util.ListWrapper;
018: import com.vividsolutions.jump.workbench.ui.plugin.AddNewLayerPlugIn;
019:
020: /**
021: * Caches features to prevent unnecessary queries. Useful for wrapping
022: * database-backed FeatureCollections. All calls are delegated to the cache,
023: * except for calls to query(envelope).iterator() where (1) the envelope is not
024: * within the cache envelope, and (2) the call is made in a non-GUI thread.
025: */
026: // The cache is a ThreadSafeFeatureCollection. [Jon Aquino 2005-03-04]
027: public class CachingFeatureCollection extends FeatureCollectionWrapper {
028:
029: private Envelope envelopeOfCompletedCache = new Envelope();
030:
031: private FeatureCollection featureCollection;
032:
033: private boolean cachingByEnvelope = true;
034:
035: // private static final FeatureCollection DUMMY_CACHED_FEATURE_COLLECTION = AddNewLayerPlugIn.createBlankFeatureCollection();
036:
037: public CachingFeatureCollection(
038: final FeatureCollection featureCollection) {
039: // Note that this implementation assumes that the feature collection is
040: // being viewed by a single LayerViewPanel. This is the common case;
041: // however, it is possibile that there could be multiple LayerViewPanels
042: // viewing the feature collection i.e. if the user clicks Clone Window.
043: // This is not used very often though. [Jon Aquino 2005-03-03]
044: super (AddNewLayerPlugIn.createBlankFeatureCollection());
045:
046: this .featureCollection = featureCollection;
047: }
048:
049: /**
050: * @see com.vividsolutions.jump.feature.FeatureCollectionWrapper#getEnvelope()
051: */
052: public Envelope getEnvelope() {
053: try {
054: Envelope e = featureCollection.getEnvelope();
055: if (e != null)
056: return e;
057: } catch (Throwable t) {
058: t.printStackTrace();
059: }
060: return super .getEnvelope();
061: }
062:
063: /**
064: * @see com.vividsolutions.jump.feature.FeatureCollectionWrapper#getFeatureSchema()
065: */
066: public FeatureSchema getFeatureSchema() {
067: return featureCollection.getFeatureSchema();
068: }
069:
070: public List query(final Envelope envelope) {
071: // This code achieves its simplicity using two wrappers:
072: // LazyList and ListWrapper. [Jon Aquino 2005-03-22]
073:
074: // To prevent an unnecessary query of the cached feature collection, use
075: // a LazyList to defer the query, as we don't yet know whether the
076: // methods are called on or off the GUI thread. In the former case, use
077: // the cached feature collection; in the latter case (and only for the
078: // #iterator method, as this is the only method used in the
079: // LayerRenderer thread), use the live feature collection.
080: // [Jon Aquino 2005-03-03]
081: final LazyList cachedFeatureCollectionQueryResults = new LazyList(
082: new Block() {
083: public Object yield() {
084: return getCachedFeatureCollection().query(
085: envelope);
086: }
087: });
088: // Use a ListWrapper to delegate all calls to the cached feature
089: // collection, except for calls to #iterator, which may or may not use
090: // the cache, depending on whether we are in the GUI thread.
091: // [Jon Aquino 2005-03-03]
092: return new ListWrapper() {
093: public Collection getCollection() {
094: return cachedFeatureCollectionQueryResults;
095: }
096:
097: public Iterator iterator() {
098: // Caching criterion 1: envelope check [Jon Aquino 2005-03-22]
099: if (cachingByEnvelope
100: && envelopeOfCompletedCache.contains(envelope)) {
101: return super .iterator();
102: }
103: // Caching criterion 2: GUI-thread check [Jon Aquino 2005-03-22]
104: if (SwingUtilities.isEventDispatchThread()) {
105: // Don't do database queries on the GUI thread, as we don't
106: // want the GUI thread to be held up by long operations.
107: // Instead, delegate to the cached feature collection.
108: // [Jon Aquino 2005-03-03]
109: return super .iterator();
110: }
111: final Iterator iterator = featureCollection.query(
112: envelope).iterator();
113: initializeCacheIfNecessary();
114: emptyCache();
115: envelopeOfCompletedCache = new Envelope();
116: return new Iterator() {
117: public void remove() {
118: iterator.remove();
119: }
120:
121: public boolean hasNext() {
122: return iterator.hasNext();
123: }
124:
125: public Object next() {
126: Feature nextFeature = (Feature) iterator.next();
127: getCachedFeatureCollection().add(nextFeature);
128: if (!hasNext()) {
129: // Set the cache envelope only when the cache is
130: // complete. [Jon Aquino 2005-03-03]
131: envelopeOfCompletedCache = new Envelope(
132: envelope);
133: }
134: return nextFeature;
135: }
136:
137: };
138: }
139: };
140: }
141:
142: private boolean initialized = false;
143:
144: private void initializeCacheIfNecessary() {
145: // The FeatureSchema might not defined until the last minute
146: // i.e. until FeatureCollection#query is called [Jon Aquino
147: // 2005-03-04]
148: // Example: DynamicFeatureCollection [Jon Aquino 2005-03-22]
149: if (initialized) {
150: return;
151: }
152: setCachedFeatureCollection(new ThreadSafeFeatureCollectionWrapper(
153: new FeatureDataset(featureCollection.getFeatureSchema())));
154: initialized = true;
155: }
156:
157: private FeatureCollection getCachedFeatureCollection() {
158: return getFeatureCollection();
159: }
160:
161: private void setCachedFeatureCollection(
162: FeatureCollection cachedFeatureCollection) {
163: setFeatureCollection(cachedFeatureCollection);
164: }
165:
166: /**
167: * This setting is ignored if the call to query(envelope).iterator() is made
168: * on the GUI thread, because long queries would make the GUI unresponsive.
169: *
170: * @param cachingByEnvelope
171: * whether query(envelope).iterator() delegates to the cache if
172: * envelope is within the cache envelope
173: */
174: public CachingFeatureCollection setCachingByEnvelope(
175: boolean cachingByEnvelope) {
176: this .cachingByEnvelope = cachingByEnvelope;
177: return this ;
178: }
179:
180: public void emptyCache() {
181: getCachedFeatureCollection().clear();
182: envelopeOfCompletedCache = new Envelope();
183: }
184: }
|