001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2005-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.feature.collection;
017:
018: import java.util.Collection;
019: import java.util.HashSet;
020: import java.util.Iterator;
021: import java.util.Set;
022:
023: import org.geotools.data.collection.ResourceCollection;
024:
025: /**
026: * Collection supporting close( Iterator ).
027: * <p>
028: * This implementation is a port of java.util.Collection with support for
029: * the use of close( Iterator ). This will allow subclasses that make use of
030: * resources during iterator() to be uses safely.
031: * </p>
032: * <p>
033: * Subclasses are reminded that they should construct their Iterator to
034: * return system resources once content has been exhuasted. While this class
035: * is safe, and we remind users, not all libraries that accept collections
036: * can be hacked.
037: * </p>
038: * <h2>How to Collectionify Resource Access</h2>
039: * <p>
040: * We need to do the same things as for use of AbstractCollection - namely:
041: * <ul>
042: * <li><b>Read-Only</b>:
043: * provide implementations for <code>size</code> and <tt>openIterator</tt> ( w/ <code>hasNext()</code> and <code>next()</code>.)
044: * and finally closeIteartor( iterator )<p>
045: * </li>
046: * <li><b>Modifiable collection</b>
047: * Do all of the above and supply <code>add( Object )</code>
048: * let that <code>Iterator</code> do <tt>remove()</tt>
049: * </li>
050: * </ul>
051: * And of course subclass, we are after all feelign <b>abstract</b> today :-)
052: * </p>
053: * <p>
054: * Why not play with <code>iterator()</code>? Because we are keeping track of them for
055: * later <code>purge()</code>...
056: * </p>
057: *
058: * @author Jody Garnett, Refractions Research, Inc.
059: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/main/src/main/java/org/geotools/feature/collection/AbstractResourceCollection.java $
060: */
061: public abstract class AbstractResourceCollection implements
062: ResourceCollection {
063:
064: protected AbstractResourceCollection() {
065: }
066:
067: /**
068: * @return <tt>true</tt> if this collection contains no elements.
069: */
070: public boolean isEmpty() {
071: return size() == 0;
072: }
073:
074: /**
075: * Returns <tt>true</tt> if this collection contains the specified
076: * element.
077: * <tt></tt>.<p>
078: *
079: * This implementation iterates over the elements in the collection,
080: * checking each element in turn for equality with the specified element.
081: *
082: * @param o object to be checked for containment in this collection.
083: * @return <tt>true</tt> if this collection contains the specified element.
084: */
085: public boolean contains(Object o) {
086: Iterator e = null;
087: try {
088: e = iterator();
089: if (o == null) {
090: while (e.hasNext())
091: if (e.next() == null)
092: return true;
093: } else {
094: while (e.hasNext())
095: if (o.equals(e.next()))
096: return true;
097: }
098: return false;
099: } finally {
100: close(e);
101: }
102: }
103:
104: /**
105: * Array of all the elements.
106: *
107: * @return an array containing all of the elements in this collection.
108: */
109: public Object[] toArray() {
110: Object[] result = new Object[size()];
111: Iterator e = null;
112: try {
113: e = iterator();
114: for (int i = 0; e.hasNext(); i++)
115: result[i] = e.next();
116: return result;
117: } finally {
118: close(e);
119: }
120: }
121:
122: public Object[] toArray(Object[] a) {
123: int size = size();
124: if (a.length < size)
125: a = (Object[]) java.lang.reflect.Array.newInstance(a
126: .getClass().getComponentType(), size);
127:
128: Iterator it = iterator();
129: try {
130:
131: Object[] result = a;
132: for (int i = 0; i < size; i++)
133: result[i] = it.next();
134: if (a.length > size)
135: a[size] = null;
136: return a;
137: } finally {
138: close(it);
139: }
140: }
141:
142: // Modification Operations
143:
144: /**
145: * Implement to support modification.
146: *
147: * @param o element whose presence in this collection is to be ensured.
148: * @return <tt>true</tt> if the collection changed as a result of the call.
149: *
150: * @throws UnsupportedOperationException if the <tt>add</tt> method is not
151: * supported by this collection.
152: *
153: * @throws NullPointerException if this collection does not permit
154: * <tt>null</tt> elements, and the specified element is
155: * <tt>null</tt>.
156: *
157: * @throws ClassCastException if the class of the specified element
158: * prevents it from being added to this collection.
159: *
160: * @throws IllegalArgumentException if some aspect of this element
161: * prevents it from being added to this collection.
162: */
163: public boolean add(Object o) {
164: throw new UnsupportedOperationException();
165: }
166:
167: /**
168: * Removes a single instance of the specified element from this
169: * collection, if it is present (optional operation).
170: *
171: * @param o element to be removed from this collection, if present.
172: * @return <tt>true</tt> if the collection contained the specified
173: * element.
174: * @throws UnsupportedOperationException if the <tt>remove</tt> method is
175: * not supported by this collection.
176: */
177: public boolean remove(Object o) {
178: Iterator e = iterator();
179: try {
180: if (o == null) {
181: while (e.hasNext()) {
182: if (e.next() == null) {
183: e.remove();
184: return true;
185: }
186: }
187: } else {
188: while (e.hasNext()) {
189: if (o.equals(e.next())) {
190: e.remove();
191: return true;
192: }
193: }
194: }
195: return false;
196: } finally {
197: close(e);
198: }
199: }
200:
201: // Bulk Operations
202:
203: /**
204: * Returns <tt>true</tt> if this collection contains all of the elements
205: * in the specified collection. <p>
206: *
207: * @param c collection to be checked for containment in this collection.
208: * @return <tt>true</tt> if this collection contains all of the elements
209: * in the specified collection.
210: * @throws NullPointerException if the specified collection is null.
211: *
212: * @see #contains(Object)
213: */
214: public boolean containsAll(Collection c) {
215: Iterator e = c.iterator();
216: try {
217: while (e.hasNext())
218: if (!contains(e.next()))
219: return false;
220: return true;
221: } finally {
222: close(e);
223: }
224: }
225:
226: /**
227: * Adds all of the elements in the specified collection to this collection
228: * (optional operation).
229: *
230: * @param c collection whose elements are to be added to this collection.
231: * @return <tt>true</tt> if this collection changed as a result of the
232: * call.
233: * @throws UnsupportedOperationException if this collection does not
234: * support the <tt>addAll</tt> method.
235: * @throws NullPointerException if the specified collection is null.
236: *
237: * @see #add(Object)
238: */
239: public boolean addAll(Collection c) {
240: boolean modified = false;
241: Iterator e = c.iterator();
242: try {
243: while (e.hasNext()) {
244: if (add(e.next()))
245: modified = true;
246: }
247: } finally {
248: if (c instanceof ResourceCollection) {
249: ResourceCollection other = (ResourceCollection) c;
250: other.close(e);
251: }
252: }
253: return modified;
254: }
255:
256: /**
257: * Removes from this collection all of its elements that are contained in
258: * the specified collection (optional operation). <p>
259: *
260: * @param c elements to be removed from this collection.
261: * @return <tt>true</tt> if this collection changed as a result of the
262: * call.
263: * @throws UnsupportedOperationException if the <tt>removeAll</tt> method
264: * is not supported by this collection.
265: * @throws NullPointerException if the specified collection is null.
266: *
267: * @see #remove(Object)
268: * @see #contains(Object)
269: */
270: public boolean removeAll(Collection c) {
271: boolean modified = false;
272: Iterator e = iterator();
273: try {
274: while (e.hasNext()) {
275: if (c.contains(e.next())) {
276: e.remove();
277: modified = true;
278: }
279: }
280: return modified;
281: } finally {
282: close(e);
283: }
284: }
285:
286: /**
287: * Retains only the elements in this collection that are contained in the
288: * specified collection (optional operation).
289: *
290: * @param c elements to be retained in this collection.
291: * @return <tt>true</tt> if this collection changed as a result of the
292: * call.
293: * @throws UnsupportedOperationException if the <tt>retainAll</tt> method
294: * is not supported by this Collection.
295: * @throws NullPointerException if the specified collection is null.
296: *
297: * @see #remove(Object)
298: * @see #contains(Object)
299: */
300: public boolean retainAll(Collection c) {
301: boolean modified = false;
302: Iterator e = iterator();
303: try {
304: while (e.hasNext()) {
305: if (!c.contains(e.next())) {
306: e.remove();
307: modified = true;
308: }
309: }
310: return modified;
311: } finally {
312: close(e);
313: }
314: }
315:
316: /**
317: * Removes all of the elements from this collection (optional operation).
318: *
319: * @throws UnsupportedOperationException if the <tt>clear</tt> method is
320: * not supported by this collection.
321: */
322: public void clear() {
323: Iterator e = iterator();
324: try {
325: while (e.hasNext()) {
326: e.next();
327: e.remove();
328: }
329: } finally {
330: close(e);
331: }
332: }
333:
334: // String conversion
335:
336: /**
337: * Returns a string representation of this collection.
338: *
339: * @return a string representation of this collection.
340: */
341: public String toString() {
342: StringBuffer buf = new StringBuffer();
343: buf.append("[");
344: Iterator i = iterator();
345: try {
346: boolean hasNext = i.hasNext();
347: while (hasNext) {
348: Object o = i.next();
349: buf.append(o == this ? "(this Collection)" : String
350: .valueOf(o));
351: hasNext = i.hasNext();
352: if (hasNext)
353: buf.append(", ");
354: }
355: buf.append("]");
356: return buf.toString();
357: } finally {
358: close(i);
359: }
360: }
361:
362: //
363: // Contents
364: //
365: //
366: /** Set of open resource iterators */
367: protected final Set open = new HashSet();
368:
369: /**
370: * Please implement!
371: * <p>
372: * Note: If you return a ResourceIterator, the default implemntation of close( Iterator )
373: * will know what to do.
374: *
375: */
376: final public Iterator iterator() {
377: Iterator iterator = openIterator();
378: open.add(iterator);
379: return iterator;
380: }
381:
382: /**
383: * Returns the number of elements in this collection.
384: *
385: * @return Number of items, or Interger.MAX_VALUE
386: */
387: public abstract int size();
388:
389: /**
390: * Clean up after any resources assocaited with this iteartor in a manner similar to JDO collections.
391: * </p>
392: * Example (safe) use:<pre><code>
393: * Iterator iterator = collection.iterator();
394: * try {
395: * for( Iterator i=collection.iterator(); i.hasNext();){
396: * Feature feature = (Feature) i.hasNext();
397: * System.out.println( feature.getID() );
398: * }
399: * }
400: * finally {
401: * collection.close( iterator );
402: * }
403: * </code></pre>
404: * </p>
405: * @param close
406: */
407: final public void close(Iterator close) {
408: if (close == null)
409: return;
410: try {
411: closeIterator(close);
412: } catch (Throwable e) {
413: // TODO Log e = ln
414: } finally {
415: open.remove(close);
416: }
417:
418: }
419:
420: /**
421: * Open a resource based Iterator, we will call close( iterator ).
422: * <p>
423: * Please subclass to provide your own iterator for the the ResourceCollection,
424: * note <code>iterator()</code> is implemented to call <code>open()</code>
425: * and track the results in for later <code>purge()</code>.
426: *
427: * @return Iterator based on resource use
428: */
429: abstract protected Iterator openIterator();
430:
431: /**
432: * Please override to cleanup after your own iterators, and
433: * any used resources.
434: * <p>
435: * As an example if the iterator was working off a File then
436: * the inputstream should be closed.
437: * </p>
438: * <p>
439: * Subclass must call super.close( close ) to allow the list
440: * of open iterators to be adjusted.
441: * </p>
442: *
443: * @param close Iterator, will not be <code>null</code>
444: */
445: abstract protected void closeIterator(Iterator close);
446:
447: /**
448: * Close any outstanding resources released by this resources.
449: * <p>
450: * This method should be used with great caution, it is however available
451: * to allow the use of the ResourceCollection with algorthims that are
452: * unaware of the need to close iterators after use.
453: * </p>
454: * <p>
455: * Example of using a normal Collections utility method:<pre><code>
456: * Collections.sort( collection );
457: * collection.purge();
458: * </code></pre>
459: */
460: public void purge() {
461: for (Iterator i = open.iterator(); i.hasNext();) {
462: Object resource = i.next();
463: if (resource instanceof Iterator) {
464: Iterator resourceIterator = (Iterator) resource;
465: try {
466: closeIterator(resourceIterator);
467: } catch (Throwable e) {
468: // TODO: Log e = ln
469: } finally {
470: i.remove();
471: }
472: }
473: }
474: }
475: }
|