001: /*
002: * Copyright 2004-2005 OpenSymphony
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005: * use this file except in compliance with the License. You may obtain a copy
006: * of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013: * License for the specific language governing permissions and limitations
014: * under the License.
015: *
016: */
017:
018: /*
019: * Previously Copyright (c) 2001-2004 James House
020: */
021: package org.quartz.utils;
022:
023: import java.lang.reflect.Array;
024: import java.util.Collection;
025: import java.util.HashMap;
026: import java.util.Iterator;
027: import java.util.Map;
028: import java.util.Set;
029:
030: /**
031: * <p>
032: * An implementation of <code>Map</code> that wraps another <code>Map</code>
033: * and flags itself 'dirty' when it is modified.
034: * </p>
035: *
036: * @author James House
037: */
038: public class DirtyFlagMap implements Map, Cloneable,
039: java.io.Serializable {
040:
041: /*
042: * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
043: *
044: * Data members.
045: *
046: * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
047: */
048: private static final long serialVersionUID = 1433884852607126222L;
049:
050: private boolean dirty = false;
051: private Map map;
052:
053: /*
054: * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
055: *
056: * Constructors.
057: *
058: * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
059: */
060:
061: /**
062: * <p>
063: * Create a DirtyFlagMap that 'wraps' a <code>HashMap</code>.
064: * </p>
065: *
066: * @see java.util.HashMap
067: */
068: public DirtyFlagMap() {
069: map = new HashMap();
070: }
071:
072: /**
073: * <p>
074: * Create a DirtyFlagMap that 'wraps' a <code>HashMap</code> that has the
075: * given initial capacity.
076: * </p>
077: *
078: * @see java.util.HashMap
079: */
080: public DirtyFlagMap(int initialCapacity) {
081: map = new HashMap(initialCapacity);
082: }
083:
084: /**
085: * <p>
086: * Create a DirtyFlagMap that 'wraps' a <code>HashMap</code> that has the
087: * given initial capacity and load factor.
088: * </p>
089: *
090: * @see java.util.HashMap
091: */
092: public DirtyFlagMap(int initialCapacity, float loadFactor) {
093: map = new HashMap(initialCapacity, loadFactor);
094: }
095:
096: /*
097: * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
098: *
099: * Interface.
100: *
101: * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
102: */
103:
104: /**
105: * <p>
106: * Clear the 'dirty' flag (set dirty flag to <code>false</code>).
107: * </p>
108: */
109: public void clearDirtyFlag() {
110: dirty = false;
111: }
112:
113: /**
114: * <p>
115: * Determine whether the <code>Map</code> is flagged dirty.
116: * </p>
117: */
118: public boolean isDirty() {
119: return dirty;
120: }
121:
122: /**
123: * <p>
124: * Get a direct handle to the underlying Map.
125: * </p>
126: */
127: public Map getWrappedMap() {
128: return map;
129: }
130:
131: public void clear() {
132: if (map.isEmpty() == false) {
133: dirty = true;
134: }
135:
136: map.clear();
137: }
138:
139: public boolean containsKey(Object key) {
140: return map.containsKey(key);
141: }
142:
143: public boolean containsValue(Object val) {
144: return map.containsValue(val);
145: }
146:
147: public Set entrySet() {
148: return new DirtyFlagMapEntrySet(map.entrySet());
149: }
150:
151: public boolean equals(Object obj) {
152: if (obj == null || !(obj instanceof DirtyFlagMap)) {
153: return false;
154: }
155:
156: return map.equals(((DirtyFlagMap) obj).getWrappedMap());
157: }
158:
159: public Object get(Object key) {
160: return map.get(key);
161: }
162:
163: public boolean isEmpty() {
164: return map.isEmpty();
165: }
166:
167: public Set keySet() {
168: return new DirtyFlagSet(map.keySet());
169: }
170:
171: public Object put(Object key, Object val) {
172: dirty = true;
173:
174: return map.put(key, val);
175: }
176:
177: public void putAll(Map t) {
178: if (!t.isEmpty()) {
179: dirty = true;
180: }
181:
182: map.putAll(t);
183: }
184:
185: public Object remove(Object key) {
186: Object obj = map.remove(key);
187:
188: if (obj != null) {
189: dirty = true;
190: }
191:
192: return obj;
193: }
194:
195: public int size() {
196: return map.size();
197: }
198:
199: public Collection values() {
200: return new DirtyFlagCollection(map.values());
201: }
202:
203: public Object clone() {
204: DirtyFlagMap copy;
205: try {
206: copy = (DirtyFlagMap) super .clone();
207: if (map instanceof HashMap) {
208: copy.map = (Map) ((HashMap) map).clone();
209: }
210: } catch (CloneNotSupportedException ex) {
211: throw new IncompatibleClassChangeError("Not Cloneable.");
212: }
213:
214: return copy;
215: }
216:
217: /**
218: * Wrap a Collection so we can mark the DirtyFlagMap as dirty if
219: * the underlying Collection is modified.
220: */
221: private class DirtyFlagCollection implements Collection {
222: private Collection collection;
223:
224: public DirtyFlagCollection(Collection c) {
225: collection = c;
226: }
227:
228: protected Collection getWrappedCollection() {
229: return collection;
230: }
231:
232: public Iterator iterator() {
233: return new DirtyFlagIterator(collection.iterator());
234: }
235:
236: public boolean remove(Object o) {
237: boolean removed = collection.remove(o);
238: if (removed) {
239: dirty = true;
240: }
241: return removed;
242: }
243:
244: public boolean removeAll(Collection c) {
245: boolean changed = collection.removeAll(c);
246: if (changed) {
247: dirty = true;
248: }
249: return changed;
250: }
251:
252: public boolean retainAll(Collection c) {
253: boolean changed = collection.retainAll(c);
254: if (changed) {
255: dirty = true;
256: }
257: return changed;
258: }
259:
260: public void clear() {
261: if (collection.isEmpty() == false) {
262: dirty = true;
263: }
264: collection.clear();
265: }
266:
267: // Pure wrapper methods
268: public int size() {
269: return collection.size();
270: }
271:
272: public boolean isEmpty() {
273: return collection.isEmpty();
274: }
275:
276: public boolean contains(Object o) {
277: return collection.contains(o);
278: }
279:
280: public boolean add(Object o) {
281: return add(o);
282: } // Not supported
283:
284: public boolean addAll(Collection c) {
285: return collection.addAll(c);
286: } // Not supported
287:
288: public boolean containsAll(Collection c) {
289: return collection.containsAll(c);
290: }
291:
292: public Object[] toArray() {
293: return collection.toArray();
294: }
295:
296: public Object[] toArray(Object[] array) {
297: return collection.toArray(array);
298: }
299: }
300:
301: /**
302: * Wrap a Set so we can mark the DirtyFlagMap as dirty if
303: * the underlying Collection is modified.
304: */
305: private class DirtyFlagSet extends DirtyFlagCollection implements
306: Set {
307: public DirtyFlagSet(Set set) {
308: super (set);
309: }
310:
311: protected Set getWrappedSet() {
312: return (Set) getWrappedCollection();
313: }
314: }
315:
316: /**
317: * Wrap an Iterator so that we can mark the DirtyFlagMap as dirty if an
318: * element is removed.
319: */
320: private class DirtyFlagIterator implements Iterator {
321: private Iterator iterator;
322:
323: public DirtyFlagIterator(Iterator iterator) {
324: this .iterator = iterator;
325: }
326:
327: public void remove() {
328: dirty = true;
329: iterator.remove();
330: }
331:
332: // Pure wrapper methods
333: public boolean hasNext() {
334: return iterator.hasNext();
335: }
336:
337: public Object next() {
338: return iterator.next();
339: }
340: }
341:
342: /**
343: * Wrap a Map.Entry Set so we can mark the Map as dirty if
344: * the Set is modified, and return Map.Entry objects
345: * wrapped in the <code>DirtyFlagMapEntry</code> class.
346: */
347: private class DirtyFlagMapEntrySet extends DirtyFlagSet {
348:
349: public DirtyFlagMapEntrySet(Set set) {
350: super (set);
351: }
352:
353: public Iterator iterator() {
354: return new DirtyFlagMapEntryIterator(getWrappedSet()
355: .iterator());
356: }
357:
358: public Object[] toArray() {
359: return toArray(new Object[super .size()]);
360: }
361:
362: public Object[] toArray(Object[] array) {
363: if (array.getClass().getComponentType().isAssignableFrom(
364: Map.Entry.class) == false) {
365: throw new IllegalArgumentException(
366: "Array must be of type assignable from Map.Entry");
367: }
368:
369: int size = super .size();
370:
371: Object[] result = (array.length < size) ? (Object[]) Array
372: .newInstance(array.getClass().getComponentType(),
373: size) : array;
374:
375: Iterator entryIter = iterator(); // Will return DirtyFlagMapEntry objects
376: for (int i = 0; i < size; i++) {
377: result[i] = entryIter.next();
378: }
379:
380: if (result.length > size) {
381: result[size] = null;
382: }
383:
384: return result;
385: }
386: }
387:
388: /**
389: * Wrap an Iterator over Map.Entry objects so that we can
390: * mark the Map as dirty if an element is removed or modified.
391: */
392: private class DirtyFlagMapEntryIterator extends DirtyFlagIterator {
393: public DirtyFlagMapEntryIterator(Iterator iterator) {
394: super (iterator);
395: }
396:
397: public Object next() {
398: return new DirtyFlagMapEntry((Map.Entry) super .next());
399: }
400: }
401:
402: /**
403: * Wrap a Map.Entry so we can mark the Map as dirty if
404: * a value is set.
405: */
406: private class DirtyFlagMapEntry implements Map.Entry {
407: private Map.Entry entry;
408:
409: public DirtyFlagMapEntry(Map.Entry entry) {
410: this .entry = entry;
411: }
412:
413: public Object setValue(Object o) {
414: dirty = true;
415: return entry.setValue(o);
416: }
417:
418: // Pure wrapper methods
419: public Object getKey() {
420: return entry.getKey();
421: }
422:
423: public Object getValue() {
424: return entry.getValue();
425: }
426: }
427: }
|