001: /* CollectionsX.java
002:
003: {{IS_NOTE
004:
005: Purpose:
006: Description:
007: History:
008: 2001/09/15 13:46:20, Create, Tom M. Yeh.
009: }}IS_NOTE
010:
011: Copyright (C) 2001 Potix Corporation. All Rights Reserved.
012:
013: {{IS_RIGHT
014: This program is distributed under GPL Version 2.0 in the hope that
015: it will be useful, but WITHOUT ANY WARRANTY.
016: }}IS_RIGHT
017: */
018: package org.zkoss.util;
019:
020: import java.util.AbstractCollection;
021: import java.util.Collection;
022: import java.util.Collections;
023: import java.util.Set;
024: import java.util.Map;
025: import java.util.AbstractList;
026: import java.util.List;
027: import java.util.LinkedList;
028: import java.util.Iterator;
029: import java.util.ListIterator;
030: import java.util.Enumeration;
031: import java.util.NoSuchElementException;
032:
033: import org.zkoss.lang.Strings;
034:
035: /**
036: * The collection related utilities.
037: *
038: * @author tomyeh
039: * @see java.util.Collections
040: */
041: public class CollectionsX {
042: /** An enumeration on top of a collection or iterator.
043: */
044: public static final class CollectionEnumeration implements
045: Enumeration {
046: private final Iterator _iter;
047:
048: public CollectionEnumeration(Collection c) {
049: this (c != null ? c.iterator() : null);
050: }
051:
052: public CollectionEnumeration(Iterator iter) {
053: _iter = iter;
054: }
055:
056: public final boolean hasMoreElements() {
057: return _iter != null && _iter.hasNext();
058: }
059:
060: public final Object nextElement() {
061: if (_iter != null)
062: return _iter.next();
063: throw new NoSuchElementException();
064: }
065: }
066:
067: /** An enumeration on top of an array.
068: */
069: public static final class ArrayEnumeration implements Enumeration {
070: private final Object[] _ary;
071: private int _cursor = 0;
072:
073: public ArrayEnumeration(Object[] ary) {
074: _ary = ary;
075: }
076:
077: public final boolean hasMoreElements() {
078: return _ary != null && _cursor < _ary.length;
079: }
080:
081: public final Object nextElement() {
082: if (hasMoreElements())
083: return _ary[_cursor++];
084: throw new NoSuchElementException();
085: }
086: }
087:
088: /** An enumeration that enumerates one element.
089: */
090: public static final class OneEnumeration implements Enumeration {
091: private boolean _nomore;
092: private final Object _one;
093:
094: public OneEnumeration(Object one) {
095: _one = one;
096: }
097:
098: public final boolean hasMoreElements() {
099: return !_nomore;
100: }
101:
102: public final Object nextElement() {
103: if (_nomore)
104: throw new NoSuchElementException();
105: _nomore = true;
106: return _one;
107: }
108: }
109:
110: /** An readonly collection on top of an array.
111: */
112: public static final class ArrayCollection extends
113: AbstractCollection {
114: private final Object[] _ary;
115:
116: public ArrayCollection(Object[] ary) {
117: _ary = ary;
118: }
119:
120: public final int size() {
121: return _ary != null ? _ary.length : 0;
122: }
123:
124: public Iterator iterator() {
125: return new ArrayIterator(_ary);
126: }
127: }
128:
129: /** An readonly list on top of an array.
130: */
131: public static final class ArrayList extends AbstractList {
132: private final Object[] _ary;
133:
134: public ArrayList(Object[] ary) {
135: _ary = ary;
136: }
137:
138: public final int size() {
139: return _ary != null ? _ary.length : 0;
140: }
141:
142: public final Object get(int index) {
143: return _ary[index];
144: }
145: }
146:
147: /** An iterator on top of an array.
148: */
149: public static class ArrayIterator implements Iterator {
150: /*package*/final Object[] _ary;
151: /*package*/int _cursor = 0, _last = -1;
152:
153: /** @param ary an array or null. */
154: public ArrayIterator(Object[] ary) {
155: _ary = ary;
156: }
157:
158: public final boolean hasNext() {
159: return _ary != null && _cursor < _ary.length;
160: }
161:
162: public final Object next() {
163: if (hasNext())
164: return _ary[_last = _cursor++];
165: throw new NoSuchElementException("cursor=" + _cursor);
166: }
167:
168: public final void remove() {
169: throw new UnsupportedOperationException();
170: }
171: }
172:
173: public static class ArrayListIterator extends ArrayIterator
174: implements ListIterator {
175: /** @param ary an array or null. */
176: public ArrayListIterator(Object[] ary) {
177: super (ary);
178: }
179:
180: /** @param ary an array or null. */
181: public ArrayListIterator(Object[] ary, int index) {
182: super (ary);
183: _cursor = index;
184: final int len = _ary != null ? _ary.length : 0;
185: if (_cursor < 0 || _cursor > len)
186: throw new IndexOutOfBoundsException("index=" + index
187: + " but len=" + len);
188: }
189:
190: public final boolean hasPrevious() {
191: return _ary != null && _cursor > 0;
192: }
193:
194: public final Object previous() {
195: if (hasPrevious())
196: return _ary[_last = --_cursor];
197: throw new NoSuchElementException("cursor=" + _cursor);
198: }
199:
200: public final int nextIndex() {
201: return _cursor;
202: }
203:
204: public final int previousIndex() {
205: return _cursor - 1;
206: }
207:
208: public final void set(Object o) {
209: if (_last < 0)
210: throw new IllegalStateException(
211: "neither next nor previous have been called");
212: _ary[_last] = o;
213: }
214:
215: public final void add(Object o) {
216: throw new UnsupportedOperationException();
217: }
218: }
219:
220: /** A collection that contains only one element.
221: */
222: public static final class OneCollection extends AbstractCollection {
223: private final Object _one;
224:
225: public OneCollection(Object one) {
226: _one = one;
227: }
228:
229: public final int size() {
230: return 1;
231: }
232:
233: public Iterator iterator() {
234: return new OneIterator(_one);
235: }
236: }
237:
238: /** An iterator that iterates one element.
239: */
240: public static final class OneIterator implements Iterator {
241: private boolean _nomore;
242: private final Object _one;
243:
244: public OneIterator(Object one) {
245: _one = one;
246: }
247:
248: public final boolean hasNext() {
249: return !_nomore;
250: }
251:
252: public final Object next() {
253: if (_nomore)
254: throw new NoSuchElementException();
255: _nomore = true;
256: return _one;
257: }
258:
259: public final void remove() {
260: throw new UnsupportedOperationException();
261: }
262: }
263:
264: /** An iterator that iterates thru an Enumeration.
265: */
266: public static final class EnumerationIterator implements Iterator {
267: private final Enumeration _enm;
268:
269: /**
270: * @param enm the enumeration. If null, it means empty.
271: */
272: public EnumerationIterator(Enumeration enm) {
273: _enm = enm;
274: }
275:
276: public final boolean hasNext() {
277: return _enm != null && _enm.hasMoreElements();
278: }
279:
280: public final Object next() {
281: if (_enm == null)
282: throw new NoSuchElementException();
283: return _enm.nextElement();
284: }
285:
286: public final void remove() {
287: throw new UnsupportedOperationException();
288: }
289: };
290:
291: /** Empty iterator.
292: */
293: public static final Iterator EMPTY_ITERATOR = new Iterator() {
294: public final boolean hasNext() {
295: return false;
296: }
297:
298: public final Object next() {
299: throw new NoSuchElementException();
300: }
301:
302: public final void remove() {
303: throw new IllegalStateException();
304: }
305: };
306: /** Empty enumeration.
307: */
308: public static final Enumeration EMPTY_ENUMERATION = new Enumeration() {
309: public final boolean hasMoreElements() {
310: return false;
311: }
312:
313: public final Object nextElement() {
314: throw new NoSuchElementException();
315: }
316: };
317:
318: /** Adds all elements returned by the iterator to a collection.
319: * @param iter the iterator; null is OK
320: * @return the number element being added
321: */
322: public static final int addAll(Collection col, Iterator iter) {
323: int cnt = 0;
324: if (iter != null)
325: for (; iter.hasNext(); ++cnt)
326: col.add(iter.next());
327: return cnt;
328: }
329:
330: /** Adds all elements returned by the enumerator to a collection.
331: * @param enm the enumeration; null is OK
332: * @return the number element being added
333: */
334: public static final int addAll(Collection col, Enumeration enm) {
335: int cnt = 0;
336: if (enm != null)
337: for (; enm.hasMoreElements(); ++cnt)
338: col.add(enm.nextElement());
339: return cnt;
340: }
341:
342: /** Adds all elements of an array to a collection.
343: * @param ary the array; null is OK
344: * @return the number element being added
345: */
346: public static final int addAll(Collection col, Object[] ary) {
347: int cnt = 0;
348: if (ary != null)
349: for (; cnt < ary.length; ++cnt)
350: col.add(ary[cnt]);
351: return cnt;
352: }
353:
354: /** Tests whether two sets has any intersection.
355: */
356: public static final boolean isIntersected(Set a, Set b) {
357: final int sza = a != null ? a.size() : 0;
358: final int szb = b != null ? b.size() : 0;
359: if (sza == 0 || szb == 0)
360: return false;
361:
362: final Set large, small;
363: if (sza > szb) {
364: large = a;
365: small = b;
366: } else {
367: large = b;
368: small = a;
369: }
370: for (final Iterator it = small.iterator(); it.hasNext();)
371: if (large.contains(it.next()))
372: return true;
373: return false;
374: }
375:
376: /**
377: * Parses a string into a list.
378: * To quote a string, both '\'' and '"' are OK.
379: * Whitespaces are trimmed between quotation and separators.
380: *
381: * <p>Unlike Java, quotation could spread over multiple lines.
382: *
383: * <p>Example,<br>
384: * a b , ' c d',"'f'", '1' "2", 3<br>
385: * generate a list of "a b", "c d", "'f'", "1", "2" and "3".
386: * Note: the separator between "1" and "2" is optional.
387: *
388: * <p>Note: Like Java, if the string is ending with a separator,
389: * it will be ignored.
390: *
391: * <p>Example,<br>
392: * a, , b, <br>
393: * generate a list of "a", "", "b".
394: *
395: * @param c the collection to hold the parsed results; a linked list
396: * is created if c is null.
397: * @param src the string to parse
398: * @param separator the separator, e.g., ',', '\n' or ' '.
399: * Note: if separator is ' ', it denotes any white space.
400: * @return the <code>c</code> collection if not null; or a linked list
401: * if c is null (so you can cast it to List)
402: *
403: * @exception IllegalSyntaxException if syntax errors
404: * @see Maps#parse
405: */
406: public static final Collection parse(Collection c,
407: final String src, char separator) {
408: return parse(c, src, separator, true);
409: }
410:
411: /**
412: * Parses a string into a list.
413: * To quote a string, both '\'' and '"' are OK.
414: * Whitespaces are trimmed between quotation and separators.
415: *
416: * <p>Unlike Java, quotation could spread over multiple lines.
417: *
418: * <p>Example,<br>
419: * a b , ' c d',"'f'", '1' "2", 3<br>
420: * generate a list of "a b", "c d", "'f'", "1", "2" and "3".
421: * Note: the separator between "1" and "2" is optional.
422: *
423: * <p>Note: Like Java, if the string is ending with a separator,
424: * it will be ignored.
425: *
426: * <p>Example,<br>
427: * a, , b, <br>
428: * generate a list of "a", "", "b".
429: *
430: * @param c the collection to hold the parsed results; a linked list
431: * is created if c is null.
432: * @param src the string to parse
433: * @param separator the separator, e.g., ',', '\n' or ' '.
434: * Note: if separator is ' ', it denotes any white space.
435: * @param handleBackslash whether to treat '\\' specially (as escape char)
436: * @return the <code>c</code> collection if not null; or a linked list
437: * if c is null (so you can cast it to List)
438: *
439: * @exception IllegalSyntaxException if syntax errors
440: * @see Maps#parse
441: */
442: public static final Collection parse(Collection c,
443: final String src, char separator, boolean handleBackslash) {
444: if (c == null)
445: c = new LinkedList();
446:
447: final char[] seps = new char[] { separator };
448: int j = 0;
449: for (Strings.Result res; (res = Strings.nextToken(src, j, seps,
450: handleBackslash, true)) != null; j = res.next) {
451: assert res.token != null;
452: c.add(res.token);
453: }
454: return c;
455: }
456:
457: /**
458: * Based on the given collection type of Object, return an iterator. The
459: * Collection type of object can be Collection, Map (return the entry), or
460: * Array.
461: */
462: public static final Iterator iterator(Object obj) {
463: if (obj instanceof Object[]) {
464: return new ArrayIterator((Object[]) obj);
465: } else if (obj instanceof Collection) {
466: return ((Collection) obj).iterator();
467: } else if (obj instanceof Map) {
468: return ((Map) obj).entrySet().iterator();
469: }
470: throw new IllegalArgumentException(
471: "obj must be a Collection, a Map, or an Object array. obj: "
472: + obj);
473: }
474: }
|