KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > openide > util > NbCollections


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.openide.util;
21
22 import java.io.Serializable JavaDoc;
23 import java.util.AbstractMap JavaDoc;
24 import java.util.AbstractSet JavaDoc;
25 import java.util.ArrayList JavaDoc;
26 import java.util.Collection JavaDoc;
27 import java.util.Enumeration JavaDoc;
28 import java.util.HashMap JavaDoc;
29 import java.util.HashSet JavaDoc;
30 import java.util.Iterator JavaDoc;
31 import java.util.LinkedList JavaDoc;
32 import java.util.List JavaDoc;
33 import java.util.Map JavaDoc;
34 import java.util.NoSuchElementException JavaDoc;
35 import java.util.RandomAccess JavaDoc;
36 import java.util.Set JavaDoc;
37 import java.util.logging.Level JavaDoc;
38 import java.util.logging.Logger JavaDoc;
39
40 /**
41  * Utilities for working with generics.
42  * <p>Note that there is no <code>checkedListByFilter</code> method currently.
43  * If constant-time operation is important (e.g. your raw list is large and {@link RandomAccess})
44  * you can use {@link #checkedListByCopy}, assuming you do not need to modify the underlying list.
45  * If you are only interested in an iterator anyway, try {@link #checkedIteratorByFilter}.
46  * @author Jesse Glick
47  * @since org.openide.util 7.1
48  */

