KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > oracle > toplink > essentials > indirection > IndirectSet


1 /*
2  * The contents of this file are subject to the terms
3  * of the Common Development and Distribution License
4  * (the "License"). You may not use this file except
5  * in compliance with the License.
6  *
7  * You can obtain a copy of the license at
8  * glassfish/bootstrap/legal/CDDLv1.0.txt or
9  * https://glassfish.dev.java.net/public/CDDLv1.0.html.
10  * See the License for the specific language governing
11  * permissions and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL
14  * HEADER in each file and include the License file at
15  * glassfish/bootstrap/legal/CDDLv1.0.txt. If applicable,
16  * add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your
18  * own identifying information: Portions Copyright [yyyy]
19  * [name of copyright owner]
20  */

21 // Copyright (c) 1998, 2006, Oracle. All rights reserved.
22
package oracle.toplink.essentials.indirection;
23
24 import java.security.AccessController JavaDoc;
25 import java.security.PrivilegedActionException JavaDoc;
26 import java.util.*;
27 import java.io.*;
28 import java.lang.reflect.Method JavaDoc;
29
30 import oracle.toplink.essentials.exceptions.DescriptorException;
31 import oracle.toplink.essentials.exceptions.QueryException;
32 import oracle.toplink.essentials.internal.localization.ToStringLocalization;
33 import oracle.toplink.essentials.internal.indirection.*;
34 import oracle.toplink.essentials.internal.security.PrivilegedAccessHelper;
35 import oracle.toplink.essentials.internal.security.PrivilegedGetMethod;
36 import oracle.toplink.essentials.internal.security.PrivilegedMethodInvoker;
37
38 /**
39  * IndirectSet is an example implementation of the Set protocol that
40  * allows a domain class to take advantage of TopLink Indirection
41  * without having to declare its instance variable as a ValueHolderInterface.
42  * <p> To use an IndirectSet:<ul>
43  * <li> Declare the appropriate instance variable with type Set (or Collection).
44  * <li> Send the message #useTransparentCollection() to the appropriate
45  * CollectionMapping.
46  * <li> Send the message #useCollectionClass(IndirectSet.class) to the same
47  * CollectionMapping. (The order of these two message sends is significant.)
48  * </ul>
49  * TopLink will place an IndirectSet in the instance variable when the
50  * containing domain object is read from the datatabase. With the first
51  * message sent to the IndirectSet, the contents
52  * are fetched from the database and normal Set behavior is resumed.
53  *
54  * <p>
55  * Implementation notes:<ul>
56  * <li> The Set interface is implemented by delegating nearly every message
57  * to the Set held on to by the 'delegate' instance variable. (The 'delegate'
58  * will be either a HashSet or yet another IndirectSet.)
59  * <li> The IndirectContainer interface is implemented in a straightforward
60  * fashion: <ul>
61  * <li> #get- and #setValueHolder() are implemented as simple accessors for the
62  * 'valueHolder' instance variable. (Note that #setValueHolder() clears out the
63  * 'delegate' instance variable, since its contents are invalidated by the arrival
64  * of a new value holder.)
65  * <li> #isInstantiated() is simply delegated to the value holder.
66  * </ul>
67  * <li> TopLink requires that the Cloneable interface be implemented. The #clone()
68  * method must clone the 'delegate'. (The implementation here uses reflection to invoke
69  * the #clone() method because it is not included in the common interface shared
70  * by IndirectSet and its base delegate class, HashSet; namely, Set.)
71  * <li> TopLink requires that the Serializable interface be implemented.
72  * <li> The database read is ultimately triggered when one of the "delegated"
73  * methods makes the first call to #getDelegate(), which in turn calls
74  * #buildDelegate(), which
75  * sends the message #getValue() to the value holder.
76  * The value holder performs the database read.
77  * <li> For debugging purposes, #toString() will <em>not</em> trigger a database
78  * read. This is not required behavior.
79  * </ul>
80  *
81  * @see oracle.toplink.essentials.mappings.CollectionMapping
82  * @author Big Country
83  * @since TOPLink/Java 3.0+
84  */

