Added
Link Here
|
1 |
/* |
2 |
* Sun Public License Notice |
3 |
* |
4 |
* The contents of this file are subject to the Sun Public License |
5 |
* Version 1.0 (the "License"). You may not use this file except in |
6 |
* compliance with the License. A copy of the License is available at |
7 |
* http://www.sun.com/ |
8 |
* |
9 |
* The Original Code is NetBeans. The Initial Developer of the Original |
10 |
* Code is Sun Microsystems, Inc. Portions Copyright 1997-2004 Sun |
11 |
* Microsystems, Inc. All Rights Reserved. |
12 |
*/ |
13 |
package org.openide.util; |
14 |
|
15 |
import java.util.*; |
16 |
import java.util.Enumeration; |
17 |
import java.util.Map; |
18 |
import java.util.Set; |
19 |
|
20 |
/** Factory methods for various types of java.util.Enumeration. Allows composition |
21 |
* of existing enumerations, filtering their content and/or modifying them. |
22 |
* All of this is designed to be done in lazy way - e.g. at the latest time |
23 |
* when needed. |
24 |
* |
25 |
* @since JST-PENDING |
26 |
* @author Jaroslav Tulach |
27 |
*/ |
28 |
public final class Enumerations extends Object { |
29 |
/** No instances */ |
30 |
private Enumerations () { |
31 |
} |
32 |
|
33 |
/** An empty enumeration. Always returns <code>false</code> when |
34 |
* <code>EMPTY.hasMoreElements()</code> and throws <code>NoSuchElementException</code> |
35 |
* from the <code>EMPTY.nextElement()</code> method. |
36 |
*/ |
37 |
public static final Enumeration EMPTY = Collections.enumeration (Collections.EMPTY_LIST); |
38 |
|
39 |
/** Creates enumeration with one element. |
40 |
* @param obj the element to be present in the enumeration. |
41 |
* @return enumeration |
42 |
*/ |
43 |
public static Enumeration singleton (Object obj) { |
44 |
return Collections.enumeration (Collections.singleton (obj)); |
45 |
} |
46 |
/** Concatenates the content of two enumerations into one. Until the |
47 |
* end of <code>en1</code> is reached its elements are being served. |
48 |
* As soon as the <code>en1</code> has no more elements, the content |
49 |
* of <code>en2</code> is being returned. |
50 |
* |
51 |
* @param en1 first enumeration |
52 |
* @param en2 second enumeration |
53 |
* @return enumeration |
54 |
*/ |
55 |
public static Enumeration concat (Enumeration en1, Enumeration en2) { |
56 |
return new SeqEn (en1, en2); |
57 |
} |
58 |
/** Concatenates the content of many enumerations. The input value |
59 |
* is enumeration of Enumeration elements and the result is composed |
60 |
* all their content. Each of the provided enumeration is fully read |
61 |
* and their content returned before the next enumeration is asked for |
62 |
* their elements. |
63 |
* |
64 |
* @param enumOfEnums Enumeration of Enumeration elements |
65 |
* @return enumeration |
66 |
*/ |
67 |
public static Enumeration concat (Enumeration enumOfEnums) { |
68 |
return new SeqEn (enumOfEnums); |
69 |
} |
70 |
/** Filters the input enumeration to new one that should contain |
71 |
* each of the provided elements just once. The elements are compared |
72 |
* using their default <code>equals</code> and <code>hashCode</code> methods. |
73 |
* |
74 |
* @param en enumeration to filter |
75 |
* @return enumeration without duplicated items |
76 |
*/ |
77 |
public static Enumeration removeDuplicates (Enumeration en) { |
78 |
class RDupls implements Processor { |
79 |
private Set set = new HashSet (); |
80 |
public Object process (Object o, Collection nothing) { |
81 |
return set.add (o) ? o : null; |
82 |
} |
83 |
} |
84 |
return filter (en, new RDupls ()); |
85 |
} |
86 |
|
87 |
/** Returns an enumeration that iterates over provided array. |
88 |
* @param arr the array of object |
89 |
* @return enumeration of those objects |
90 |
*/ |
91 |
public static Enumeration array (Object[] arr) { |
92 |
return Collections.enumeration (Arrays.asList (arr)); |
93 |
} |
94 |
/** Removes all <code>null</code>s from the input enumeration |
95 |
* @param en enumeration that can contain nulls |
96 |
* @return new enumeration without null values |
97 |
*/ |
98 |
public static Enumeration removeNulls (Enumeration en) { |
99 |
return filter (en, new RNulls()); |
100 |
} |
101 |
|
102 |
/** For each element of the input enumeration <code>en</code> asks the |
103 |
* {@link Processor} to provide a replacement. The <code>toAdd</code> |
104 |
* argument of the processor is always null. |
105 |
* <p> |
106 |
* Example to convert any objects into strings: |
107 |
* <pre> |
108 |
* Processor convertToString = new Process () { |
109 |
* public Object process (Object obj, Collection toAdd) { // toAdd is always null |
110 |
* return obj.toString (): // converts to string |
111 |
* } |
112 |
* }; |
113 |
* Enumeration strings = Enumerations.convert (elems, convertToString); |
114 |
* </pre> |
115 |
* |
116 |
* @param en enumeration of any objects |
117 |
* @param processor a callback processor for the elements (its toAdd arguments is always null) |
118 |
* @return new enumeration where all elements has been processed |
119 |
*/ |
120 |
public static Enumeration convert (Enumeration en, Processor processor) { |
121 |
return new AltEn (en, processor); |
122 |
} |
123 |
/** Allows to filter out some elements from the input enumeration. Just |
124 |
* make the |
125 |
* {@link Processor} return <code>null</code>. Please notice the <code>toAdd</code> |
126 |
* argument of the processor is always <code>null</code>. |
127 |
* <p> |
128 |
* Example to remove all objects that are not strings: |
129 |
* <pre> |
130 |
* Processor onlyString = new Processor () { |
131 |
* public Object process (Object obj, Collection alwaysNull) { |
132 |
* if (obj instanceof String) { |
133 |
* return obj; |
134 |
* } else { |
135 |
* return null; |
136 |
* } |
137 |
* } |
138 |
* }; |
139 |
* Enumeration strings = Enumerations.filter (elems, onlyString); |
140 |
* </pre> |
141 |
* |
142 |
* @param en enumeration of any objects |
143 |
* @param filter a callback processor for the elements (its toAdd arguments is always null) |
144 |
* @return new enumeration which does not include non-processed (returned null from processor) elements |
145 |
*/ |
146 |
public static Enumeration filter (Enumeration en, Processor filter) { |
147 |
return new FilEn (en, filter); |
148 |
} |
149 |
|
150 |
/** Support for breadth-first enumerating. Before any element is returned |
151 |
* for the resulting enumeration it is processed in the {@link Processor} and |
152 |
* the processor is allowed to modify it and also add additional elements |
153 |
* at the (current) end of the <q>queue</q> by calling <code>toAdd.add</code> |
154 |
* or <code>toAdd.addAll</code>. No other methods can be called on the |
155 |
* provided <code>toAdd</code> collection. |
156 |
* <p> |
157 |
* Example of doing breadth-first walk through a tree: |
158 |
* <pre> |
159 |
* Processor queueSubnodes = new Process () { |
160 |
* public Object process (Object obj, Collection toAdd) { |
161 |
* Node n = (Node)obj; |
162 |
* toAdd.addAll (n.getChildrenList ()); |
163 |
* return n; |
164 |
* } |
165 |
* }; |
166 |
* Enumeration strings = Enumerations.queue (elems, queueSubnodes); |
167 |
* </pre> |
168 |
* |
169 |
* @param en initial content of the resulting enumeration |
170 |
* @param filter the processor that is called for each element and can |
171 |
* add and addAll elements to its toAdd Collection argument |
172 |
* @return enumeration with the initial and queued content |
173 |
*/ |
174 |
public static Enumeration queue (Enumeration en, Processor filter) { |
175 |
QEn q = new QEn (filter); |
176 |
while (en.hasMoreElements ()) { |
177 |
q.put (en.nextElement ()); |
178 |
} |
179 |
return q; |
180 |
} |
181 |
|
182 |
/** Processor interface that can filter out objects from the enumeration, |
183 |
* change them or add aditional objects to the end of the current enumeration. |
184 |
*/ |
185 |
public static interface Processor { |
186 |
/** @param original the object that is going to be returned from the enumeration right now |
187 |
* @return a replacement for this object |
188 |
* @param toAdd can be non-null if one can add new objects at the end of the enumeration |
189 |
*/ |
190 |
public Object process (Object original, Collection toAdd); |
191 |
} |
192 |
|
193 |
|
194 |
/** Altering enumeration implementation */ |
195 |
private static final class AltEn extends Object implements Enumeration { |
196 |
/** enumeration to filter */ |
197 |
private Enumeration en; |
198 |
/** map to alter */ |
199 |
private Processor process; |
200 |
|
201 |
/** |
202 |
* @param en enumeration to filter |
203 |
*/ |
204 |
public AltEn (Enumeration en, Processor process) { |
205 |
this.en = en; |
206 |
this.process = process; |
207 |
} |
208 |
|
209 |
/** @return true if there is more elements in the enumeration |
210 |
*/ |
211 |
public boolean hasMoreElements () { |
212 |
return en.hasMoreElements (); |
213 |
} |
214 |
|
215 |
/** @return next object in the enumeration |
216 |
* @exception NoSuchElementException can be thrown if there is no next object |
217 |
* in the enumeration |
218 |
*/ |
219 |
public Object nextElement () { |
220 |
return process.process (en.nextElement (), null); |
221 |
} |
222 |
} // end of AltEn |
223 |
|
224 |
/** Sequence of enumerations */ |
225 |
private static final class SeqEn extends Object implements Enumeration { |
226 |
/** enumeration of Enumerations */ |
227 |
private Enumeration en; |
228 |
/** current enumeration */ |
229 |
private Enumeration current; |
230 |
|
231 |
/** is {@link #current} up-to-date and has more elements? |
232 |
* The combination <CODE>current == null</CODE> and |
233 |
* <CODE>checked == true means there are no more elements |
234 |
* in this enumeration. |
235 |
*/ |
236 |
private boolean checked = false; |
237 |
|
238 |
/** Constructs new enumeration from already existing. The elements |
239 |
* of <CODE>en</CODE> should be also enumerations. The resulting |
240 |
* enumeration contains elements of such enumerations. |
241 |
* |
242 |
* @param en enumeration of Enumerations that should be sequenced |
243 |
*/ |
244 |
public SeqEn (Enumeration en) { |
245 |
this.en = en; |
246 |
} |
247 |
|
248 |
/** Composes two enumerations into one. |
249 |
* @param first first enumeration |
250 |
* @param second second enumeration |
251 |
*/ |
252 |
public SeqEn (Enumeration first, Enumeration second) { |
253 |
this (array (new Enumeration[] { first, second })); |
254 |
} |
255 |
|
256 |
/** Ensures that current enumeration is set. If there aren't more |
257 |
* elements in the Enumerations, sets the field <CODE>current</CODE> to null. |
258 |
*/ |
259 |
private void ensureCurrent () { |
260 |
while (current == null || !current.hasMoreElements ()) { |
261 |
if (en.hasMoreElements ()) { |
262 |
current = (Enumeration)en.nextElement (); |
263 |
} else { |
264 |
// no next valid enumeration |
265 |
current = null; |
266 |
return; |
267 |
} |
268 |
} |
269 |
} |
270 |
|
271 |
/** @return true if we have more elements */ |
272 |
public boolean hasMoreElements () { |
273 |
if( !checked ) { |
274 |
ensureCurrent (); |
275 |
checked = true; |
276 |
} |
277 |
return current != null; |
278 |
} |
279 |
|
280 |
/** @return next element |
281 |
* @exception NoSuchElementException if there is no next element |
282 |
*/ |
283 |
public synchronized Object nextElement () { |
284 |
if( !checked ) { |
285 |
ensureCurrent (); |
286 |
} |
287 |
if( current != null ) { |
288 |
checked = false; |
289 |
return current.nextElement (); |
290 |
} else { |
291 |
checked = true; |
292 |
throw new java.util.NoSuchElementException (); |
293 |
} |
294 |
} |
295 |
} // end of SeqEn |
296 |
|
297 |
/** QueueEnumeration |
298 |
*/ |
299 |
private static class QEn extends Object implements Enumeration { |
300 |
/** item in linked list of Objects */ |
301 |
private static final class ListItem { |
302 |
Object object; |
303 |
ListItem next; |
304 |
|
305 |
/** @param o the object for this item */ |
306 |
ListItem (Object o) { |
307 |
object = o; |
308 |
} |
309 |
} |
310 |
/** next object to be returned */ |
311 |
private ListItem next = null; |
312 |
/** last object in the queue */ |
313 |
private ListItem last = null; |
314 |
/** processor to use */ |
315 |
private Processor processor; |
316 |
|
317 |
public QEn (Processor p) { |
318 |
this.processor = p; |
319 |
} |
320 |
|
321 |
/** Put adds new object to the end of queue. |
322 |
* @param o the object to add |
323 |
*/ |
324 |
public synchronized void put (Object o) { |
325 |
if (last != null) { |
326 |
ListItem li = new ListItem (o); |
327 |
last.next = li; |
328 |
last = li; |
329 |
} else { |
330 |
next = last = new ListItem (o); |
331 |
} |
332 |
} |
333 |
|
334 |
/** Adds array of objects into the queue. |
335 |
* @param arr array of objects to put into the queue |
336 |
*/ |
337 |
public synchronized void put (Object[] arr) { |
338 |
for (int i = 0; i < arr.length; i++) { |
339 |
put (arr[i]); |
340 |
} |
341 |
} |
342 |
|
343 |
/** Is there any next object? |
344 |
* @return true if there is next object, false otherwise |
345 |
*/ |
346 |
public boolean hasMoreElements () { |
347 |
return next != null; |
348 |
} |
349 |
|
350 |
/** @return next object in enumeration |
351 |
* @exception NoSuchElementException if there is no next object |
352 |
*/ |
353 |
public synchronized Object nextElement () { |
354 |
if (next == null) { |
355 |
throw new NoSuchElementException (); |
356 |
} |
357 |
Object res = next.object; |
358 |
|
359 |
if ((next = next.next) == null) { |
360 |
last = null; |
361 |
}; |
362 |
ToAdd toAdd = new ToAdd (this); |
363 |
res = processor.process (res, toAdd); |
364 |
toAdd.finish (); |
365 |
return res; |
366 |
} |
367 |
|
368 |
/** Temporary collection that supports only add and addAll operations*/ |
369 |
private static final class ToAdd extends Object implements Collection { |
370 |
private QEn q; |
371 |
|
372 |
public ToAdd (QEn q) { |
373 |
this.q = q; |
374 |
} |
375 |
|
376 |
public void finish () { |
377 |
this.q = null; |
378 |
} |
379 |
|
380 |
public boolean add (Object o) { |
381 |
q.put (o); |
382 |
return true; |
383 |
} |
384 |
|
385 |
public boolean addAll (Collection c) { |
386 |
q.put (c.toArray ()); |
387 |
return true; |
388 |
} |
389 |
|
390 |
private String msg () { |
391 |
return "Only add and addAll are implemented"; // NOI18N |
392 |
} |
393 |
|
394 |
public void clear () { |
395 |
throw new IllegalStateException (msg ()); |
396 |
} |
397 |
public boolean contains (Object o) { |
398 |
throw new IllegalStateException (msg ()); |
399 |
} |
400 |
public boolean containsAll (Collection c) { |
401 |
throw new IllegalStateException (msg ()); |
402 |
} |
403 |
public boolean isEmpty () { |
404 |
throw new IllegalStateException (msg ()); |
405 |
} |
406 |
public Iterator iterator () { |
407 |
throw new IllegalStateException (msg ()); |
408 |
} |
409 |
public boolean remove (Object o) { |
410 |
throw new IllegalStateException (msg ()); |
411 |
} |
412 |
public boolean removeAll (Collection c) { |
413 |
throw new IllegalStateException (msg ()); |
414 |
} |
415 |
public boolean retainAll (Collection c) { |
416 |
throw new IllegalStateException (msg ()); |
417 |
} |
418 |
public int size () { |
419 |
throw new IllegalStateException (msg ()); |
420 |
} |
421 |
public Object[] toArray () { |
422 |
throw new IllegalStateException (msg ()); |
423 |
} |
424 |
public Object[] toArray (Object[] a) { |
425 |
throw new IllegalStateException (msg ()); |
426 |
} |
427 |
} // end of ToAdd |
428 |
} // end of QEn |
429 |
|
430 |
/** Filtering enumeration */ |
431 |
private static final class FilEn extends Object implements Enumeration { |
432 |
/** marker object stating there is no nexte element prepared */ |
433 |
private static final Object EMPTY = new Object(); |
434 |
|
435 |
/** enumeration to filter */ |
436 |
private Enumeration en; |
437 |
|
438 |
/** element to be returned next time or {@link #EMPTY} if there is |
439 |
* no such element prepared */ |
440 |
private Object next = EMPTY; |
441 |
|
442 |
/** the set to use as filter */ |
443 |
private Processor filter; |
444 |
|
445 |
/** |
446 |
* @param en enumeration to filter |
447 |
*/ |
448 |
public FilEn (Enumeration en, Processor filter) { |
449 |
this.en = en; |
450 |
this.filter = filter; |
451 |
} |
452 |
|
453 |
/** @return true if there is more elements in the enumeration |
454 |
*/ |
455 |
public boolean hasMoreElements () { |
456 |
if (next != EMPTY) { |
457 |
// there is a object already prepared |
458 |
return true; |
459 |
} |
460 |
while (en.hasMoreElements ()) { |
461 |
// read next |
462 |
next = filter.process (en.nextElement (), null); |
463 |
if (next != null) { |
464 |
// if the object is accepted |
465 |
return true; |
466 |
}; |
467 |
} |
468 |
next = EMPTY; |
469 |
return false; |
470 |
} |
471 |
|
472 |
/** @return next object in the enumeration |
473 |
* @exception NoSuchElementException can be thrown if there is no next object |
474 |
* in the enumeration |
475 |
*/ |
476 |
public Object nextElement () { |
477 |
if( next == EMPTY && !hasMoreElements() ) { |
478 |
throw new NoSuchElementException (); |
479 |
} |
480 |
Object res = next; |
481 |
next = EMPTY; |
482 |
return res; |
483 |
} |
484 |
} // end of FilEn |
485 |
|
486 |
/** Returns true from contains if object is not null */ |
487 |
private static class RNulls implements Processor { |
488 |
public Object process (Object original, Collection toAdd) { |
489 |
return original; |
490 |
} |
491 |
} // end of RNulls |
492 |
} |