FileDocCategorySizeDatePackage
OrderedListContainerPolicy.javaAPI DocGlassfish v2 API16812Tue May 22 16:54:40 BST 2007oracle.toplink.essentials.internal.queryframework

OrderedListContainerPolicy

public class OrderedListContainerPolicy extends ListContainerPolicy

Purpose: A OrderedListContainerPolicy is ContainerPolicy whose container class implements the List interface and is ordered by an @OrderBy.

Responsibilities: Provide the functionality to operate on an instance of a List.

see
ContainerPolicy
see
CollectionContainerPolicy
see
ListContainerPolicy

Fields Summary
Constructors Summary
public OrderedListContainerPolicy()
INTERNAL: Construct a new policy.

        super();
    
public OrderedListContainerPolicy(Class containerClass)
INTERNAL: Construct a new policy for the specified class.

        super(containerClass);
    
public OrderedListContainerPolicy(String containerClassName)
INTERNAL: Construct a new policy for the specified class name.

        super(containerClassName);
    
Methods Summary
protected voidaddIntoAtIndex(java.lang.Integer index, java.lang.Object object, java.lang.Object container, oracle.toplink.essentials.internal.sessions.AbstractSession session)
INTERNAL: Add element into a container which implements the List interface. Add at a particular index.

        if (hasElementDescriptor()) {
            object = getElementDescriptor().getObjectBuilder().wrapObject(object, session);
        }
        
        try {
            if (index == null || (index.intValue() > sizeFor(container))) {
                // The index can be larger than the size on a merge,
                // so should be added to the end, it may also be null if the 
                // index was unknown, such as an event using the add API.
                ((List)container).add(object);
            } else {
                ((List)container).add(index.intValue(), object);
            }
        } catch (ClassCastException ex1) {
            throw QueryException.cannotAddElement(object, container, ex1);
        } catch (IllegalArgumentException ex2) {
            throw QueryException.cannotAddElement(object, container, ex2);
        } catch (UnsupportedOperationException ex3) {
            throw QueryException.cannotAddElement(object, container, ex3);
        }
    
