KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > oracle > toplink > essentials > internal > queryframework > OrderedListContainerPolicy


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.internal.queryframework;
23
24 import java.util.List JavaDoc;
25 import java.util.Vector JavaDoc;
26 import java.util.Hashtable JavaDoc;
27 import java.util.Collections JavaDoc;
28 import java.util.Enumeration JavaDoc;
29 import java.util.IdentityHashMap JavaDoc;
30 import java.util.ListIterator JavaDoc;
31 import oracle.toplink.essentials.exceptions.QueryException;
32 import oracle.toplink.essentials.internal.helper.IdentityHashtable;
33 import oracle.toplink.essentials.internal.sessions.MergeManager;
34 import oracle.toplink.essentials.internal.sessions.ObjectChangeSet;
35 import oracle.toplink.essentials.internal.sessions.UnitOfWorkChangeSet;
36 import oracle.toplink.essentials.internal.sessions.CollectionChangeRecord;
37 import oracle.toplink.essentials.internal.sessions.AbstractSession;
38 import oracle.toplink.essentials.descriptors.ClassDescriptor;
39
40 /**
41  * <p><b>Purpose</b>: A OrderedListContainerPolicy is ContainerPolicy whose
42  * container class implements the List interface and is ordered by an @OrderBy.
43  * <p>
44  * <p><b>Responsibilities</b>:
45  * Provide the functionality to operate on an instance of a List.
46  *
47  * @see ContainerPolicy
48  * @see CollectionContainerPolicy
49  * @see ListContainerPolicy
50  */