85 public class IndirectSet implements Set, IndirectContainer, Cloneable JavaDoc, Serializable {
86
87     /** Reduce type casting */
88     private Set delegate;
89
90     /** Delegate indirection behavior to a value holder */
91     private ValueHolderInterface valueHolder;
92     
93     /** The mapping attribute name, used to raise change events. */
94     private String JavaDoc attributeName;
95
96     /** Store initial size for lazy init. */
97     protected int initialCapacity = 10;
98
99     /** Store load factor for lazy init. */
100     protected float loadFactor = 0.75f;
101
102     /**
103      * Construct an empty IndirectSet.
104      */

105     public IndirectSet() {
106         this.delegate = null;
107         this.valueHolder = null;
108     }
109
110     /**
111      * Construct an empty IndirectSet with the specified initial capacity.
112      *
113      * @param initialCapacity the initial capacity of the set
114      * @exception IllegalArgumentException if the specified initial capacity
115      * is negative
116      */

117     public IndirectSet(int initialCapacity) {
118         this.delegate = null;
119         this.initialCapacity = initialCapacity;
120         this.valueHolder = null;
121     }
122
123     /**
124      * Construct an empty IndirectSet with the specified initial capacity and
125      * load factor.
126      *
127      * @param initialCapacity the initial capacity of the set
128      * @param loadFactor the load factor of the set
129      * @exception IllegalArgumentException if the specified initial capacity
130      * is negative
131      */

132     public IndirectSet(int initialCapacity, float loadFactor) {
133         this.delegate = null;
134         this.initialCapacity = initialCapacity;
135         this.loadFactor = loadFactor;
136         this.valueHolder = null;
137     }
138
139     /**
140      * Construct an IndirectSet containing the elements of the specified collection.
141      *
142      * @param c the initial elements of the set
143      */

144     public IndirectSet(Collection c) {
145         this.delegate = null;
146         this.valueHolder = new ValueHolder(new HashSet(c));
147     }
148
149     /**
150      * @see java.util.Set#add(java.lang.Object)
151      */

152     public synchronized boolean add(Object JavaDoc o) {
153         this.getDelegate().add(o);
154         this.raiseAddChangeEvent(o);
155         return true;
156     }
157
158     /**
159      * @see java.util.Set#addAll(java.util.Collection)
160      */

161     public synchronized boolean addAll(Collection c) {
162         // Must trigger add events if tracked or uow.
163
if (hasBeenRegistered()) {
164             Iterator objects = c.iterator();
165             while (objects.hasNext()) {
166                 this.add(objects.next());
167             }
168             return true;
169         }
170
171         return getDelegate().addAll(c);
172     }
173
174     /**
175      * Return the freshly-built delegate.
176      */

177     protected Set buildDelegate() {
178         return (Set)getValueHolder().getValue();
179     }
180
181     /**
182      * @see java.util.Set#clear()
183      */

184     public void clear() {
185         if (hasBeenRegistered()) {
186             Iterator objects = this.iterator();
187             while (objects.hasNext()) {
188                 Object JavaDoc o = objects.next();
189                 objects.remove();
190                 this.raiseRemoveChangeEvent(o);
191             }
192         } else {
193             this.getDelegate().clear();
194         }
195     }
196
197     /**
198      * @see java.lang.Object#clone()
199      * This will result in a database query if necessary.
200      */

201
202     /*
203         There are 3 situations when #clone() is called:
204         1. The developer actually wants to clone the collection (typically to modify one
205             of the 2 resulting collections). In which case the contents must be read from
206             the database.
207         2. A UnitOfWork needs a clone (or backup clone) of the collection. But the
208             UnitOfWork checks "instantiation" before cloning collections (i.e. "un-instantiated"
209             collections are not cloned).
210         3. A MergeManager needs an extra copy of the collection (because the "backup"
211             and "target" are the same object?). But the MergeManager also checks "instantiation"
212             before merging collections (again, "un-instantiated" collections are not merged).
213     */

214     public Object JavaDoc clone() {
215         try {
216             IndirectSet result = (IndirectSet)super.clone();
217             result.delegate = this.cloneDelegate();
218             result.attributeName = null;
219             return result;
220         } catch (CloneNotSupportedException JavaDoc e) {
221             throw new InternalError JavaDoc("clone not supported");
222         }
223     }
224
225     /**
226      * Clone the delegate.
227      */

228     protected Set cloneDelegate() {
229         java.lang.reflect.Method JavaDoc cloneMethod;
230         try {
231             if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){
232                 try {
233                     cloneMethod = (Method JavaDoc)AccessController.doPrivileged(new PrivilegedGetMethod(this.getDelegate().getClass(), "clone", (Class JavaDoc[])null, false));
234                 } catch (PrivilegedActionException JavaDoc exception) {
235                     throw QueryException.cloneMethodRequired();
236                 }
237             } else {
238                 cloneMethod = PrivilegedAccessHelper.getMethod(this.getDelegate().getClass(), "clone", (Class JavaDoc[])null, false);
239             }
240         } catch (NoSuchMethodException JavaDoc ex) {
241             throw QueryException.cloneMethodRequired();
242         }
243
244         try {
245             if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){
246                 try {
247                     return (Set)AccessController.doPrivileged(new PrivilegedMethodInvoker(cloneMethod, this.getDelegate(), (Object JavaDoc[])null));
248                 } catch (PrivilegedActionException JavaDoc exception) {
249                     Exception JavaDoc throwableException = exception.getException();
250                     if (throwableException instanceof IllegalAccessException JavaDoc) {
251                         throw QueryException.cloneMethodInaccessible();
252                     } else {
253                         throw QueryException.cloneMethodThrowException(((java.lang.reflect.InvocationTargetException JavaDoc)throwableException).getTargetException());
254                     }
255                 }
256             } else {
257                 return (Set)PrivilegedAccessHelper.invokeMethod(cloneMethod, this.getDelegate(), (Object JavaDoc[])null);
258             }
259         } catch (IllegalAccessException JavaDoc ex1) {
260             throw QueryException.cloneMethodInaccessible();
261         } catch (java.lang.reflect.InvocationTargetException JavaDoc ex2) {
262             throw QueryException.cloneMethodThrowException(ex2.getTargetException());
263         }
264     }
265
266     /**
267      * @see java.util.Set#contains(java.lang.Object)
268      */

