001: package org.geotools.feature.iso;
002:
003: import java.util.ArrayList;
004: import java.util.Collection;
005: import java.util.HashSet;
006: import java.util.Iterator;
007: import java.util.List;
008: import java.util.Set;
009:
010: import org.geotools.feature.iso.collection.AbstractFeatureCollection;
011: import org.geotools.feature.iso.collection.AbstractResourceCollection;
012: import org.opengis.feature.Feature;
013: import org.opengis.feature.FeatureCollection;
014: import org.opengis.feature.type.AttributeDescriptor;
015: import org.opengis.feature.type.FeatureCollectionType;
016:
017: /**
018: * This is is a sample FeatureCollection implementation.
019: * <p>
020: * If you are a user - yes this FeatureCollection can be used to hold content in
021: * memory.
022: * <p>
023: * <p>
024: * Note/Rant to data providers:
025: * <p>
026: * If you are a data provider (implementing a DataStore?) please don't use this
027: * class, you should be doing your own thing.
028: * </p>
029: * As an example of doing your own thing, JDBC DataStore should be making a
030: * JDBCFeatureCollection that just maintains a Filter defining the contents.
031: * Until such time as content is accessed for the first time; at which point the
032: * collection can be relized by fetching a ResultSet. If possible (for a paged
033: * result set) this may be cached for subsequent access.
034: * <p>
035: * Note that in a good implementation FeatureCollection will form a "chain" that
036: * is grounded by a FeatureStore that holds the Transaction etc... It is up to
037: * the implementor to decide what to do when an FeatureCollection in the chain
038: * actually relizes content? Collections downstream should be able to work off
039: * the FeatureCollection that is already relized. Note additional API may be
040: * used, as the FeatureCollection directly creates subCollections that act views
041: * on the origional content.
042: * </p>
043: * If this is too complicated (aka you don't want to break out data mining
044: * techniques) please consider working with the following - A three tiered
045: * approach with different assumptions at each level:
046: * <ol>
047: * <li>Level 1 - All <br>
048: * Example:<code>FeatureStore.getFeatures()</code>
049: * <ul>
050: * <li>represents all the content, assume this cannot fit into memory.
051: * <li>don't cache unless high latency w/ modification notification or
052: * timestamp available (aka WFS)
053: * <li>use metadata for aggregate function results if available (bounds, count)
054: * </ul>
055: * <li>Level 2 - Collection <br>
056: * Example:<code>FeatureStore.getFeatures().getSubCollection( Filter )</code>
057: * <br>
058: * Example:<code>FeatureStore.getFeatures( Filter )</code>
059: * <ul>
060: * <li>- represents the results of a query, may cache
061: * <li>- consider cache result of aggregate functions
062: * <li>- consider cache data (database resultset, local hsql cache, whatever)
063: * <li>- consider cache in memory (for small count)
064: * </ul>
065: * <li>Level 3 - Transient <br>
066: * Example:<code>FeatureStore.getFeatures().getSubCollection( Filter ).getSubCollection( Filter )</code>
067: * <br>
068: * Example:<code>FeatureCollection.getSubCollection( Filter )</code>
069: * <ul>
070: * <li>temporary collection (used to hold a Filter for subsequent opperation
071: * and cut down on API) <br>
072: * <b>Example:</b><code>collection.getSubCollection( Filter ).remove()</code>
073: * <li>don't cache result, see above use
074: * <li>if getSubCollection( Filter) then they are breaking out assumption, take
075: * appropriate action. <br>
076: * <b>Appropriate Action:</b?construct a Level 2 collection, and wrap it (aka
077: * switch over to delegation), and provide the client code with another Level 3
078: * <li>
079: * </ul>
080: * </ul>
081: * The above breakdown would be a good breakdown of abstract classes for
082: * implementors to work against. However even if this is provided, there is no
083: * excuse not to do the right thing for your datasource. And for a data source
084: * the right thing is never to burn memory.
085: * </p>
086: *
087: * @author Jody Garnett
088: */
089: public class FeatureCollectionImpl extends AbstractFeatureCollection
090: implements FeatureCollection {
091:
092: List/*<Feature>*/features;
093:
094: Set/*<String>*/fids = new HashSet();
095:
096: public FeatureCollectionImpl(Collection values,
097: AttributeDescriptor desc, String id) {
098: super (values, desc, id);
099: features = new ArrayList/*<Feature>*/();
100: }
101:
102: public FeatureCollectionImpl(Collection values,
103: FeatureCollectionType type, String id) {
104: super (values, type, id);
105: features = new ArrayList/*<Feature>*/();
106: }
107:
108: /**
109: * Implements Collection.size()
110: */
111: public int size() {
112: return features.size();
113: }
114:
115: /**
116: * Implements {@link AbstractResourceCollection#openIterator()}
117: */
118: protected Iterator openIterator() {
119: return features.iterator();
120: }
121:
122: /**
123: * Implements {@link AbstractResourceCollection#closeIterator(Iterator)}
124: */
125: protected void closeIterator(Iterator close) {
126: //noop
127: }
128:
129: /**
130: * Implemens Collection.add(E)
131: *
132: * @param f
133: * @return
134: */
135: public boolean add(Object/*Feature*/object) {
136: Feature f = (Feature) object;
137: boolean added = false;
138: if (f != null && !fids.contains(f.getID())) {
139: added = features.add(f);
140: if (added) {
141: fids.add(f.getID());
142: }
143: }
144: return added;
145: }
146:
147: public void clear() {
148: features.clear();
149: fids.clear();
150: }
151:
152: public boolean remove(Object/*Feature*/object) {
153: Feature f = (Feature) object;
154: boolean removed = features.remove(f);
155: if (removed) {
156: fids.remove(f.getID());
157: }
158: return removed;
159: }
160:
161: public boolean removeAll(Collection/*<?>*/c) {
162: boolean changed = features.removeAll(c);
163: if (changed) {
164: for (Iterator itr = c.iterator(); itr.hasNext();) {
165: Object o = itr.next();
166: if (o instanceof Feature)
167: fids.remove(((Feature) o).getID());
168: }
169: }
170: return changed;
171: }
172: }
|