001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * // Copyright (c) 1998, 2007, Oracle. All rights reserved.
005: *
006: *
007: * The contents of this file are subject to the terms of either the GNU
008: * General Public License Version 2 only ("GPL") or the Common Development
009: * and Distribution License("CDDL") (collectively, the "License"). You
010: * may not use this file except in compliance with the License. You can obtain
011: * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
012: * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
013: * language governing permissions and limitations under the License.
014: *
015: * When distributing the software, include this License Header Notice in each
016: * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
017: * Sun designates this particular file as subject to the "Classpath" exception
018: * as provided by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the License
020: * Header, with the fields enclosed by brackets [] replaced by your own
021: * identifying information: "Portions Copyrighted [year]
022: * [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * If you wish your version of this file to be governed by only the CDDL or
027: * only the GPL Version 2, indicate your decision by adding "[Contributor]
028: * elects to include this software in this distribution under the [CDDL or GPL
029: * Version 2] license." If you don't indicate a single choice of license, a
030: * recipient has the option to distribute your version of this file under
031: * either the CDDL, the GPL Version 2 or to extend the choice of license to
032: * its licensees as provided above. However, if you add GPL Version 2 code
033: * and therefore, elected the GPL Version 2 license, then the option applies
034: * only if the new code is made subject to such option by the copyright
035: * holder.
036: */
037: package oracle.toplink.essentials.indirection;
038:
039: import java.util.*;
040:
041: /**
042: * IndirectMap allows a domain class to take advantage of TopLink indirection
043: * without having to declare its instance variable as a ValueHolderInterface.
044: * <p>To use an IndirectMap:<ul>
045: * <li> Declare the appropriate instance variable with type Hashtable (jdk1.1)
046: * or Map (jdk1.2).
047: * <li> Send the message #useTransparentMap(String) to the appropriate
048: * CollectionMapping.
049: * </ul>
050: * TopLink will place an
051: * IndirectMap in the instance variable when the containing domain object is read from
052: * the datatabase. With the first message sent to the IndirectMap, the contents
053: * are fetched from the database and normal Hashtable/Map behavior is resumed.
054: *
055: * @see oracle.toplink.essentials.mappings.CollectionMapping
056: * @see oracle.toplink.essentials.indirection.IndirectList
057: * @author Big Country
058: * @since TOPLink/Java 2.5
059: */
060: public class IndirectMap extends Hashtable implements IndirectContainer {
061:
062: /** Reduce type casting */
063: protected Hashtable delegate;
064:
065: /** Delegate indirection behavior to a value holder */
066: protected ValueHolderInterface valueHolder;
067:
068: /** The mapping attribute name, used to raise change events. */
069: private transient String attributeName;
070:
071: /** Store initial size for lazy init. */
072: protected int initialCapacity = 11;
073:
074: /** Store load factor for lazy init. */
075: protected float loadFactor = 0.75f;
076:
077: /**
078: * PUBLIC:
079: * Construct a new, empty IndirectMap with a default
080: * capacity and load factor.
081: */
082: public IndirectMap() {
083: this (11);
084: }
085:
086: /**
087: * PUBLIC:
088: * Construct a new, empty IndirectMap with the specified initial capacity
089: * and default load factor.
090: *
091: * @param initialCapacity the initial capacity of the hashtable
092: */
093: public IndirectMap(int initialCapacity) {
094: this (initialCapacity, 0.75f);
095: }
096:
097: /**
098: * PUBLIC:
099: * Construct a new, empty IndirectMap with the specified initial
100: * capacity and load factor.
101: *
102: * @param initialCapacity the initial capacity of the hashtable
103: * @param loadFactor a number between 0.0 and 1.0
104: * @exception IllegalArgumentException if the initial capacity is less
105: * than or equal to zero, or if the load factor is less than
106: * or equal to zero
107: */
108: public IndirectMap(int initialCapacity, float loadFactor) {
109: super (0);
110: this .initialize(initialCapacity, loadFactor);
111: }
112:
113: /**
114: * PUBLIC:
115: * Construct a new IndirectMap with the same mappings as the given Map.
116: * The IndirectMap is created with a capacity of twice the number of entries
117: * in the given Map or 11 (whichever is greater), and a default load factor, which is 0.75.
118: * @param m a map containing the mappings to use
119: */
120: public IndirectMap(Map m) {
121: super (0);
122: this .initialize(m);
123: }
124:
125: /**
126: * Return the freshly-built delegate.
127: */
128: protected Hashtable buildDelegate() {
129: return (Hashtable) getValueHolder().getValue();
130: }
131:
132: /**
133: * @see java.util.Hashtable#clear()
134: */
135: public synchronized void clear() {
136: this .getDelegate().clear();
137: }
138:
139: /**
140: * @see java.util.Hashtable#clone()
141: * This will result in a database query if necessary.
142: */
143:
144: /*
145: There are 3 situations when clone() is called:
146: 1. The developer actually wants to clone the collection (typically to modify one
147: of the 2 resulting collections). In which case the contents must be read from
148: the database.
149: 2. A UnitOfWork needs a clone (or backup clone) of the collection. But the
150: UnitOfWork checks "instantiation" before cloning collections ("un-instantiated"
151: collections are not cloned).
152: 3. A MergeManager needs an extra copy of the collection (because the "backup"
153: and "target" are the same object?). But the MergeManager checks "instantiation"
154: before merging collections (again, "un-instantiated" collections are not merged).
155: */
156: public synchronized Object clone() {
157: IndirectMap result = (IndirectMap) super .clone();
158: result.delegate = (Hashtable) this .getDelegate().clone();
159: return result;
160: }
161:
162: /**
163: * @see java.util.Hashtable#contains(java.lang.Object)
164: */
165: public synchronized boolean contains(Object value) {
166: return this .getDelegate().contains(value);
167: }
168:
169: /**
170: * @see java.util.Hashtable#containsKey(java.lang.Object)
171: */
172: public synchronized boolean containsKey(Object key) {
173: return this .getDelegate().containsKey(key);
174: }
175:
176: /**
177: * @see java.util.Hashtable#containsValue(java.lang.Object)
178: */
179: public boolean containsValue(Object value) {
180: return this .getDelegate().containsValue(value);
181: }
182:
183: /**
184: * @see java.util.Hashtable#elements()
185: */
186: public synchronized Enumeration elements() {
187: return this .getDelegate().elements();
188: }
189:
190: /**
191: * @see java.util.Hashtable#entrySet()
192: */
193: public Set entrySet() {
194: return new Set() {
195: Set delegateSet = IndirectMap.this .getDelegate().entrySet();
196:
197: public int size() {
198: return this .delegateSet.size();
199: }
200:
201: public boolean isEmpty() {
202: return this .delegateSet.isEmpty();
203: }
204:
205: public boolean contains(Object o) {
206: return this .delegateSet.contains(o);
207: }
208:
209: public Iterator iterator() {
210: return new Iterator() {
211: Iterator delegateIterator = delegateSet.iterator();
212: Object currentObject;
213:
214: public boolean hasNext() {
215: return this .delegateIterator.hasNext();
216: }
217:
218: public Object next() {
219: this .currentObject = this .delegateIterator
220: .next();
221: return this .currentObject;
222: }
223:
224: public void remove() {
225: raiseRemoveChangeEvent(
226: ((Map.Entry) currentObject).getKey(),
227: ((Map.Entry) currentObject).getValue());
228: this .delegateIterator.remove();
229: }
230: };
231: }
232:
233: public Object[] toArray() {
234: return this .delegateSet.toArray();
235: }
236:
237: public Object[] toArray(Object a[]) {
238: return this .delegateSet.toArray(a);
239: }
240:
241: public boolean add(Object o) {
242: return this .delegateSet.add(o);
243: }
244:
245: public boolean remove(Object o) {
246: if (!(o instanceof Map.Entry)) {
247: return false;
248: }
249: return (IndirectMap.this .remove(((Map.Entry) o)
250: .getKey()) != null);
251: }
252:
253: public boolean containsAll(Collection c) {
254: return this .delegateSet.containsAll(c);
255: }
256:
257: public boolean addAll(Collection c) {
258: return this .delegateSet.addAll(c);
259: }
260:
261: public boolean retainAll(Collection c) {
262: boolean result = false;
263: Iterator objects = delegateSet.iterator();
264: while (objects.hasNext()) {
265: Map.Entry object = (Map.Entry) objects.next();
266: if (!c.contains(object)) {
267: objects.remove();
268: raiseRemoveChangeEvent(object.getKey(), object
269: .getValue());
270: result = true;
271: }
272: }
273: return result;
274: }
275:
276: public boolean removeAll(Collection c) {
277: boolean result = false;
278: for (Iterator cs = c.iterator(); cs.hasNext();) {
279: Object object = cs.next();
280: if (!(object instanceof Map.Entry)) {
281: continue;
282: }
283: Object removed = IndirectMap.this
284: .remove(((Map.Entry) object).getKey());
285: if (removed != null) {
286: result = true;
287: }
288: }
289: return result;
290: }
291:
292: public void clear() {
293: IndirectMap.this .clear();
294: }
295:
296: public boolean equals(Object o) {
297: return this .delegateSet.equals(o);
298: }
299:
300: public int hashCode() {
301: return this .delegateSet.hashCode();
302: }
303: };
304: }
305:
306: /**
307: * @see java.util.Hashtable#equals(java.lang.Object)
308: */
309: public synchronized boolean equals(Object o) {
310: return this .getDelegate().equals(o);
311: }
312:
313: /**
314: * @see java.util.Hashtable#get(java.lang.Object)
315: */
316: public synchronized Object get(Object key) {
317: return this .getDelegate().get(key);
318: }
319:
320: /**
321: * Check whether the contents have been read from the database.
322: * If they have not, read them and set the delegate.
323: */
324: protected synchronized Hashtable getDelegate() {
325: if (delegate == null) {
326: delegate = this .buildDelegate();
327: }
328: return delegate;
329: }
330:
331: /**
332: * Return the mapping attribute name, used to raise change events.
333: */
334: public String getTopLinkAttributeName() {
335: return attributeName;
336: }
337:
338: /**
339: * PUBLIC:
340: * Return the valueHolder.
341: */
342: public synchronized ValueHolderInterface getValueHolder() {
343: // PERF: lazy initialize value holder and vector as are normally set after creation.
344: if (valueHolder == null) {
345: valueHolder = new ValueHolder(new Hashtable(
346: initialCapacity, loadFactor));
347: }
348: return valueHolder;
349: }
350:
351: /**
352: * @see java.util.Hashtable#hashCode()
353: */
354: public synchronized int hashCode() {
355: return this .getDelegate().hashCode();
356: }
357:
358: /**
359: * Initialize the instance.
360: */
361: protected void initialize(int initialCapacity, float loadFactor) {
362: this .delegate = null;
363: this .loadFactor = loadFactor;
364: this .initialCapacity = initialCapacity;
365: this .valueHolder = null;
366: }
367:
368: /**
369: * Initialize the instance.
370: */
371: protected void initialize(Map m) {
372: this .delegate = null;
373: Hashtable temp = new Hashtable(m);
374:
375: this .valueHolder = new ValueHolder(temp);
376: }
377:
378: /**
379: * @see java.util.Hashtable#isEmpty()
380: */
381: public boolean isEmpty() {
382: return this .getDelegate().isEmpty();
383: }
384:
385: /**
386: * PUBLIC:
387: * Return whether the contents have been read from the database.
388: */
389: public boolean isInstantiated() {
390: return this .getValueHolder().isInstantiated();
391: }
392:
393: /**
394: * @see java.util.Hashtable#keys()
395: */
396: public synchronized Enumeration keys() {
397: return this .getDelegate().keys();
398: }
399:
400: /**
401: * @see java.util.Hashtable#keySet()
402: */
403: public Set keySet() {
404:
405: return new Set() {
406: Set delegateSet = IndirectMap.this .getDelegate().keySet();
407:
408: public int size() {
409: return this .delegateSet.size();
410: }
411:
412: public boolean isEmpty() {
413: return this .delegateSet.isEmpty();
414: }
415:
416: public boolean contains(Object o) {
417: return this .delegateSet.contains(o);
418: }
419:
420: public Iterator iterator() {
421: return new Iterator() {
422: Iterator delegateIterator = delegateSet.iterator();
423: Object currentObject;
424:
425: public boolean hasNext() {
426: return this .delegateIterator.hasNext();
427: }
428:
429: public Object next() {
430: this .currentObject = this .delegateIterator
431: .next();
432: return this .currentObject;
433: }
434:
435: public void remove() {
436: IndirectMap.this .raiseRemoveChangeEvent(
437: currentObject, IndirectMap.this
438: .getDelegate().get(
439: currentObject));
440: this .delegateIterator.remove();
441: }
442: };
443: }
444:
445: public Object[] toArray() {
446: return this .delegateSet.toArray();
447: }
448:
449: public Object[] toArray(Object a[]) {
450: return this .delegateSet.toArray(a);
451: }
452:
453: public boolean add(Object o) {
454: return this .delegateSet.add(o);
455: }
456:
457: public boolean remove(Object o) {
458: return (IndirectMap.this .remove(o) != null);
459: }
460:
461: public boolean containsAll(Collection c) {
462: return this .delegateSet.containsAll(c);
463: }
464:
465: public boolean addAll(Collection c) {
466: return this .delegateSet.addAll(c);
467: }
468:
469: public boolean retainAll(Collection c) {
470: boolean result = false;
471: Iterator objects = delegateSet.iterator();
472: while (objects.hasNext()) {
473: Object object = objects.next();
474: if (!c.contains(object)) {
475: objects.remove();
476: IndirectMap.this .raiseRemoveChangeEvent(object,
477: IndirectMap.this .getDelegate().get(
478: object));
479: result = true;
480: }
481: }
482: return result;
483: }
484:
485: public boolean removeAll(Collection c) {
486: boolean result = false;
487: for (Iterator cs = c.iterator(); cs.hasNext();) {
488: if (IndirectMap.this .remove(cs.next()) != null) {
489: result = true;
490: }
491: }
492: return result;
493: }
494:
495: public void clear() {
496: IndirectMap.this .clear();
497: }
498:
499: public boolean equals(Object o) {
500: return this .delegateSet.equals(o);
501: }
502:
503: public int hashCode() {
504: return this .delegateSet.hashCode();
505: }
506: };
507:
508: }
509:
510: /**
511: * @see java.util.Hashtable#put(java.lang.Object, java.lang.Object)
512: */
513: public synchronized Object put(Object key, Object value) {
514: Object oldValue = this .getDelegate().put(key, value);
515: if (oldValue != null) {
516: raiseRemoveChangeEvent(key, oldValue);
517: }
518: raiseAddChangeEvent(key, value);
519: return oldValue;
520: }
521:
522: /**
523: * @see java.util.Hashtable#putAll(java.util.Map)
524: */
525: public synchronized void putAll(Map t) {
526: this .getDelegate().putAll(t);
527: }
528:
529: /**
530: * @see java.util.Hashtable#rehash()
531: */
532: protected void rehash() {
533: throw new InternalError("unsupported");
534: }
535:
536: /**
537: * Raise the add change event and relationship maintainence.
538: */
539: protected void raiseAddChangeEvent(Object key, Object value) {
540: // this is where relationship maintenance would go
541: }
542:
543: /**
544: * Raise the remove change event.
545: */
546: protected void raiseRemoveChangeEvent(Object key, Object value) {
547: // this is where relationship maintenance would go
548: }
549:
550: /**
551: * @see java.util.Hashtable#remove(java.lang.Object)
552: */
553: public synchronized Object remove(Object key) {
554: Object value = this .getDelegate().remove(key);
555: if (value != null) {
556: raiseRemoveChangeEvent(key, value);
557: }
558: return value;
559: }
560:
561: /**
562: * Set the mapping attribute name, used to raise change events.
563: * This is required if the change listener is set.
564: */
565: public void setTopLinkAttributeName(String attributeName) {
566: this .attributeName = attributeName;
567: }
568:
569: /**
570: * PUBLIC:
571: * Set the value holder.
572: */
573: public void setValueHolder(ValueHolderInterface valueHolder) {
574: this .delegate = null;
575: this .valueHolder = valueHolder;
576: }
577:
578: /**
579: * @see java.util.Hashtable#size()
580: */
581: public int size() {
582: return this .getDelegate().size();
583: }
584:
585: /**
586: * PUBLIC:
587: * Use the Hashtable.toString(); but wrap it with braces to indicate
588: * there is a bit of indirection.
589: * Don't allow this method to trigger a database read.
590: * @see java.util.Hashtable#toString()
591: */
592: public String toString() {
593: if (ValueHolderInterface.shouldToStringInstantiate) {
594: return this .getDelegate().toString();
595: }
596: if (this .isInstantiated()) {
597: return "{" + this .getDelegate().toString() + "}";
598: } else {
599: return "{"
600: + oracle.toplink.essentials.internal.helper.Helper
601: .getShortClassName(this .getClass())
602: + ": not instantiated}";
603: }
604: }
605:
606: /**
607: * @see java.util.Hashtable#values()
608: */
609: public Collection values() {
610: return new Collection() {
611: protected Collection delegateCollection = IndirectMap.this
612: .getDelegate().values();
613:
614: public int size() {
615: return delegateCollection.size();
616: }
617:
618: public boolean isEmpty() {
619: return delegateCollection.isEmpty();
620: }
621:
622: public boolean contains(Object o) {
623: return delegateCollection.contains(o);
624: }
625:
626: public Iterator iterator() {
627: return new Iterator() {
628: Iterator delegateIterator = delegateCollection
629: .iterator();
630: Object currentObject;
631:
632: public boolean hasNext() {
633: return this .delegateIterator.hasNext();
634: }
635:
636: public Object next() {
637: this .currentObject = this .delegateIterator
638: .next();
639: return this .currentObject;
640: }
641:
642: public void remove() {
643: Iterator iterator = IndirectMap.this
644: .getDelegate().entrySet().iterator();
645: while (iterator.hasNext()) {
646: Map.Entry entry = (Map.Entry) iterator
647: .next();
648: if (entry.getValue().equals(currentObject)) {
649: IndirectMap.this
650: .raiseRemoveChangeEvent(entry
651: .getKey(), entry
652: .getValue());
653: }
654:
655: }
656: this .delegateIterator.remove();
657: }
658: };
659: }
660:
661: public Object[] toArray() {
662: return this .delegateCollection.toArray();
663: }
664:
665: public Object[] toArray(Object a[]) {
666: return this .delegateCollection.toArray(a);
667: }
668:
669: public boolean add(Object o) {
670: return this .delegateCollection.add(o);
671: }
672:
673: public boolean remove(Object o) {
674: Iterator iterator = IndirectMap.this .getDelegate()
675: .entrySet().iterator();
676: while (iterator.hasNext()) {
677: Map.Entry entry = (Map.Entry) iterator.next();
678: if (entry.getValue().equals(o)) {
679: IndirectMap.this .raiseRemoveChangeEvent(entry
680: .getKey(), entry.getValue());
681: }
682: return true;
683: }
684: return false;
685: }
686:
687: public boolean containsAll(Collection c) {
688: return this .delegateCollection.containsAll(c);
689: }
690:
691: public boolean addAll(Collection c) {
692: return this .delegateCollection.addAll(c);
693: }
694:
695: public boolean removeAll(Collection c) {
696: boolean result = false;
697: for (Iterator iterator = c.iterator(); iterator
698: .hasNext();) {
699: if (remove(iterator.next())) {
700: result = true;
701: }
702: }
703: return result;
704: }
705:
706: public boolean retainAll(Collection c) {
707: boolean result = false;
708: for (Iterator iterator = IndirectMap.this .entrySet()
709: .iterator(); iterator.hasNext();) {
710: Map.Entry entry = (Map.Entry) iterator.next();
711: if (!c.contains(entry.getValue())) {
712: iterator.remove();
713: result = true;
714: }
715: }
716: return result;
717: }
718:
719: public void clear() {
720: IndirectMap.this .clear();
721: }
722:
723: public boolean equals(Object o) {
724: return this .delegateCollection.equals(o);
725: }
726:
727: public int hashCode() {
728: return this.delegateCollection.hashCode();
729: }
730:
731: };
732: }
733: }
|