public voidcompareCollectionsForChange(java.lang.Object oldList, java.lang.Object newList, oracle.toplink.essentials.internal.sessions.CollectionChangeRecord changeRecord, oracle.toplink.essentials.internal.sessions.AbstractSession session, oracle.toplink.essentials.descriptors.ClassDescriptor referenceDescriptor)
INTERNAL: This method is used to calculate the differences between two collections. This algorithm is a work in progress. It works great and all, but like anything, you can always make it better.

    
        Vector orderedObjectsToAdd = new Vector();
        Hashtable indicesToRemove = new Hashtable();
        Hashtable oldListIndexValue = new Hashtable();
        IdentityHashMap oldListValueIndex = new IdentityHashMap();
        IdentityHashMap objectsToAdd = new IdentityHashMap();
        IdentityHashtable newListValueIndex = new IdentityHashtable();
        
        // Step 1 - Go through the old list.
        if (oldList != null) {
            ListIterator iterator = iteratorFor(oldList);
        
            while (iterator.hasNext()) {
                Integer index = new Integer(iterator.nextIndex());
                Object value = iterator.next();
                oldListValueIndex.put(value, index);
                oldListIndexValue.put(index, value);
                indicesToRemove.put(index, index);
            }
        }
            
        // Step 2 - Go though the new list.
        if (newList != null) {
            // Step i - Gather the list info.
            ListIterator iterator = iteratorFor(newList);
            while (iterator.hasNext()) {
                newListValueIndex.put(iterator.next(), new Integer(iterator.previousIndex()));
            }
        
            // Step ii - Go through the new list again.        
            int index = 0;
            int offset = 0;
            iterator = iteratorFor(newList);
            while (iterator.hasNext()) {
                index = iterator.nextIndex();
                Object currentObject = iterator.next();
            
                // If value is null then nothing can be done with it.
                if (currentObject != null) {
                    if (oldListValueIndex.containsKey(currentObject)) {
                        int oldIndex = ((Integer) oldListValueIndex.get(currentObject)).intValue();
                        oldListValueIndex.remove(currentObject);
                    
                        if (index == oldIndex) {
                            indicesToRemove.remove(new Integer(oldIndex));
                            offset = 0; // Reset the offset, assume we're back on track.
                        } else if (index == (oldIndex + offset)) {
                            // We're in the right spot according to the offset.
                            indicesToRemove.remove(new Integer(oldIndex));
                        } else {
                            // Time to be clever and figure out why we're not in the right spot!
                            int movedObjects = 0;
                            int deletedObjects = 0;
                            boolean moved = true;
                        
                            if (oldIndex < index) {
                                ++offset;    
                            } else {
                                for (int i = oldIndex - 1; i >= index; i--) {
                                    Object oldObject = oldListIndexValue.get(new Integer(i));
                                    if (newListValueIndex.containsKey(oldObject)) {
                                        ++movedObjects;
                                    } else {
                                        ++deletedObjects;
                                    }
                                }
                            
                                if (index == ((oldIndex + offset) - deletedObjects)) {
                                    // We fell into place because of deleted objects.
                                    offset = offset - deletedObjects;
                                    moved = false;
                                } else if (movedObjects > 1) {
                                    // Assume we moved down, bumping everyone by one. 
                                    ++offset;
                                } else {
                                    // Assume we moved down unless the object that was
                                    // here before is directly beside us.
                                    Object oldObject = oldListIndexValue.get(new Integer(index));
                                
                                    if (newListValueIndex.containsKey(oldObject)) {
                                        if ((((Integer) newListValueIndex.get(oldObject)).intValue() - index) > 1) {
                                            moved = false; // Assume the old object moved up.
                                            --offset; 
                                        }
                                    }
                                }
                            }
                        
                            if (moved) {
                                // Add ourselves to the ordered add list.
                                orderedObjectsToAdd.add(currentObject);
                            } else {
                                // Take us off the removed list.
                                indicesToRemove.remove(new Integer(oldIndex));    
                            }
                        }
                    } else {
                        ++offset;
                        objectsToAdd.put(currentObject, currentObject);
                        orderedObjectsToAdd.add(currentObject);
                    }
                } else {
                    // If we find nulls we need decrease our offset.
                    offset--;
                }
            }
        }
        
        // Sort the remove indices that are left and set the data on the collection change 
        // record to be processed on the merge.
        Vector orderedIndicesToRemove = new Vector(indicesToRemove.values());
        Collections.sort(orderedIndicesToRemove);
        changeRecord.addAdditionChange(objectsToAdd, (UnitOfWorkChangeSet) changeRecord.getOwner().getUOWChangeSet(), session);
        changeRecord.addRemoveChange(oldListValueIndex, (UnitOfWorkChangeSet) changeRecord.getOwner().getUOWChangeSet(), session);
        changeRecord.addOrderedAdditionChange(orderedObjectsToAdd, newListValueIndex, (UnitOfWorkChangeSet) changeRecord.getOwner().getUOWChangeSet(), session);
        changeRecord.addOrderedRemoveChange(orderedIndicesToRemove, oldListIndexValue, (UnitOfWorkChangeSet) changeRecord.getOwner().getUOWChangeSet(), session);                
    
public java.util.ListIteratoriteratorFor(java.lang.Object container)
INTERNAL: Return an list iterator for the given container.

        return ((List)container).listIterator();
    
