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