49 public class NbCollections {
50
51     private NbCollections() {}
52     
53     private static final Logger JavaDoc LOG = Logger.getLogger(NbCollections.class.getName());
54
55     /**
56      * Create a typesafe copy of a raw set.
57      * @param rawSet an unchecked set
58      * @param type the desired supertype of the entries
59      * @param strict true to throw a <code>ClassCastException</code> if the raw set has an invalid entry,
60      * false to skip over such entries (warnings may be logged)
61      * @return a typed set guaranteed to contain only entries assignable
62      * to the named type (or they may be null)
63      * @throws ClassCastException if some entry in the raw set was not well-typed, and only if <code>strict</code> was true
64      */

65     public static <E> Set JavaDoc<E> checkedSetByCopy(Set JavaDoc rawSet, Class JavaDoc<E> type, boolean strict) throws ClassCastException JavaDoc {
66         Set JavaDoc<E> s = new HashSet JavaDoc<E>(rawSet.size() * 4 / 3 + 1);
67         Iterator JavaDoc it = rawSet.iterator();
68         while (it.hasNext()) {
69             Object JavaDoc e = it.next();
70             try {
71                 s.add(type.cast(e));
72             } catch (ClassCastException JavaDoc x) {
73                 if (strict) {
74                     throw x;
75                 } else {
76                     LOG.log(Level.WARNING, "Element {0} not assignable to {1}", new Object JavaDoc[] {e, type});
77                 }
78             }
79         }
80         return s;
81     }
82
83     /**
84      * Create a typesafe copy of a raw list.
85      * @param rawList an unchecked list
86      * @param type the desired supertype of the entries
87      * @param strict true to throw a <code>ClassCastException</code> if the raw list has an invalid entry,
88      * false to skip over such entries (warnings may be logged)
89      * @return a typed list guaranteed to contain only entries assignable
90      * to the named type (or they may be null)
91      * @throws ClassCastException if some entry in the raw list was not well-typed, and only if <code>strict</code> was true
92      */

93     public static <E> List JavaDoc<E> checkedListByCopy(List JavaDoc rawList, Class JavaDoc<E> type, boolean strict) throws ClassCastException JavaDoc {
94         List JavaDoc<E> l = (rawList instanceof RandomAccess JavaDoc) ? new ArrayList JavaDoc<E>(rawList.size()) : new LinkedList JavaDoc<E>();
95         Iterator JavaDoc it = rawList.iterator();
96         while (it.hasNext()) {
97             Object JavaDoc e = it.next();
98             try {
99                 l.add(type.cast(e));
100             } catch (ClassCastException JavaDoc x) {
101                 if (strict) {
102                     throw x;
103                 } else {
104                     LOG.log(Level.WARNING, "Element {0} not assignable to {1}", new Object JavaDoc[] {e, type});
105                 }
106             }
107         }
108         return l;
109     }
110
111     /**
112      * Create a typesafe copy of a raw map.
113      * @param rawMap an unchecked map
114      * @param keyType the desired supertype of the keys
115      * @param valueType the desired supertype of the values
116      * @param strict true to throw a <code>ClassCastException</code> if the raw map has an invalid key or value,
117      * false to skip over such map entries (warnings may be logged)
118      * @return a typed map guaranteed to contain only keys and values assignable
119      * to the named types (or they may be null)
120      * @throws ClassCastException if some key or value in the raw map was not well-typed, and only if <code>strict</code> was true
121      */

122     public static <K,V> Map JavaDoc<K,V> checkedMapByCopy(Map JavaDoc rawMap, Class JavaDoc<K> keyType, Class JavaDoc<V> valueType, boolean strict) throws ClassCastException JavaDoc {
123         Map JavaDoc<K,V> m2 = new HashMap JavaDoc<K,V>(rawMap.size() * 4 / 3 + 1);
124         Iterator JavaDoc it = rawMap.entrySet().iterator();
125         while (it.hasNext()) {
126             Map.Entry JavaDoc e = (Map.Entry JavaDoc) it.next();
127             try {
128                 m2.put(keyType.cast(e.getKey()), valueType.cast(e.getValue()));
129             } catch (ClassCastException JavaDoc x) {
130                 if (strict) {
131                     throw x;
132                 } else {
133                     LOG.log(Level.WARNING, "Entry {0} not assignable to <{1},{2}>", new Object JavaDoc[] {e, keyType, valueType});
134                 }
135             }
136         }
137         return m2;
138     }
139
140     private static abstract class CheckedIterator<E> implements Iterator JavaDoc<E> {
141
142         private static final Object JavaDoc WAITING = new Object JavaDoc();
143
144         private final Iterator JavaDoc it;
145         private Object JavaDoc next = WAITING;
146
147         public CheckedIterator(Iterator JavaDoc it) {
148             this.it = it;
149         }
150
151         protected abstract boolean accept(Object JavaDoc o);
152
153         public boolean hasNext() {
154             if (next != WAITING) {
155                 return true;
156             }
157             while (it.hasNext()) {
158                 next = it.next();
159                 if (accept(next)) {
160                     return true;
161                 }
162             }
163             next = WAITING;
164             return false;
165         }
166
167         public E next() {
168             if (next == WAITING && !hasNext()) {
169                 throw new NoSuchElementException JavaDoc();
170             }
171             assert next != WAITING;
172             @SuppressWarnings JavaDoc("unchecked") // type-checking is done by accept()
173
E x = (E) next;
174             next = WAITING;
175             return x;
176         }
177
178         public void remove() {
179             it.remove();
180         }
181
182     }
183
184     /**
185      * Create a typesafe filter of an unchecked iterator.
186      * {@link Iterator#remove} will work if it does in the unchecked iterator.
187      * @param rawIterator an unchecked iterator
188      * @param type the desired enumeration type
189      * @param strict if false, elements which are not null but not assignable to the requested type are omitted;
190      * if true, {@link ClassCastException} may be thrown from an iterator operation
191      * @return an iterator guaranteed to contain only objects of the requested type (or null)
192      */

193     public static <E> Iterator JavaDoc<E> checkedIteratorByFilter(Iterator JavaDoc rawIterator, final Class JavaDoc<E> type, final boolean strict) {
194         return new CheckedIterator<E>(rawIterator) {
195             protected boolean accept(Object JavaDoc o) {
196                 if (o == null) {
197                     return true;
198                 } else if (type.isInstance(o)) {
199                     return true;
200                 } else if (strict) {
201                     throw new ClassCastException JavaDoc(o + " was not a " + type.getName()); // NOI18N
202
} else {
203                     return false;
204                 }
205             }
206         };
207     }
208
209     /**
210      * Create a typesafe view over an underlying raw set.
211      * Mutations affect the underlying set (this is not a copy).
212      * {@link Set#clear} will make the view empty but may not clear the underlying set.
213      * You may add elements only of the requested type.
214      * {@link Set#contains} also performs a type check and will throw {@link ClassCastException}
215      * for an illegal argument.
216      * The view is serializable if the underlying set is.
217      * @param rawSet an unchecked set
218      * @param type the desired element type
219      * @param strict if false, elements in the underlying set which are not null and which are not assignable
220      * to the requested type are omitted from the view;
221      * if true, a {@link ClassCastException} may arise during some set operation
222      * @return a view over the raw set guaranteed to match the specified type
223      */

224     public static <E> Set JavaDoc<E> checkedSetByFilter(Set JavaDoc rawSet, Class JavaDoc<E> type, boolean strict) {
225         return new CheckedSet<E>(rawSet, type, strict);
226     }
227     private static final class CheckedSet<E> extends AbstractSet JavaDoc<E> implements Serializable JavaDoc {
228
229         private static final long serialVersionUID = 1L;
230
231         private final Set JavaDoc rawSet;
232         private final Class JavaDoc<E> type;
233         private final boolean strict;
234
235         public CheckedSet(Set JavaDoc rawSet, Class JavaDoc<E> type, boolean strict) {
236             this.rawSet = rawSet;
237             this.type = type;
238             this.strict = strict;
239         }
240
241         private boolean acceptEntry(Object JavaDoc o) {
242             if (o == null) {
243                 return true;
244             } else if (type.isInstance(o)) {
245                 return true;
246             } else if (strict) {
247                 throw new ClassCastException JavaDoc(o + " was not a " + type.getName()); // NOI18N
248
} else {
249                 return false;
250             }
251         }
252
253         @Override JavaDoc
254         public Iterator JavaDoc<E> iterator() {
255             return new CheckedIterator<E>(rawSet.iterator()) {
256                 @Override JavaDoc
257                 protected boolean accept(Object JavaDoc o) {
258                     return acceptEntry(o);
259                 }
260             };
261         }
262
263         @Override JavaDoc
264         public int size() {
265             int c = 0;
266             Iterator JavaDoc it = rawSet.iterator();
267             while (it.hasNext()) {
268                 if (acceptEntry(it.next())) {
269                     c++;
270                 }
271             }
272             return c;
273         }
274
275         @Override JavaDoc
276         @SuppressWarnings JavaDoc("unchecked") // complains about usage of raw set
277
public boolean add(E o) {
278             return rawSet.add(type.cast(o));
279         }
280
281         @Override JavaDoc
282         public boolean contains(Object JavaDoc o) {
283             return rawSet.contains(type.cast(o));
284         }
285
286     }
287
288     /**
289      * Create a typesafe view over an underlying raw map.
290      * Mutations affect the underlying map (this is not a copy).
291      * {@link Map#clear} will make the view empty but may not clear the underlying map.
292      * You may add entries only of the requested type pair.
293      * {@link Map#get}, {@link Map#containsKey}, and {@link Map#containsValue} also perform a type check
294      * and will throw {@link ClassCastException} for an illegal argument.
295      * The view is serializable if the underlying map is.
296      * @param rawMap an unchecked map
297      * @param keyType the desired entry key type
298      * @param valueType the desired entry value type
299      * @param strict if false, entries in the underlying map for which the key is not null but not assignable
300      * to the requested key type, and/or the value is not null but not assignable to
301      * the requested value type, are omitted from the view;
302      * if true, a {@link ClassCastException} may arise during some map operation
303      * @return a view over the raw map guaranteed to match the specified type
304      */

305     public static <K,V> Map JavaDoc<K,V> checkedMapByFilter(Map JavaDoc rawMap, Class JavaDoc<K> keyType, Class JavaDoc<V> valueType, boolean strict) {
306         return new CheckedMap<K,V>(rawMap, keyType, valueType, strict);
307     }
308     private static final class CheckedMap<K,V> extends AbstractMap JavaDoc<K,V> implements Serializable JavaDoc {
309
310         private static final long serialVersionUID = 1L;
311
312         private final Map JavaDoc rawMap;
313         private final Class JavaDoc<K> keyType;
314         private final Class JavaDoc<V> valueType;
315         private final boolean strict;
316
317         public CheckedMap(Map JavaDoc rawMap, Class JavaDoc<K> keyType, Class JavaDoc<V> valueType, boolean strict) {
318             this.rawMap = rawMap;
319             this.keyType = keyType;
320             this.valueType = valueType;
321             this.strict = strict;
322         }
323
324         private boolean acceptKey(Object JavaDoc o) {
325             if (o == null) {
326                 return true;
327             } else if (keyType.isInstance(o)) {
328                 return true;
329             } else if (strict) {
330                 throw new ClassCastException JavaDoc(o + " was not a " + keyType.getName()); // NOI18N
331
} else {
332                 return false;
333             }
334         }
335
336         private boolean acceptValue(Object JavaDoc o) {
337             if (o == null) {
338                 return true;
339             } else if (valueType.isInstance(o)) {
340                 return true;
341             } else if (strict) {
342                 throw new ClassCastException JavaDoc(o + " was not a " + valueType.getName()); // NOI18N
343
} else {
344                 return false;
345             }
346         }
347
348         private boolean acceptEntry(Map.Entry JavaDoc e) {
349             return acceptKey(e.getKey()) && acceptValue(e.getValue());
350         }
351
352         private final class EntrySet extends AbstractSet JavaDoc<Map.Entry JavaDoc<K, V>> {
353
354             @Override JavaDoc
355             public Iterator JavaDoc<Map.Entry JavaDoc<K,V>> iterator() {
356                 return new CheckedIterator<Map.Entry JavaDoc<K,V>>(rawMap.entrySet().iterator()) {
357                     @Override JavaDoc
358                     protected boolean accept(Object JavaDoc o) {
359                         return acceptEntry((Map.Entry JavaDoc) o);
360                     }
361                 };
362             }
363
364             @Override JavaDoc
365             public int size() {
366                 int c = 0;
367                 Iterator JavaDoc it = rawMap.entrySet().iterator();
368                 while (it.hasNext()) {
369                     if (acceptEntry((Map.Entry JavaDoc) it.next())) {
370                         c++;
371                     }
372                 }
373                 return c;
374             }
375
376         }
377         @Override JavaDoc
378         public Set JavaDoc<Map.Entry JavaDoc<K, V>> entrySet() {
379             return new EntrySet();
380         }
381
382         @Override JavaDoc
383         public V get(Object JavaDoc key) {
384             Object JavaDoc o = rawMap.get(keyType.cast(key));
385             if (acceptValue(o)) {
386                 @SuppressWarnings JavaDoc("unchecked")
387                 V v = (V) o;
388                 return v;
389             } else {
390                 return null;
391             }
392         }
393
394         @SuppressWarnings JavaDoc("unchecked")
395         @Override JavaDoc
396         public V put(K key, V value) {
397             Object JavaDoc old = rawMap.put(keyType.cast(key), valueType.cast(value));
398             if (acceptValue(old)) {
399                 return (V) old;
400             } else {
401                 return null;
402             }
403         }
404
405         @Override JavaDoc
406         public V remove(Object JavaDoc key) {
407             Object JavaDoc old = rawMap.remove(keyType.cast(key));
408             if (acceptValue(old)) {
409                 @SuppressWarnings JavaDoc("unchecked")
410                 V v = (V) old;
411                 return v;
412             } else {
413                 return null;
414             }
415         }
416
417         @Override JavaDoc
418         public boolean containsKey(Object JavaDoc key) {
419             return rawMap.containsKey(keyType.cast(key)) &&
420                     acceptValue(rawMap.get(key));
421         }
422
423         @Override JavaDoc
424         public boolean containsValue(Object JavaDoc value) {
425             // Cannot just ask rawMap since we could not check type of key.
426
return super.containsValue(valueType.cast(value));
427         }
428
429         @Override JavaDoc
430         public int size() {
431             int c = 0;
432             Iterator JavaDoc it = rawMap.entrySet().iterator();
433             while (it.hasNext()) {
434                 if (acceptEntry((Map.Entry JavaDoc) it.next())) {
435                     c++;
436                 }
437             }
438             return c;
439         }
440
441         // keySet, values cannot be so easily overridden because we type-check whole entries
442

443     }
444
445     /**
446      * Create a typesafe filter of an unchecked enumeration.
447      * @param rawEnum an unchecked enumeration
448      * @param type the desired enumeration type
449      * @param strict if false, elements which are not null but not assignable to the requested type are omitted;
450      * if true, {@link ClassCastException} may be thrown from an enumeration operation
451      * @return an enumeration guaranteed to contain only objects of the requested type (or null)
452      */

453     public static <E> Enumeration JavaDoc<E> checkedEnumerationByFilter(Enumeration JavaDoc rawEnum, final Class JavaDoc<E> type, final boolean strict) {
454         @SuppressWarnings JavaDoc("unchecked")
455         Enumeration JavaDoc<Object JavaDoc> _rawEnum = rawEnum;
456         return Enumerations.<Object JavaDoc,E>filter(_rawEnum, new Enumerations.Processor<Object JavaDoc,E>() {
457             public E process(Object JavaDoc o, Collection JavaDoc<Object JavaDoc> ignore) {
458                 if (o == null) {
459                     return null;
460                 } else {
461                     try {
462                         return type.cast(o);
463                     } catch (ClassCastException JavaDoc x) {
464                         if (strict) {
465                             throw x;
466                         } else {
467                             return null;
468                         }
469                     }
470                 }
471             }
472         });
473     }
474
475     /**
476      * Treat an {@link Iterator} as an {@link Iterable} so it can be used in an enhanced for-loop.
477      * Bear in mind that the iterator is "consumed" by the loop and so should be used only once.
478      * Generally it is best to put the code which obtains the iterator inside the loop header.
479      * <div class="nonnormative">
480      * <p>Example of correct usage:</p>
481      * <pre>
482      * String text = ...;
483      * for (String token : NbCollections.iterable(new {@link java.util.Scanner}(text))) {
484      * // ...
485      * }
486      * </pre>
487      * </div>
488      * @param iterator an iterator
489      * @return an iterable wrapper which will traverse the iterator once
490      * @throws NullPointerException if the iterator is null
491      * @see <a HREF="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6312085">Java bug #6312085</a>
492      * @see <a HREF="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6360734">Java bug #6360734</a>
493      * @see <a HREF="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4988624">Java bug #4988624</a>
494      * @since org.openide.util 7.5
495      */

496     public static <E> Iterable JavaDoc<E> iterable(final Iterator JavaDoc<E> iterator) {
497         if (iterator == null) {
498             throw new NullPointerException JavaDoc();
499         }
500         return new Iterable JavaDoc<E>() {
501             public Iterator JavaDoc<E> iterator() {
502                 return iterator;
503             }
504         };
505     }
506
507     /**
508      * Treat an {@link Enumeration} as an {@link Iterable} so it can be used in an enhanced for-loop.
509      * Bear in mind that the enumeration is "consumed" by the loop and so should be used only once.
510      * Generally it is best to put the code which obtains the enumeration inside the loop header.
511      * <div class="nonnormative">
512      * <p>Example of correct usage:</p>
513      * <pre>
514      * ClassLoader loader = ...;
515      * String name = ...;
516      * for (URL resource : NbCollections.iterable(loader.{@link ClassLoader#getResources getResources}(name))) {
517      * // ...
518      * }
519      * </pre>
520      * </div>
521      * @param enumeration an enumeration
522      * @return an iterable wrapper which will traverse the enumeration once
523      * ({@link Iterator#remove} is not supported)
524      * @throws NullPointerException if the enumeration is null
525      * @see <a HREF="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6349852">Java bug #6349852</a>
526      * @since org.openide.util 7.5
527      */

528     public static <E> Iterable JavaDoc<E> iterable(final Enumeration JavaDoc<E> enumeration) {
529         if (enumeration == null) {
530             throw new NullPointerException JavaDoc();
531         }
532         return new Iterable JavaDoc<E>() {
533             public Iterator JavaDoc<E> iterator() {
534                 return new Iterator JavaDoc<E>() {
535                     public boolean hasNext() {
536                         return enumeration.hasMoreElements();
537                     }
538                     public E next() {
539                         return enumeration.nextElement();
540                     }
541                     public void remove() {
542                         throw new UnsupportedOperationException JavaDoc();
543                     }
544                 };
545             }
546         };
547     }
548
549 }
550
Popular Tags