public voidmergeChanges(oracle.toplink.essentials.internal.sessions.CollectionChangeRecord changeRecord, java.lang.Object valueOfTarget, boolean shouldMergeCascadeParts, oracle.toplink.essentials.internal.sessions.MergeManager mergeManager, oracle.toplink.essentials.internal.sessions.AbstractSession parentSession)
INTERNAL: Merge changes from the source to the target object. Because this is a collection mapping, values are added to or removed from the collection based on the change set.

        ObjectChangeSet objectChanges;
        
        synchronized (valueOfTarget) {
            // Step 1 - iterate over the removed changes and remove them from the container.
            Vector removedIndices = changeRecord.getOrderedRemoveObjectIndices();

            if (removedIndices.isEmpty()) {
                // Check if we have removed objects via a 
                // simpleRemoveFromCollectionChangeRecord API call.
                Enumeration removedObjects = changeRecord.getRemoveObjectList().keys();
            
                while (removedObjects.hasMoreElements()) {
                    objectChanges = (ObjectChangeSet) removedObjects.nextElement();
                    removeFrom(objectChanges.getOldKey(), objectChanges.getTargetVersionOfSourceObject(mergeManager.getSession()), valueOfTarget, parentSession);
                    registerRemoveNewObjectIfRequired(objectChanges, mergeManager);
                }
            } else {
                for (int i = removedIndices.size() - 1; i >= 0; i--) {
                    Integer index = ((Integer) removedIndices.elementAt(i)).intValue();
                    objectChanges = (ObjectChangeSet) changeRecord.getOrderedRemoveObject(index);;
                    removeFromAtIndex(index, valueOfTarget);
                
                    // The object was actually removed and not moved.
                    if (changeRecord.getRemoveObjectList().containsKey(objectChanges)) {
                        registerRemoveNewObjectIfRequired(objectChanges, mergeManager);
                    }
                }
            }
            
            // Step 2 - iterate over the added changes and add them to the container.
            Enumeration addObjects = changeRecord.getOrderedAddObjects().elements();
            while (addObjects.hasMoreElements()) {
                objectChanges =  (ObjectChangeSet) addObjects.nextElement();
                boolean objectAdded = changeRecord.getAddObjectList().containsKey(objectChanges);
                Object object = null;
                
                // The object was actually added and not moved.
                if (objectAdded && shouldMergeCascadeParts) {
                    object = mergeCascadeParts(objectChanges, mergeManager, parentSession);
                }
                
                if (object == null) {
                    // Retrieve the object to be added to the collection.
                    object = objectChanges.getTargetVersionOfSourceObject(mergeManager.getSession());
                }

                // Assume at this point the above merge will have created a new 
                // object if required and that the object was actually added and 
                // not moved.
                if (objectAdded && mergeManager.shouldMergeChangesIntoDistributedCache()) {
                    // Bugs 4458089 & 4454532 - check if collection contains new item before adding 
                    // during merge into distributed cache					
                    if (! contains(object, valueOfTarget, mergeManager.getSession())) {
                        addIntoAtIndex(changeRecord.getOrderedAddObjectIndex(objectChanges), object, valueOfTarget, mergeManager.getSession());                                
                    }
                } else {
                    addIntoAtIndex(changeRecord.getOrderedAddObjectIndex(objectChanges), object, valueOfTarget, mergeManager.getSession());
                }
            }
        }
    
protected voidregisterRemoveNewObjectIfRequired(oracle.toplink.essentials.internal.sessions.ObjectChangeSet objectChanges, oracle.toplink.essentials.internal.sessions.MergeManager mergeManager)
INTERNAL:

        if (! mergeManager.shouldMergeChangesIntoDistributedCache()) {
            mergeManager.registerRemovedNewObjectIfRequired(objectChanges.getUnitOfWorkClone());
        }
    
protected voidremoveFromAtIndex(int index, java.lang.Object container)
INTERNAL: Remove the element at the specified index.

        try {
            ((List) container).remove(index);
        } catch (ClassCastException ex1) {
            throw QueryException.cannotRemoveFromContainer(new Integer(index), container, this);
        } catch (IllegalArgumentException ex2) {
            throw QueryException.cannotRemoveFromContainer(new Integer(index), container, this);
        } catch (UnsupportedOperationException ex3) {
            throw QueryException.cannotRemoveFromContainer(new Integer(index), container, this);
        }