269     public boolean contains(Object JavaDoc o) {
270         return this.getDelegate().contains(o);
271     }
272
273     /**
274      * @see java.util.Set#containsAll(java.util.Collection)
275      */

276     public boolean containsAll(Collection c) {
277         return this.getDelegate().containsAll(c);
278     }
279
280     /**
281      * @see java.util.Set#equals(java.lang.Object)
282      */

283     public boolean equals(Object JavaDoc o) {
284         return this.getDelegate().equals(o);
285     }
286
287     /**
288      * Check whether the contents have been read from the database.
289      * If they have not, read them and set the delegate.
290      */

291     protected Set getDelegate() {
292         if (delegate == null) {
293             delegate = this.buildDelegate();
294         }
295         return delegate;
296     }
297
298     /**
299      * Return the valueHolder.
300      */

301     public ValueHolderInterface getValueHolder() {
302         // PERF: lazy initialize value holder and vector as are normally set after creation.
303
if (valueHolder == null) {
304             valueHolder = new ValueHolder(new HashSet(initialCapacity, loadFactor));
305         }
306         return valueHolder;
307     }
308
309     /**
310      * INTERNAL:
311      * Return whether this IndirectSet has been registered in a UnitOfWork
312      */

313     public boolean hasBeenRegistered() {
314         return getValueHolder() instanceof oracle.toplink.essentials.internal.indirection.UnitOfWorkQueryValueHolder;
315     }
316
317     /**
318      * @see java.util.Set#hashCode()
319      */

320     public int hashCode() {
321         return this.getDelegate().hashCode();
322     }
323
324     /**
325      * @see java.util.Set#isEmpty()
326      */

327     public boolean isEmpty() {
328         return this.getDelegate().isEmpty();
329     }
330
331     /**
332      * Return whether the contents have been read from the database.
333      */

334     public boolean isInstantiated() {
335         return this.getValueHolder().isInstantiated();
336     }
337
338     /**
339      * @see java.util.Set#iterator()
340      */

