/* (c) 2014 Open Source Geospatial Foundation - all rights reserved * (c) 2001 - 2013 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. */ package org.geoserver.csw.feature; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import org.geotools.feature.FeatureCollection; import org.geotools.feature.FeatureIterator; import org.geotools.feature.collection.DelegateFeatureIterator; import org.geotools.geometry.jts.ReferencedEnvelope; import org.geotools.util.NullProgressListener; import org.opengis.feature.Feature; import org.opengis.feature.type.FeatureType; /** * A derivation of GeoTools {@link org.geotools.feature.collection.AbstractFeatureCollection} that * works on top of complex features * * @author Jody Garnett (Refractions Research Inc) * @author Andrea Aime - GeoSolutions * * @source $URL$ */ public abstract class AbstractFeatureCollection<T extends FeatureType, F extends Feature> implements FeatureCollection<T, F> { /** * id used when serialized to gml */ protected String id; protected T schema; protected AbstractFeatureCollection(T memberType) { this.id = id == null ? "featureCollection" : id; this.schema = memberType; } // // FeatureCollection - Feature Access // @SuppressWarnings("unchecked") public FeatureIterator<F> features() { FeatureIterator iter = new DelegateFeatureIterator(openIterator()); getOpenIterators().add(iter); return iter; } /** * Clean up after any resources associated with this iteartor in a manner similar to JDO * collections. </p> Example (safe) use: * * <pre> * <code> * Iterator iterator = collection.iterator(); * try { * for( Iterator i=collection.iterator(); i.hasNext();){ * Feature feature = (Feature) i.hasNext(); * System.out.println( feature.getID() ); * } * } * finally { * collection.close( iterator ); * } * </code> * </pre> * * </p> * * @param close iterator to close */ @SuppressWarnings("unchecked") final public void close(Iterator close) { if (close == null) return; try { closeIterator(close); } catch (Throwable e) { // TODO Log e = ln } finally { open.remove(close); } } public void close(FeatureIterator<F> close) { if (close != null) { close.close(); } } /** * Open a resource based Iterator, we will call close( iterator ). * <p> * Please subclass to provide your own iterator for the the ResourceCollection, note * <code>iterator()</code> is implemented to call <code>open()</code> and track the results in * for later <code>purge()</code>. * * @return Iterator based on resource use */ abstract protected Iterator<F> openIterator(); /** * Please override to cleanup after your own iterators, and any used resources. * <p> * As an example if the iterator was working off a File then the inputstream should be closed. * </p> * <p> * Subclass must call super.close( close ) to allow the list of open iterators to be adjusted. * </p> * * @param close Iterator, will not be <code>null</code> */ abstract protected void closeIterator(Iterator<F> close); /** * Close any outstanding resources released by this resources. * <p> * This method should be used with great caution, it is however available to allow the use of * the ResourceCollection with algorthims that are unaware of the need to close iterators after * use. * </p> * <p> * Example of using a normal Collections utility method: * * <pre> * <code> * Collections.sort( collection ); * collection.purge(); * </code> * </pre> */ @SuppressWarnings("unchecked") public void purge() { for (Iterator i = open.iterator(); i.hasNext();) { Object resource = i.next(); if (resource instanceof Iterator) { Iterator resourceIterator = (Iterator) resource; try { closeIterator(resourceIterator); } catch (Throwable e) { // TODO: Log e = ln } finally { i.remove(); } } } } /** * Removes all of the elements from this collection (optional operation). * * @throws UnsupportedOperationException if the <tt>clear</tt> method is not supported by this * collection. */ public void clear() { Iterator<F> e = iterator(); try { while (e.hasNext()) { e.next(); e.remove(); } } finally { close(e); } } /** * Returns <tt>true</tt> if this collection contains the specified element. <tt></tt>. * <p> * * This implementation iterates over the elements in the collection, checking each element in * turn for equality with the specified element. * * @param o object to be checked for containment in this collection. * @return <tt>true</tt> if this collection contains the specified element. */ public boolean contains(Object o) { Iterator<F> e = null; try { e = iterator(); if (o == null) { while (e.hasNext()) if (e.next() == null) return true; } else { while (e.hasNext()) if (o.equals(e.next())) return true; } return false; } finally { close(e); } } /** * Returns <tt>true</tt> if this collection contains all of the elements in the specified * collection. * <p> * * @param c collection to be checked for containment in this collection. * @return <tt>true</tt> if this collection contains all of the elements in the specified * collection. * @throws NullPointerException if the specified collection is null. * * @see #contains(Object) */ public boolean containsAll(Collection<?> c) { Iterator<?> e = c.iterator(); try { while (e.hasNext()) if (!contains(e.next())) return false; return true; } finally { close(e); } } // // Contents // // /** Set of open resource iterators */ @SuppressWarnings("unchecked") protected final Set open = new HashSet<Iterator<F>>(); /** * Returns the set of open iterators. * <p> * Contents are a mix of Iterator<F> and FeatureIterator */ @SuppressWarnings("unchecked") final public Set getOpenIterators() { return open; } /** * Please implement! * <p> * Note: If you return a ResourceIterator, the default implemntation of close( Iterator ) will * know what to do. * */ @SuppressWarnings("unchecked") final public Iterator<F> iterator() { Iterator<F> iterator = openIterator(); getOpenIterators().add(iterator); return iterator; } /** * @return <tt>true</tt> if this collection contains no elements. */ public boolean isEmpty() { Iterator<F> iterator = iterator(); try { return !iterator.hasNext(); } finally { close(iterator); } } /** * Array of all the elements. * * @return an array containing all of the elements in this collection. */ public Object[] toArray() { Object[] result = new Object[size()]; Iterator<F> e = null; try { e = iterator(); for (int i = 0; e.hasNext(); i++) result[i] = e.next(); return result; } finally { close(e); } } @SuppressWarnings("unchecked") public <T2> T2[] toArray(T2[] a) { int size = size(); if (a.length < size) { a = (T2[]) java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), size); } Iterator<F> it = iterator(); try { Object[] result = a; for (int i = 0; i < size; i++) result[i] = it.next(); if (a.length > size) a[size] = null; return a; } finally { close(it); } } public void accepts(org.opengis.feature.FeatureVisitor visitor, org.opengis.util.ProgressListener progress) { Iterator<F> iterator = null; if (progress == null) progress = new NullProgressListener(); try { float size = size(); float position = 0; progress.started(); for (iterator = iterator(); !progress.isCanceled() && iterator.hasNext();) { if (size > 0) progress.progress(position++ / size); try { Feature feature = (Feature) iterator.next(); visitor.visit(feature); } catch (Exception erp) { progress.exceptionOccurred(erp); } } } finally { progress.complete(); close(iterator); } } public String getID() { return id; } public T getSchema() { return schema; } @Override public int size() { FeatureIterator<F> fi = null; int count = 0; try { fi = features(); while (fi.hasNext()) { fi.next(); count++; } } finally { if (fi != null) { fi.close(); } } return count; } @Override public ReferencedEnvelope getBounds() { FeatureIterator<F> fi = null; ReferencedEnvelope bounds = null; try { fi = features(); while (fi.hasNext()) { Feature f = fi.next(); ReferencedEnvelope re = ReferencedEnvelope.reference(f.getBounds()); if (bounds == null) { bounds = re; } else { bounds.expandToInclude(re); } } } finally { if (fi != null) { fi.close(); } } return bounds; } }