001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.openide.util;
043:
044: import java.util.ArrayList;
045: import java.util.Arrays;
046: import java.util.Collection;
047: import java.util.Collections;
048: import java.util.Enumeration;
049: import java.util.HashSet;
050: import java.util.Iterator;
051: import java.util.NoSuchElementException;
052: import java.util.Set;
053:
054: /**
055: * Factory methods for various types of {@link Enumeration}.
056: * Allows composition of existing enumerations, filtering their contents, and/or modifying them.
057: * All of this is designed to be done lazily, i.e. elements created on demand.
058: * @since 4.37
059: * @author Jaroslav Tulach
060: * @see NbCollections#checkedEnumerationByFilter
061: * @see NbCollections#iterable(Enumeration)
062: */
063: public final class Enumerations extends Object {
064: /** No instances */
065: private Enumerations() {
066: }
067:
068: /**
069: * An empty enumeration.
070: * Always returns <code>false</code> from
071: * <code>empty().hasMoreElements()</code> and throws <code>NoSuchElementException</code>
072: * from <code>empty().nextElement()</code>.
073: * @return the enumeration
074: */
075: public static final <T> Enumeration<T> empty() {
076: Collection<T> emptyL = Collections.emptyList();
077: return Collections.enumeration(emptyL);
078: }
079:
080: /**
081: * Creates an enumeration with one element.
082: * @param obj the element to be present in the enumeration.
083: * @return enumeration
084: */
085: public static <T> Enumeration<T> singleton(T obj) {
086: return Collections.enumeration(Collections.singleton(obj));
087: }
088:
089: /**
090: * Concatenates the content of two enumerations into one.
091: * Until the
092: * end of <code>en1</code> is reached its elements are being served.
093: * As soon as the <code>en1</code> has no more elements, the content
094: * of <code>en2</code> is being returned.
095: *
096: * @param en1 first enumeration
097: * @param en2 second enumeration
098: * @return enumeration
099: */
100: public static <T> Enumeration<T> concat(
101: Enumeration<? extends T> en1, Enumeration<? extends T> en2) {
102: ArrayList<Enumeration<? extends T>> two = new ArrayList<Enumeration<? extends T>>();
103: two.add(en1);
104: two.add(en2);
105: return new SeqEn<T>(Collections.enumeration(two));
106: }
107:
108: /**
109: * Concatenates the content of many enumerations.
110: * The input value
111: * is enumeration of Enumeration elements and the result is composed
112: * all their content. Each of the provided enumeration is fully read
113: * and their content returned before the next enumeration is asked for
114: * their elements.
115: *
116: * @param enumOfEnums Enumeration of Enumeration elements
117: * @return enumeration
118: */
119: public static <T> Enumeration<T> concat(
120: Enumeration<? extends Enumeration<? extends T>> enumOfEnums) {
121: return new SeqEn<T>(enumOfEnums);
122: }
123:
124: /**
125: * Filters the input enumeration to new one that should contain
126: * each of the provided elements just once.
127: * The elements are compared
128: * using their default <code>equals</code> and <code>hashCode</code> methods.
129: *
130: * @param en enumeration to filter
131: * @return enumeration without duplicated items
132: */
133: public static <T> Enumeration<T> removeDuplicates(Enumeration<T> en) {
134: class RDupls implements Processor<T, T> {
135: private Set<T> set = new HashSet<T>();
136:
137: public T process(T o, Collection<T> nothing) {
138: return set.add(o) ? o : null;
139: }
140: }
141:
142: return filter(en, new RDupls());
143: }
144:
145: /**
146: * Returns an enumeration that iterates over provided array.
147: * @param arr the array of object
148: * @return enumeration of those objects
149: */
150: public static <T> Enumeration<T> array(T... arr) {
151: return Collections.enumeration(Arrays.asList(arr));
152: }
153:
154: /**
155: * Removes all <code>null</code>s from the input enumeration.
156: * @param en enumeration that can contain nulls
157: * @return new enumeration without null values
158: */
159: public static <T> Enumeration<T> removeNulls(Enumeration<T> en) {
160: return filter(en, new RNulls<T>());
161: }
162:
163: /**
164: * For each element of the input enumeration <code>en</code> asks the
165: * {@link Processor} to provide a replacement.
166: * The <code>toAdd</code> argument of the processor is always null.
167: * <p>
168: * Example to convert any objects into strings:
169: * <pre>
170: * Processor convertToString = new Processor() {
171: * public Object process(Object obj, Collection alwaysNull) {
172: * return obj.toString(); // converts to string
173: * }
174: * };
175: * Enumeration strings = Enumerations.convert(elems, convertToString);
176: * </pre>
177: *
178: * @param en enumeration of any objects
179: * @param processor a callback processor for the elements (its toAdd arguments is always null)
180: * @return new enumeration where all elements has been processed
181: */
182: public static <T, R> Enumeration<R> convert(
183: Enumeration<? extends T> en, Processor<T, R> processor) {
184: return new AltEn<T, R>(en, processor);
185: }
186:
187: /**
188: * Filters some elements out from the input enumeration.
189: * Just make the
190: * {@link Processor} return <code>null</code>. Please notice the <code>toAdd</code>
191: * argument of the processor is always <code>null</code>.
192: * <p>
193: * Example to remove all objects that are not strings:
194: * <pre>
195: * Processor onlyString = new Processor() {
196: * public Object process(Object obj, Collection alwaysNull) {
197: * if (obj instanceof String) {
198: * return obj;
199: * } else {
200: * return null;
201: * }
202: * }
203: * };
204: * Enumeration strings = Enumerations.filter(elems, onlyString);
205: * </pre>
206: *
207: * @param en enumeration of any objects
208: * @param filter a callback processor for the elements (its toAdd arguments is always null)
209: * @return new enumeration which does not include non-processed (returned null from processor) elements
210: * @see NbCollections#checkedEnumerationByFilter
211: */
212: public static <T, R> Enumeration<R> filter(
213: Enumeration<? extends T> en, Processor<T, R> filter) {
214: return new FilEn<T, R>(en, filter);
215: }
216:
217: /**
218: * Support for breadth-first enumerating.
219: * Before any element is returned
220: * for the resulting enumeration it is processed in the {@link Processor} and
221: * the processor is allowed to modify it and also add additional elements
222: * at the (current) end of the <q>queue</q> by calling <code>toAdd.add</code>
223: * or <code>toAdd.addAll</code>. No other methods can be called on the
224: * provided <code>toAdd</code> collection.
225: * <p>
226: * Example of doing breadth-first walk through a tree:
227: * <pre>
228: * Processor queueSubnodes = new Processor() {
229: * public Object process(Object obj, Collection toAdd) {
230: * Node n = (Node)obj;
231: * toAdd.addAll (n.getChildrenList());
232: * return n;
233: * }
234: * };
235: * Enumeration strings = Enumerations.queue(elems, queueSubnodes);
236: * </pre>
237: *
238: * @param en initial content of the resulting enumeration
239: * @param filter the processor that is called for each element and can
240: * add and addAll elements to its toAdd Collection argument and
241: * also change the value to be returned
242: * @return enumeration with the initial and queued content (it can contain
243: * <code>null</code> if the filter returned <code>null</code> from its
244: * {@link Processor#process} method.
245: */
246: public static <T, R> Enumeration<R> queue(
247: Enumeration<? extends T> en, Processor<T, R> filter) {
248: QEn<T, R> q = new QEn<T, R>(filter);
249:
250: while (en.hasMoreElements()) {
251: q.put(en.nextElement());
252: }
253:
254: return q;
255: }
256:
257: /**
258: * Processor interface that can filter out objects from the enumeration,
259: * change them or add aditional objects to the end of the current enumeration.
260: */
261: public static interface Processor<T, R> {
262: /** @param original the object that is going to be returned from the enumeration right now
263: * @return a replacement for this object
264: * @param toAdd can be non-null if one can add new objects at the end of the enumeration
265: */
266: public R process(T original, Collection<T> toAdd);
267: }
268:
269: /** Altering enumeration implementation */
270: private static final class AltEn<T, R> extends Object implements
271: Enumeration<R> {
272: /** enumeration to filter */
273: private Enumeration<? extends T> en;
274:
275: /** map to alter */
276: private Processor<T, R> process;
277:
278: /**
279: * @param en enumeration to filter
280: */
281: public AltEn(Enumeration<? extends T> en,
282: Processor<T, R> process) {
283: this .en = en;
284: this .process = process;
285: }
286:
287: /** @return true if there is more elements in the enumeration
288: */
289: public boolean hasMoreElements() {
290: return en.hasMoreElements();
291: }
292:
293: /** @return next object in the enumeration
294: * @exception NoSuchElementException can be thrown if there is no next object
295: * in the enumeration
296: */
297: public R nextElement() {
298: return process.process(en.nextElement(), null);
299: }
300: }
301:
302: // end of AltEn
303:
304: /** Sequence of enumerations */
305: private static final class SeqEn<T> extends Object implements
306: Enumeration<T> {
307: /** enumeration of Enumerations */
308: private Enumeration<? extends Enumeration<? extends T>> en;
309:
310: /** current enumeration */
311: private Enumeration<? extends T> current;
312:
313: /** is {@link #current} up-to-date and has more elements?
314: * The combination <CODE>current == null</CODE> and
315: * <CODE>checked == true means there are no more elements
316: * in this enumeration.
317: */
318: private boolean checked = false;
319:
320: /** Constructs new enumeration from already existing. The elements
321: * of <CODE>en</CODE> should be also enumerations. The resulting
322: * enumeration contains elements of such enumerations.
323: *
324: * @param en enumeration of Enumerations that should be sequenced
325: */
326: public SeqEn(Enumeration<? extends Enumeration<? extends T>> en) {
327: this .en = en;
328: }
329:
330: /** Ensures that current enumeration is set. If there aren't more
331: * elements in the Enumerations, sets the field <CODE>current</CODE> to null.
332: */
333: private void ensureCurrent() {
334: while ((current == null) || !current.hasMoreElements()) {
335: if (en.hasMoreElements()) {
336: current = en.nextElement();
337: } else {
338: // no next valid enumeration
339: current = null;
340:
341: return;
342: }
343: }
344: }
345:
346: /** @return true if we have more elements */
347: public boolean hasMoreElements() {
348: if (!checked) {
349: ensureCurrent();
350: checked = true;
351: }
352:
353: return current != null;
354: }
355:
356: /** @return next element
357: * @exception NoSuchElementException if there is no next element
358: */
359: public T nextElement() {
360: if (!checked) {
361: ensureCurrent();
362: }
363:
364: if (current != null) {
365: checked = false;
366:
367: return current.nextElement();
368: } else {
369: checked = true;
370: throw new java.util.NoSuchElementException();
371: }
372: }
373: }
374:
375: // end of SeqEn
376:
377: /** QueueEnumeration
378: */
379: private static class QEn<T, R> extends Object implements
380: Enumeration<R> {
381: /** next object to be returned */
382: private ListItem<T> next = null;
383:
384: /** last object in the queue */
385: private ListItem<T> last = null;
386:
387: /** processor to use */
388: private Processor<T, R> processor;
389:
390: public QEn(Processor<T, R> p) {
391: this .processor = p;
392: }
393:
394: /** Put adds new object to the end of queue.
395: * @param o the object to add
396: */
397: public void put(T o) {
398: if (last != null) {
399: ListItem<T> li = new ListItem<T>(o);
400: last.next = li;
401: last = li;
402: } else {
403: next = last = new ListItem<T>(o);
404: }
405: }
406:
407: /** Adds array of objects into the queue.
408: * @param arr array of objects to put into the queue
409: */
410: public void put(Collection<? extends T> arr) {
411: for (T e : arr) {
412: put(e);
413: }
414: }
415:
416: /** Is there any next object?
417: * @return true if there is next object, false otherwise
418: */
419: public boolean hasMoreElements() {
420: return next != null;
421: }
422:
423: /** @return next object in enumeration
424: * @exception NoSuchElementException if there is no next object
425: */
426: public R nextElement() {
427: if (next == null) {
428: throw new NoSuchElementException();
429: }
430:
431: T res = next.object;
432:
433: if ((next = next.next) == null) {
434: last = null;
435: }
436:
437: ;
438:
439: ToAdd<T, R> toAdd = new ToAdd<T, R>(this );
440: R out = processor.process(res, toAdd);
441: toAdd.finish();
442:
443: return out;
444: }
445:
446: /** item in linked list of Objects */
447: private static final class ListItem<T> {
448: T object;
449: ListItem<T> next;
450:
451: /** @param o the object for this item */
452: ListItem(T o) {
453: object = o;
454: }
455: }
456:
457: /** Temporary collection that supports only add and addAll operations*/
458: private static final class ToAdd<T, R> extends Object implements
459: Collection<T> {
460: private QEn<T, R> q;
461:
462: public ToAdd(QEn<T, R> q) {
463: this .q = q;
464: }
465:
466: public void finish() {
467: this .q = null;
468: }
469:
470: public boolean add(T o) {
471: q.put(o);
472:
473: return true;
474: }
475:
476: public boolean addAll(Collection<? extends T> c) {
477: q.put(c);
478:
479: return true;
480: }
481:
482: private String msg() {
483: return "Only add and addAll are implemented"; // NOI18N
484: }
485:
486: public void clear() {
487: throw new UnsupportedOperationException(msg());
488: }
489:
490: public boolean contains(Object o) {
491: throw new UnsupportedOperationException(msg());
492: }
493:
494: public boolean containsAll(Collection c) {
495: throw new UnsupportedOperationException(msg());
496: }
497:
498: public boolean isEmpty() {
499: throw new UnsupportedOperationException(msg());
500: }
501:
502: public Iterator<T> iterator() {
503: throw new UnsupportedOperationException(msg());
504: }
505:
506: public boolean remove(Object o) {
507: throw new UnsupportedOperationException(msg());
508: }
509:
510: public boolean removeAll(Collection c) {
511: throw new UnsupportedOperationException(msg());
512: }
513:
514: public boolean retainAll(Collection c) {
515: throw new UnsupportedOperationException(msg());
516: }
517:
518: public int size() {
519: throw new UnsupportedOperationException(msg());
520: }
521:
522: public Object[] toArray() {
523: throw new UnsupportedOperationException(msg());
524: }
525:
526: public <X> X[] toArray(X[] a) {
527: throw new UnsupportedOperationException(msg());
528: }
529: }
530: // end of ToAdd
531: }
532:
533: // end of QEn
534:
535: /** Filtering enumeration */
536: private static final class FilEn<T, R> extends Object implements
537: Enumeration<R> {
538: /** marker object stating there is no nexte element prepared */
539: private static final Object EMPTY = new Object();
540:
541: /** enumeration to filter */
542: private Enumeration<? extends T> en;
543:
544: /** element to be returned next time or {@link #EMPTY} if there is
545: * no such element prepared */
546: private R next = empty();
547:
548: /** the set to use as filter */
549: private Processor<T, R> filter;
550:
551: /**
552: * @param en enumeration to filter
553: */
554: public FilEn(Enumeration<? extends T> en, Processor<T, R> filter) {
555: this .en = en;
556: this .filter = filter;
557: }
558:
559: /** @return true if there is more elements in the enumeration
560: */
561: public boolean hasMoreElements() {
562: if (next != empty()) {
563: // there is a object already prepared
564: return true;
565: }
566:
567: while (en.hasMoreElements()) {
568: // read next
569: next = filter.process(en.nextElement(), null);
570:
571: if (next != null) {
572: // if the object is accepted
573: return true;
574: }
575:
576: ;
577: }
578:
579: next = empty();
580:
581: return false;
582: }
583:
584: /** @return next object in the enumeration
585: * @exception NoSuchElementException can be thrown if there is no next object
586: * in the enumeration
587: */
588: public R nextElement() {
589: if ((next == EMPTY) && !hasMoreElements()) {
590: throw new NoSuchElementException();
591: }
592:
593: R res = next;
594: next = empty();
595:
596: return res;
597: }
598:
599: @SuppressWarnings("unchecked")
600: private R empty() {
601: return (R) EMPTY;
602: }
603: }
604:
605: // end of FilEn
606:
607: /** Returns true from contains if object is not null */
608: private static class RNulls<T> implements Processor<T, T> {
609: public T process(T original, Collection<T> toAdd) {
610: return original;
611: }
612: }
613: // end of RNulls
614: }
|