341     public Iterator iterator() {
342         // Must wrap the interator to raise the remove event.
343
return new Iterator() {
344             Iterator delegateIterator = IndirectSet.this.getDelegate().iterator();
345             Object JavaDoc currentObject;
346             
347             public boolean hasNext() {
348                 return this.delegateIterator.hasNext();
349             }
350             
351             public Object JavaDoc next() {
352                 this.currentObject = this.delegateIterator.next();
353                 return this.currentObject;
354             }
355             
356             public void remove() {
357                 this.delegateIterator.remove();
358                 IndirectSet.this.raiseRemoveChangeEvent(this.currentObject);
359             }
360         };
361     }
362
363     /**
364      * @see java.util.Set#remove(java.lang.Object)
365      */

366     public synchronized boolean remove(Object JavaDoc o) {
367         if (this.getDelegate().remove(o)) {
368             this.raiseRemoveChangeEvent(o);
369             return true;
370         }
371         return false;
372     }
373
374     /**
375      * @see java.util.Set#removeAll(java.util.Collection)
376      */

377     public synchronized boolean removeAll(Collection c) {
378         // Must trigger remove events if tracked or uow.
379
if (hasBeenRegistered()) {
380             Iterator objects = c.iterator();
381             while (objects.hasNext()) {
382                 this.remove(objects.next());
383             }
384             return true;
385         }
386         return this.getDelegate().removeAll(c);
387     }
388
389     /**
390      * @see java.util.Set#retainAll(java.util.Collection)
391      */

392     public synchronized boolean retainAll(Collection c) {
393         // Must trigger remove events if tracked or uow.
394
if (hasBeenRegistered()) {
395             Iterator objects = getDelegate().iterator();
396             while (objects.hasNext()) {
397                 Object JavaDoc object = objects.next();
398                 if (!c.contains(object)) {
399                     objects.remove();
400                     this.raiseRemoveChangeEvent(object);
401                 }
402             }
403             return true;
404         }
405         return this.getDelegate().retainAll(c);
406     }
407
408     /**
409      * Set the value holder.
410      * Note that the delegate must be cleared out.
411      */

412     public void setValueHolder(ValueHolderInterface valueHolder) {
413         this.delegate = null;
414         this.valueHolder = valueHolder;
415     }
416
417     /**
418      * @see java.util.Set#size()
419      */

420     public int size() {
421         return this.getDelegate().size();
422     }
423
424     /**
425      * @see java.util.Set#toArray()
426      */

427     public Object JavaDoc[] toArray() {
428         return this.getDelegate().toArray();
429     }
430
431     /**
432      * @see java.util.Set#toArray(java.lang.Object[])
433      */

434     public Object JavaDoc[] toArray(Object JavaDoc[] a) {
435         return this.getDelegate().toArray(a);
436     }
437
438     /**
439      * Use the delegate's #toString(); but wrap it with braces to indicate
440      * there is a bit of indirection.
441      * Don't allow this method to trigger a database read.
442      * @see java.util.HashSet#toString()
443      */

444     public String JavaDoc toString() {
445         if (ValueHolderInterface.shouldToStringInstantiate) {
446             return this.getDelegate().toString();
447         }
448         if (this.isInstantiated()) {
449             return "{" + this.getDelegate().toString() + "}";
450         } else {
451             return "{" + oracle.toplink.essentials.internal.helper.Helper.getShortClassName(this.getClass()) + ": " + ToStringLocalization.buildMessage("not_instantiated", (Object JavaDoc[])null) + "}";
452
453         }
454     }
455     
456     /**
457      * Raise the add change event and relationship maintainence.
458      */

459     protected void raiseAddChangeEvent(Object JavaDoc element) {
460         if (hasBeenRegistered()) {
461             ((UnitOfWorkQueryValueHolder)getValueHolder()).updateForeignReferenceSet(element, null);
462         }
463     }
464     
465     /**
466      * Raise the remove change event.
467      */

468     protected void raiseRemoveChangeEvent(Object JavaDoc element) {
469         if (hasBeenRegistered()) {
470             ((UnitOfWorkQueryValueHolder)getValueHolder()).updateForeignReferenceRemove(element);
471         }
472     }
473      
474     /**
475      * Return the mapping attribute name, used to raise change events.
476      */

477      public String JavaDoc getTopLinkAttributeName() {
478          return attributeName;
479      }
480      
481     /**
482      * Set the mapping attribute name, used to raise change events.
483      * This is required if the change listener is set.
484      */

485      public void setTopLinkAttributeName(String JavaDoc attributeName) {
486          this.attributeName = attributeName;
487      }
488 }
489
Popular Tags