001: /*
002: * This file is part of the WfMOpen project.
003: * Copyright (C) 2001-2003 Danet GmbH (www.danet.de), GS-AN.
004: * All rights reserved.
005: *
006: * This program is free software; you can redistribute it and/or modify
007: * it under the terms of the GNU General Public License as published by
008: * the Free Software Foundation; either version 2 of the License, or
009: * (at your option) any later version.
010: *
011: * This program is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: * GNU General Public License for more details.
015: *
016: * You should have received a copy of the GNU General Public License
017: * along with this program; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019: *
020: * $Id: CollectionsUtil.java,v 1.4 2006/11/23 20:38:22 mlipp Exp $
021: *
022: * $Log: CollectionsUtil.java,v $
023: * Revision 1.4 2006/11/23 20:38:22 mlipp
024: * Minor optimization.
025: *
026: * Revision 1.3 2006/09/29 12:32:08 drmlipp
027: * Consistently using WfMOpen as projct name now.
028: *
029: * Revision 1.2 2005/09/28 15:09:44 drmlipp
030: * Added tracked list.
031: *
032: * Revision 1.1.1.3 2004/08/18 15:17:34 drmlipp
033: * Update to 1.2
034: *
035: * Revision 1.5 2004/02/25 12:05:38 lipp
036: * Added debugging helper for Map.
037: *
038: * Revision 1.4 2003/09/22 12:32:57 lipp
039: * Implemented deadline creation for block activities.
040: *
041: * Revision 1.3 2003/06/27 08:51:47 lipp
042: * Fixed copyright/license information.
043: *
044: * Revision 1.2 2002/11/05 10:16:49 schlue
045: * Tracked map added.
046: *
047: * Revision 1.1 2002/10/17 14:45:33 lipp
048: * Initial version.
049: *
050: */
051: package de.danet.an.util;
052:
053: import java.io.Serializable;
054:
055: import java.util.ArrayList;
056: import java.util.Collection;
057: import java.util.Collections;
058: import java.util.HashSet;
059: import java.util.Iterator;
060: import java.util.List;
061: import java.util.ListIterator;
062: import java.util.Map;
063: import java.util.Set;
064:
065: /**
066: * This class provides some helper methods for the use of the Java
067: * collection framework (this includes both collections and maps).
068: *
069: * @author <a href="mailto:lipp@danet.de"></a>
070: * @version $Revision: 1.4 $
071: */
072: public class CollectionsUtil {
073:
074: private static class TrackedMap implements Map, Serializable {
075:
076: /** Backing Map. */
077: private Map m;
078: /** Modified entries. */
079: private Set modifiedEntries = new HashSet();
080:
081: /**
082: * Sole constructor for creating a new tracked map.
083: * @return new tracked map.
084: */
085: public TrackedMap(Map map) {
086: m = map;
087: }
088:
089: /**
090: * Return the set of modified entries.
091: * @return the modified entries
092: */
093: Set modifiedEntries() {
094: if (modifiedEntries.size() == 0) {
095: return Collections.EMPTY_SET;
096: }
097: Set res = modifiedEntries;
098: modifiedEntries = new HashSet();
099: return res;
100: }
101:
102: // Implementation of the methods from java.util.Map;
103:
104: /**
105: * Returns the number of key-value mappings in this map. If
106: * the map contains more than Integer.MAX_VALUE elements,
107: * returns Integer.MAX_VALUE.
108: * This method call is delegated to the backing map.
109: * @return the number of key-value mappings in this map.
110: */
111: public int size() {
112: return m.size();
113: }
114:
115: /**
116: * Checks if map is empty.
117: * This method call is delegated to the backing map.
118: * @return true if this map contains no key-value mappings.
119: */
120: public boolean isEmpty() {
121: return m.isEmpty();
122: }
123:
124: /**
125: * Check if map contains the given key.
126: * This method call is delegated to the backing map.
127: * @param key key whose presence in this map is to be tested.
128: * @return true if this map contains a mapping for the specified key.
129: */
130: public boolean containsKey(Object key) {
131: return m.containsKey(key);
132: }
133:
134: /**
135: * Check if given value is part of the map.
136: * This method call is delegated to the backing map.
137: * @param value value whose presence in this map is to be tested.
138: * @return true if this map maps one or more keys to the
139: * specified value.
140: */
141: public boolean containsValue(Object value) {
142: return m.containsValue(value);
143: }
144:
145: /**
146: * Returns the value to which this map maps the specified
147: * key. Returns null if the map contains no mapping for this
148: * key. A return value of null does not necessarily indicate
149: * that the map contains no mapping for the key; it's also
150: * possible that the map explicitly maps the key to null. The
151: * containsKey operation may be used to distinguish these two
152: * cases.
153: * This method call is delegated to the backing map.
154: * @param key key whose associated value is to be returned.
155: * @return the value to which this map maps the specified key,
156: * or null if the map contains no mapping for this key.
157: * @throws ClassCastException if the key is of an
158: * inappropriate type for this map.
159: * @throws NullPointerException key is null and this map does
160: * not not permit null keys.
161: */
162: public Object get(Object key) throws ClassCastException,
163: NullPointerException {
164: return m.get(key);
165: }
166:
167: /**
168: * Associates the specified value with the specified key in
169: * this map (optional operation). If the map previously
170: * contained a mapping for this key, the old value is
171: * replaced.
172: * This method call is delegated to the backing map.
173: * @param key key with which the specified value is to be associated.
174: * @param value value to be associated with the specified key.
175: * @return previous value associated with specified key, or
176: * null if there was no mapping for key. A null return can
177: * also indicate that the map previously associated null with
178: * the specified key, if the implementation supports null
179: * values.
180: * @throws UnsupportedOperationException if the put operation
181: * is not supported by this map.
182: * @throws ClassCastException if the class of the specified
183: * key or value prevents it from being stored in this map.
184: * @throws IllegalArgumentException if some aspect of this key
185: * or value prevents it from being stored in this map.
186: * @throws NullPointerException this map does not permit null
187: * keys or values, and the specified key or value is null.
188: */
189: public Object put(Object key, Object value)
190: throws UnsupportedOperationException,
191: ClassCastException, IllegalArgumentException,
192: NullPointerException {
193: Object oldValue = m.put(key, value);
194: modifiedEntries.add(key);
195: return oldValue;
196: }
197:
198: /**
199: * Removes the mapping for this key from this map if present.
200: * This method call is delegated to the backing map.
201: * @param key key whose mapping is to be removed from the map.
202: * @return previous value associated with specified key, or
203: * null if there was no mapping for key. A null return can
204: * also indicate that the map previously associated null with
205: * the specified key, if the implementation supports null
206: * values.
207: * @throws UnsupportedOperationException if the remove
208: * method is not supported by this map.
209: */
210: public Object remove(Object key)
211: throws UnsupportedOperationException {
212: Object oldValue = m.remove(key);
213: modifiedEntries.add(key);
214: return oldValue;
215: }
216:
217: /**
218: * Copies all of the mappings from the specified map to this
219: * map. These mappings will replace any mappings that this map
220: * had for any of the keys currently in the specified map.
221: * This method call is delegated to the backing map.
222: * @param t Mappings to be stored in this map.
223: * @throws UnsupportedOperationException if the put operation
224: * is not supported by this map.
225: * @throws ClassCastException if the class of the specified
226: * key or value prevents it from being stored in this map.
227: * @throws IllegalArgumentException if some aspect of this key
228: * or value prevents it from being stored in this map.
229: * @throws NullPointerException this map does not permit null
230: * keys or values, and the specified key or value is null.
231: */
232: public void putAll(Map t) throws UnsupportedOperationException,
233: ClassCastException, IllegalArgumentException,
234: NullPointerException {
235: m.putAll(t);
236: modifiedEntries.addAll(t.keySet());
237: }
238:
239: /**
240: * Removes all mappings from this map.
241: * This method call is delegated to the backing map.
242: * @throws UnsupportedOperationException clear is not
243: * supported by this map.
244: */
245: public void clear() throws UnsupportedOperationException {
246: modifiedEntries.addAll(keySet());
247: m.clear();
248: }
249:
250: /**
251: * Returns a set view of the keys contained in this map. The
252: * set is backed by the map, so changes to the map are
253: * reflected in the set, and vice-versa. If the map is
254: * modified while an iteration over the set is in progress,
255: * the results of the iteration are undefined. The set
256: * supports element removal, which removes the corresponding
257: * mapping from the map, via the Iterator.remove, Set.remove,
258: * removeAll retainAll, and clear operations. It does not
259: * support the add or addAll operations.
260: * This method call is delegated to the backing map.
261: * @return set view of the keys contained in this map.
262: */
263: public Set keySet() {
264: return m.keySet();
265: }
266:
267: /**
268: * Returns a collection view of the values contained in this
269: * map. The collection is backed by the map, so changes to the
270: * map are reflected in the collection, and vice-versa. If the
271: * map is modified while an iteration over the collection is
272: * in progress, the results of the iteration are
273: * undefined. The collection supports element removal, which
274: * removes the corresponding mapping from the map, via the
275: * Iterator.remove, Collection.remove, removeAll, retainAll
276: * and clear operations. It does not support the add or addAll
277: * operations.
278: * This method call is delegated to the backing map.
279: * @return a collection view of the values contained in this map.
280: */
281: public Collection values() {
282: return m.values();
283: }
284:
285: /**
286: * Returns a set view of the mappings contained in this
287: * map. Each element in the returned set is a Map.Entry. The
288: * set is backed by the map, so changes to the map are
289: * reflected in the set, and vice-versa. If the map is
290: * modified while an iteration over the set is in progress,
291: * the results of the iteration are undefined. The set
292: * supports element removal, which removes the corresponding
293: * mapping from the map, via the Iterator.remove, Set.remove,
294: * removeAll, retainAll and clear operations. It does not
295: * support the add or addAll operations.
296: * This method call is delegated to the backing map.
297: * @return a set view of the mappings contained in this map.
298: */
299: public Set entrySet() {
300: return m.entrySet();
301: }
302:
303: /**
304: * Compares the specified object with this map for
305: * equality. Returns true if the given object is also a map
306: * and the two Maps represent the same mappings. More
307: * formally, two maps t1 and t2 represent the same mappings if
308: * t1.entrySet().equals(t2.entrySet()). This ensures that the
309: * equals method works properly across different
310: * implementations of the Map interface. This method call is
311: * delegated to the backing map.
312: * @param o object to be compared for equality with this map.
313: * @return true if the specified object is equal to this map.
314: */
315: public boolean equals(Object o) {
316: return m.equals(o);
317: }
318:
319: /**
320: * Returns the hash code value for this map. The hash code of
321: * a map is defined to be the sum of the hashCodes of each
322: * entry in the map's entrySet view. This ensures that
323: * t1.equals(t2) implies that t1.hashCode()==t2.hashCode() for
324: * any two maps t1 and t2, as required by the general contract
325: * of Object.hashCode.
326: * This method call is delegated to the backing map.
327: * @return the hash code value for this map.
328: */
329: public int hashCode() {
330: return m.hashCode();
331: }
332: }
333:
334: /**
335: * Factory method for creating a new tracked map out of an "ordinary" Map.
336: * @param map the backing map for modification tracking.
337: * @return a tracked map as a Map.
338: */
339: public static Map trackedMap(Map map) {
340: return new TrackedMap(map);
341: }
342:
343: /**
344: * Check if the given map is a tracked map.
345: * @param map the map to be tested
346: * @return true if map is tracked, otherwise false.
347: */
348: public static boolean isTracked(Map map) {
349: return (map instanceof TrackedMap);
350: }
351:
352: /**
353: * Check if the given map has been modified. The method resets the
354: * internal modified flag as it is assumed that after calling this
355: * method some synchronization will be done and tracking starts
356: * anew.<P>
357: *
358: * The method throws an <code>IllegalArgumentException</code> if
359: * the argument is not a result of {@link #trackedMap
360: * <code>trackedMap</code>}, i.e. if <code>isTracked(map) ==
361: * false</code>.
362: * @param map the backing map for modification tracking.
363: * @return true if map has been modified, otherwise false.
364: */
365: public static boolean hasBeenModified(Map map) {
366: if (map instanceof TrackedMap) {
367: return ((TrackedMap) map).modifiedEntries().size() > 0;
368: }
369: throw new IllegalArgumentException("Not a tracked map.");
370: }
371:
372: /**
373: * Returns the modified entries in the given map. The method
374: * resets the internal modified flag as it is assumed that after
375: * calling this method some synchronization will be done and
376: * tracking starts anew.<P>
377: *
378: * The method throws an <code>IllegalArgumentException</code> if
379: * the argument is not a result of {@link #trackedMap
380: * <code>trackedMap</code>}, i.e. if <code>isTracked(map) ==
381: * false</code>.
382: * @param map the backing map for modification tracking.
383: * @return true if map has been modified, otherwise false.
384: */
385: public static Set modifiedEntries(Map map) {
386: if (map instanceof TrackedMap) {
387: return ((TrackedMap) map).modifiedEntries();
388: }
389: throw new IllegalArgumentException("Not a tracked map.");
390: }
391:
392: /**
393: * Generate a string representation of a map for debugging
394: * purposes.
395: * @param map the map
396: * @return string representation
397: */
398: public static String toString(Map map) {
399: List keys = new ArrayList(map.keySet());
400: try {
401: Collections.sort(keys);
402: } catch (Throwable e) {
403: }
404: StringBuffer res = new StringBuffer("Map[");
405: boolean first = true;
406: for (Iterator i = keys.iterator(); i.hasNext();) {
407: String key = (String) i.next();
408: if (!first) {
409: res.append(",");
410: } else {
411: first = false;
412: }
413: res.append(key + "=" + map.get(key));
414: }
415: res.append("]");
416: return res.toString();
417: }
418:
419: public static class TrackedList implements List {
420: /** Backing List. */
421: private List l;
422: /** Modified entries. */
423: private boolean modified = false;
424:
425: /**
426: * Create a new tracked list with the given list as backing list.
427: * @param list the list to track.
428: */
429: public TrackedList(List list) {
430: l = list;
431: }
432:
433: /**
434: * Check if list has been modified and reset modified flag.
435: * @return <code>true</code> if list has been modified.
436: */
437: public boolean isModified() {
438: boolean m = modified;
439: modified = false;
440: return m;
441: }
442:
443: /* (non-Javadoc)
444: * @see java.util.List#add(int, java.lang.Object)
445: */
446: public void add(int index, Object element) {
447: l.add(index, element);
448: modified = true;
449: }
450:
451: /* (non-Javadoc)
452: * @see java.util.List#add(java.lang.Object)
453: */
454: public boolean add(Object o) {
455: modified = true;
456: return l.add(o);
457: }
458:
459: /* (non-Javadoc)
460: * @see java.util.List#addAll(java.util.Collection)
461: */
462: public boolean addAll(Collection c) {
463: modified = true;
464: return l.addAll(c);
465: }
466:
467: /* (non-Javadoc)
468: * @see java.util.List#addAll(int, java.util.Collection)
469: */
470: public boolean addAll(int index, Collection c) {
471: modified = true;
472: return l.addAll(index, c);
473: }
474:
475: /* (non-Javadoc)
476: * @see java.util.List#clear()
477: */
478: public void clear() {
479: modified = true;
480: l.clear();
481: }
482:
483: /* (non-Javadoc)
484: * @see java.util.List#contains(java.lang.Object)
485: */
486: public boolean contains(Object o) {
487: return l.contains(o);
488: }
489:
490: /* (non-Javadoc)
491: * @see java.util.List#containsAll(java.util.Collection)
492: */
493: public boolean containsAll(Collection c) {
494: return l.containsAll(c);
495: }
496:
497: /* (non-Javadoc)
498: * @see java.util.List#equals(java.lang.Object)
499: */
500: public boolean equals(Object o) {
501: return l.equals(o);
502: }
503:
504: /* (non-Javadoc)
505: * @see java.util.List#get(int)
506: */
507: public Object get(int index) {
508: return l.get(index);
509: }
510:
511: /* (non-Javadoc)
512: * @see java.util.List#hashCode()
513: */
514: public int hashCode() {
515: return l.hashCode();
516: }
517:
518: /* (non-Javadoc)
519: * @see java.util.List#indexOf(java.lang.Object)
520: */
521: public int indexOf(Object o) {
522: return l.indexOf(o);
523: }
524:
525: /* (non-Javadoc)
526: * @see java.util.List#isEmpty()
527: */
528: public boolean isEmpty() {
529: return l.isEmpty();
530: }
531:
532: /* (non-Javadoc)
533: * @see java.util.List#iterator()
534: */
535: public Iterator iterator() {
536: return l.iterator();
537: }
538:
539: /* (non-Javadoc)
540: * @see java.util.List#lastIndexOf(java.lang.Object)
541: */
542: public int lastIndexOf(Object o) {
543: return l.lastIndexOf(o);
544: }
545:
546: /* (non-Javadoc)
547: * @see java.util.List#listIterator()
548: */
549: public ListIterator listIterator() {
550: return l.listIterator();
551: }
552:
553: /* (non-Javadoc)
554: * @see java.util.List#listIterator(int)
555: */
556: public ListIterator listIterator(int index) {
557: return l.listIterator(index);
558: }
559:
560: /* (non-Javadoc)
561: * @see java.util.List#remove(int)
562: */
563: public Object remove(int index) {
564: modified = true;
565: return l.remove(index);
566: }
567:
568: /* (non-Javadoc)
569: * @see java.util.List#remove(java.lang.Object)
570: */
571: public boolean remove(Object o) {
572: modified = true;
573: return l.remove(o);
574: }
575:
576: /* (non-Javadoc)
577: * @see java.util.List#removeAll(java.util.Collection)
578: */
579: public boolean removeAll(Collection c) {
580: modified = true;
581: return l.removeAll(c);
582: }
583:
584: /* (non-Javadoc)
585: * @see java.util.List#retainAll(java.util.Collection)
586: */
587: public boolean retainAll(Collection c) {
588: modified = true;
589: return l.retainAll(c);
590: }
591:
592: /* (non-Javadoc)
593: * @see java.util.List#set(int, java.lang.Object)
594: */
595: public Object set(int index, Object element) {
596: modified = true;
597: return l.set(index, element);
598: }
599:
600: /* (non-Javadoc)
601: * @see java.util.List#size()
602: */
603: public int size() {
604: return l.size();
605: }
606:
607: /* (non-Javadoc)
608: * @see java.util.List#subList(int, int)
609: */
610: public List subList(int fromIndex, int toIndex) {
611: return l.subList(fromIndex, toIndex);
612: }
613:
614: /* (non-Javadoc)
615: * @see java.util.List#toArray()
616: */
617: public Object[] toArray() {
618: return l.toArray();
619: }
620:
621: /* (non-Javadoc)
622: * @see java.util.List#toArray(java.lang.Object[])
623: */
624: public Object[] toArray(Object[] a) {
625: return l.toArray(a);
626: }
627:
628: public String toString() {
629: return l.toString();
630: }
631: }
632:
633: /**
634: * Factory method for creating a new tracked list out of an "ordinary" List.
635: * @param list the backing list for modification tracking.
636: * @return a tracked list as a List.
637: */
638: public static List trackedList(List list) {
639: return new TrackedList(list);
640: }
641:
642: /**
643: * Check if the given list is a tracked list.
644: * @param list the list to be tested
645: * @return true if list is tracked, otherwise false.
646: */
647: public static boolean isTracked(List list) {
648: return (list instanceof TrackedList);
649: }
650:
651: /**
652: * Check if the given list has been modified. The method resets the
653: * internal modified flag as it is assumed that after calling this
654: * method some synchronization will be done and tracking starts
655: * anew.<P>
656: *
657: * The method throws an <code>IllegalArgumentException</code> if
658: * the argument is not a result of {@link #trackedList
659: * <code>trackedList</code>}, i.e. if <code>isTracked(list) ==
660: * false</code>.
661: * @param list the list to be checked.
662: * @return true if list has been modified, otherwise false.
663: */
664: public static boolean hasBeenModified(List list) {
665: if (list instanceof TrackedList) {
666: return ((TrackedList) list).isModified();
667: }
668: throw new IllegalArgumentException("Not a tracked list.");
669: }
670: }
|