51 public class OrderedListContainerPolicy extends ListContainerPolicy {
52     /**
53      * INTERNAL:
54      * Construct a new policy.
55      */

56     public OrderedListContainerPolicy() {
57         super();
58     }
59     
60     /**
61      * INTERNAL:
62      * Construct a new policy for the specified class.
63      */

64     public OrderedListContainerPolicy(Class JavaDoc containerClass) {
65         super(containerClass);
66     }
67     
68     /**
69      * INTERNAL:
70      * Construct a new policy for the specified class name.
71      */

72     public OrderedListContainerPolicy(String JavaDoc containerClassName) {
73         super(containerClassName);
74     }
75     
76     /**
77      * INTERNAL:
78      * Add element into a container which implements the List interface.
79      * Add at a particular index.
80      */

81     protected void addIntoAtIndex(Integer JavaDoc index, Object JavaDoc object, Object JavaDoc container, AbstractSession session) {
82         if (hasElementDescriptor()) {
83             object = getElementDescriptor().getObjectBuilder().wrapObject(object, session);
84         }
85         
86         try {
87             if (index == null || (index.intValue() > sizeFor(container))) {
88                 // The index can be larger than the size on a merge,
89
// so should be added to the end, it may also be null if the
90
// index was unknown, such as an event using the add API.
91
((List JavaDoc)container).add(object);
92             } else {
93                 ((List JavaDoc)container).add(index.intValue(), object);
94             }
95         } catch (ClassCastException JavaDoc ex1) {
96             throw QueryException.cannotAddElement(object, container, ex1);
97         } catch (IllegalArgumentException JavaDoc ex2) {
98             throw QueryException.cannotAddElement(object, container, ex2);
99         } catch (UnsupportedOperationException JavaDoc ex3) {
100             throw QueryException.cannotAddElement(object, container, ex3);
101         }
102     }
103     
104     /**
105      * INTERNAL:
106      * This method is used to calculate the differences between two collections.
107      * This algorithm is a work in progress. It works great and all, but like
108      * anything, you can always make it better.
109      */

110     public void compareCollectionsForChange(Object JavaDoc oldList, Object JavaDoc newList, CollectionChangeRecord changeRecord, AbstractSession session, ClassDescriptor referenceDescriptor) {
111         Vector JavaDoc orderedObjectsToAdd = new Vector JavaDoc();
112         Hashtable JavaDoc indicesToRemove = new Hashtable JavaDoc();
113         Hashtable JavaDoc oldListIndexValue = new Hashtable JavaDoc();
114         IdentityHashMap JavaDoc oldListValueIndex = new IdentityHashMap JavaDoc();
115         IdentityHashMap JavaDoc objectsToAdd = new IdentityHashMap JavaDoc();
116         IdentityHashtable newListValueIndex = new IdentityHashtable();
117         
118         // Step 1 - Go through the old list.
119
if (oldList != null) {
120             ListIterator JavaDoc iterator = iteratorFor(oldList);
121         
122             while (iterator.hasNext()) {
123                 Integer JavaDoc index = new Integer JavaDoc(iterator.nextIndex());
124                 Object JavaDoc value = iterator.next();
125                 oldListValueIndex.put(value, index);
126                 oldListIndexValue.put(index, value);
127                 indicesToRemove.put(index, index);
128             }
129         }
130             
131         // Step 2 - Go though the new list.
132
if (newList != null) {
133             // Step i - Gather the list info.
134
ListIterator JavaDoc iterator = iteratorFor(newList);
135             while (iterator.hasNext()) {
136                 newListValueIndex.put(iterator.next(), new Integer JavaDoc(iterator.previousIndex()));
137             }
138         
139             // Step ii - Go through the new list again.
140
int index = 0;
141             int offset = 0;
142             iterator = iteratorFor(newList);
143             while (iterator.hasNext()) {
144                 index = iterator.nextIndex();
145                 Object JavaDoc currentObject = iterator.next();
146             
147                 // If value is null then nothing can be done with it.
148
if (currentObject != null) {
149                     if (oldListValueIndex.containsKey(currentObject)) {
150                         int oldIndex = ((Integer JavaDoc) oldListValueIndex.get(currentObject)).intValue();
151                         oldListValueIndex.remove(currentObject);
152                     
153                         if (index == oldIndex) {
154                             indicesToRemove.remove(new Integer JavaDoc(oldIndex));
155                             offset = 0; // Reset the offset, assume we're back on track.
156
} else if (index == (oldIndex + offset)) {
157                             // We're in the right spot according to the offset.
158
indicesToRemove.remove(new Integer JavaDoc(oldIndex));
159                         } else {
160                             // Time to be clever and figure out why we're not in the right spot!
161
int movedObjects = 0;
162                             int deletedObjects = 0;
163                             boolean moved = true;
164                         
165                             if (oldIndex < index) {
166                                 ++offset;
167                             } else {
168                                 for (int i = oldIndex - 1; i >= index; i--) {
169                                     Object JavaDoc oldObject = oldListIndexValue.get(new Integer JavaDoc(i));
170                                     if (newListValueIndex.containsKey(oldObject)) {
171                                         ++movedObjects;
172                                     } else {
173                                         ++deletedObjects;
174                                     }
175                                 }
176                             
177                                 if (index == ((oldIndex + offset) - deletedObjects)) {
178                                     // We fell into place because of deleted objects.
179
offset = offset - deletedObjects;
180                                     moved = false;
181                                 } else if (movedObjects > 1) {
182                                     // Assume we moved down, bumping everyone by one.
183
++offset;
184                                 } else {
185                                     // Assume we moved down unless the object that was
186
// here before is directly beside us.
187
Object JavaDoc oldObject = oldListIndexValue.get(new Integer JavaDoc(index));
188                                 
189                                     if (newListValueIndex.containsKey(oldObject)) {
190                                         if ((((Integer JavaDoc) newListValueIndex.get(oldObject)).intValue() - index) > 1) {
191                                             moved = false; // Assume the old object moved up.
192
--offset;
193                                         }
194                                     }
195                                 }
196                             }
197                         
198                             if (moved) {
199                                 // Add ourselves to the ordered add list.
200
orderedObjectsToAdd.add(currentObject);
201                             } else {
202                                 // Take us off the removed list.
203
indicesToRemove.remove(new Integer JavaDoc(oldIndex));
204                             }
205                         }
206                     } else {
207                         ++offset;
208                         objectsToAdd.put(currentObject, currentObject);
209                         orderedObjectsToAdd.add(currentObject);
210                     }
211                 } else {
212                     // If we find nulls we need decrease our offset.
213
offset--;
214                 }
215             }
216         }
217         
218         // Sort the remove indices that are left and set the data on the collection change
219
// record to be processed on the merge.
220
Vector JavaDoc orderedIndicesToRemove = new Vector JavaDoc(indicesToRemove.values());
221         Collections.sort(orderedIndicesToRemove);
222         changeRecord.addAdditionChange(objectsToAdd, (UnitOfWorkChangeSet) changeRecord.getOwner().getUOWChangeSet(), session);
223         changeRecord.addRemoveChange(oldListValueIndex, (UnitOfWorkChangeSet) changeRecord.getOwner().getUOWChangeSet(), session);
224         changeRecord.addOrderedAdditionChange(orderedObjectsToAdd, newListValueIndex, (UnitOfWorkChangeSet) changeRecord.getOwner().getUOWChangeSet(), session);
225         changeRecord.addOrderedRemoveChange(orderedIndicesToRemove, oldListIndexValue, (UnitOfWorkChangeSet) changeRecord.getOwner().getUOWChangeSet(), session);
226     }
227     
228     /**
229      * INTERNAL:
230      * Return an list iterator for the given container.
231      */

232     public ListIterator JavaDoc iteratorFor(Object JavaDoc container) {
233         return ((List JavaDoc)container).listIterator();
234     }
235     
236     /**
237      * INTERNAL:
238      * Merge changes from the source to the target object. Because this is a
239      * collection mapping, values are added to or removed from the collection
240      * based on the change set.
241      */

242     public void mergeChanges(CollectionChangeRecord changeRecord, Object JavaDoc valueOfTarget, boolean shouldMergeCascadeParts, MergeManager mergeManager, AbstractSession parentSession) {
243         ObjectChangeSet objectChanges;
244         
245         synchronized (valueOfTarget) {
246             // Step 1 - iterate over the removed changes and remove them from the container.
247
Vector JavaDoc removedIndices = changeRecord.getOrderedRemoveObjectIndices();
248
249             if (removedIndices.isEmpty()) {
250                 // Check if we have removed objects via a
251
// simpleRemoveFromCollectionChangeRecord API call.
252
Enumeration JavaDoc removedObjects = changeRecord.getRemoveObjectList().keys();
253             
254                 while (removedObjects.hasMoreElements()) {
255                     objectChanges = (ObjectChangeSet) removedObjects.nextElement();
256                     removeFrom(objectChanges.getOldKey(), objectChanges.getTargetVersionOfSourceObject(mergeManager.getSession()), valueOfTarget, parentSession);
257                     registerRemoveNewObjectIfRequired(objectChanges, mergeManager);
258                 }
259             } else {
260                 for (int i = removedIndices.size() - 1; i >= 0; i--) {
261                     Integer JavaDoc index = ((Integer JavaDoc) removedIndices.elementAt(i)).intValue();
262                     objectChanges = (ObjectChangeSet) changeRecord.getOrderedRemoveObject(index);;
263                     removeFromAtIndex(index, valueOfTarget);
264                 
265                     // The object was actually removed and not moved.
266
if (changeRecord.getRemoveObjectList().containsKey(objectChanges)) {
267                         registerRemoveNewObjectIfRequired(objectChanges, mergeManager);
268                     }
269                 }
270             }
271             
272             // Step 2 - iterate over the added changes and add them to the container.
273
Enumeration JavaDoc addObjects = changeRecord.getOrderedAddObjects().elements();
274             while (addObjects.hasMoreElements()) {
275                 objectChanges = (ObjectChangeSet) addObjects.nextElement();
276                 boolean objectAdded = changeRecord.getAddObjectList().containsKey(objectChanges);
277                 Object JavaDoc object = null;
278                 
279                 // The object was actually added and not moved.
280
if (objectAdded && shouldMergeCascadeParts) {
281                     object = mergeCascadeParts(objectChanges, mergeManager, parentSession);
282                 }
283                 
284                 if (object == null) {
285                     // Retrieve the object to be added to the collection.
286
object = objectChanges.getTargetVersionOfSourceObject(mergeManager.getSession());
287                 }
288
289                 // Assume at this point the above merge will have created a new
290
// object if required and that the object was actually added and
291
// not moved.
292
if (objectAdded && mergeManager.shouldMergeChangesIntoDistributedCache()) {
293                     // Bugs 4458089 & 4454532 - check if collection contains new item before adding
294
// during merge into distributed cache
295
if (! contains(object, valueOfTarget, mergeManager.getSession())) {
296                         addIntoAtIndex(changeRecord.getOrderedAddObjectIndex(objectChanges), object, valueOfTarget, mergeManager.getSession());
297                     }
298                 } else {
299                     addIntoAtIndex(changeRecord.getOrderedAddObjectIndex(objectChanges), object, valueOfTarget, mergeManager.getSession());
300                 }
301             }
302         }
303     }
304     
305     /**
306      * INTERNAL:
307      */

308     protected void registerRemoveNewObjectIfRequired(ObjectChangeSet objectChanges, MergeManager mergeManager) {
309         if (! mergeManager.shouldMergeChangesIntoDistributedCache()) {
310             mergeManager.registerRemovedNewObjectIfRequired(objectChanges.getUnitOfWorkClone());
311         }
312     }
313     
314     /**
315      * INTERNAL:
316      * Remove the element at the specified index.
317      */

318     protected void removeFromAtIndex(int index, Object JavaDoc container) {
319         try {
320             ((List JavaDoc) container).remove(index);
321         } catch (ClassCastException JavaDoc ex1) {
322             throw QueryException.cannotRemoveFromContainer(new Integer JavaDoc(index), container, this);
323         } catch (IllegalArgumentException JavaDoc ex2) {
324             throw QueryException.cannotRemoveFromContainer(new Integer JavaDoc(index), container, this);
325         } catch (UnsupportedOperationException JavaDoc ex3) {
326             throw QueryException.cannotRemoveFromContainer(new Integer JavaDoc(index), container, this);
327         }
328     }
329 }
330
Popular Tags