org.eclipse.persistence.internal.queries.ListContainerPolicy Maven / Gradle / Ivy
/*
* Copyright (c) 1998, 2023 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0,
* or the Eclipse Distribution License v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
// Contributors:
// Oracle - initial API and implementation from Oracle TopLink
// 08/15/2008-1.0.1 Chris Delahunt
// - 237545: List attribute types on OneToMany using @OrderBy does not work with attribute change tracking
package org.eclipse.persistence.internal.queries;
import org.eclipse.persistence.annotations.CacheKeyType;
import org.eclipse.persistence.internal.identitymaps.CacheId;
import org.eclipse.persistence.internal.sessions.AbstractRecord;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.internal.sessions.ChangeRecord;
import org.eclipse.persistence.internal.sessions.CollectionChangeRecord;
import org.eclipse.persistence.internal.sessions.ObjectChangeSet;
import org.eclipse.persistence.internal.sessions.UnitOfWorkChangeSet;
import org.eclipse.persistence.internal.sessions.UnitOfWorkImpl;
import org.eclipse.persistence.mappings.ForeignReferenceMapping;
import org.eclipse.persistence.queries.ReadAllQuery;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
* Purpose: A ListContainerPolicy is ContainerPolicy whose container class
* implements the List interface. This signifies that the collection has order
*
Responsibilities:
* Provide the functionality to operate on an instance of a List.
*
* @see ContainerPolicy
* @see CollectionContainerPolicy
*/
public class ListContainerPolicy extends CollectionContainerPolicy {
/**
* INTERNAL:
* Construct a new policy.
*/
public ListContainerPolicy() {
super();
}
/**
* INTERNAL:
* Construct a new policy for the specified class.
*/
public ListContainerPolicy(Class containerClass) {
super(containerClass);
}
/**
* INTERNAL:
* Construct a new policy for the specified class name.
*/
public ListContainerPolicy(String containerClassName) {
super(containerClassName);
}
/**
* INTERNAL:
* Returns the element at the specified position in this list.
* The session may be required to unwrap for the wrapper policy.
*/
public Object get(int index, Object container, AbstractSession session){
if ( (index <0) || (index>= sizeFor(container)) ) {
return null;
}
Object object = ((List)container).get(index);
if (hasElementDescriptor() && getElementDescriptor().hasWrapperPolicy()) {
object = getElementDescriptor().getObjectBuilder().unwrapObject(object, session);
}
return object;
}
/**
* INTERNAL:
* Validate the container type.
*/
@Override
public boolean isValidContainer(Object container) {
// PERF: Use instanceof which is inlined, not isAssignable which is very inefficent.
return container instanceof List;
}
/**
* INTERNAL:
* Returns true if the collection has order
*
* @see ContainerPolicy#iteratorFor(java.lang.Object)
*/
@Override
public boolean hasOrder() {
return true;
}
@Override
public boolean isListPolicy() {
return true;
}
/**
* INTERNAL:
* Returns the index in this list of the first occurrence of the specified element,
* or -1 if this list does not contain this element
* The session may be required to unwrap for the wrapper policy.
*/
public int indexOf(Object element, Object container, AbstractSession session) {
if (hasElementDescriptor() && getElementDescriptor().hasWrapperPolicy()) {
// The wrapper for the object must be removed.
int count = -1;
Object iterator = iteratorFor(container);
while (hasNext(iterator)) {
count++;
Object next = next(iterator);
if (getElementDescriptor().getObjectBuilder().unwrapObject(next, session).equals(element)) {
return count;
}
}
return -1;
} else {
return ((List)container).indexOf(element);
}
}
/**
* This method is used to bridge the behavior between Attribute Change Tracking and
* deferred change tracking with respect to adding the same instance multiple times.
* Each ContainerPolicy type will implement specific behavior for the collection
* type it is wrapping. These methods are only valid for collections containing object references
*/
@Override
public void recordAddToCollectionInChangeRecord(ObjectChangeSet changeSetToAdd, CollectionChangeRecord collectionChangeRecord){
if (collectionChangeRecord.getRemoveObjectList().containsKey(changeSetToAdd)) {
collectionChangeRecord.getRemoveObjectList().remove(changeSetToAdd);
} else {
if (collectionChangeRecord.getAddObjectList().containsKey(changeSetToAdd)){
collectionChangeRecord.getAddOverFlow().add(changeSetToAdd);
}else{
collectionChangeRecord.getAddObjectList().put(changeSetToAdd, changeSetToAdd);
}
}
}
/**
* This method is used to bridge the behavior between Attribute Change Tracking and
* deferred change tracking with respect to adding the same instance multiple times.
* Each ContainerPolicy type will implement specific behavior for the collection
* type it is wrapping. These methods are only valid for collections containing object references
*/
@Override
public void recordRemoveFromCollectionInChangeRecord(ObjectChangeSet changeSetToRemove, CollectionChangeRecord collectionChangeRecord){
if(collectionChangeRecord.getAddObjectList().containsKey(changeSetToRemove)) {
if (collectionChangeRecord.getAddOverFlow().contains(changeSetToRemove)){
collectionChangeRecord.getAddOverFlow().remove(changeSetToRemove);
}else {
collectionChangeRecord.getAddObjectList().remove(changeSetToRemove);
}
} else {
collectionChangeRecord.getRemoveObjectList().put(changeSetToRemove, changeSetToRemove);
}
}
/**
* INTERNAL:
* Update a ChangeRecord to replace the ChangeSet for the old entity with the changeSet for the new Entity. This is
* used when an Entity is merged into itself and the Entity reference new or detached entities.
*/
@Override
public void updateChangeRecordForSelfMerge(ChangeRecord changeRecord, Object source, Object target, ForeignReferenceMapping mapping, UnitOfWorkChangeSet parentUOWChangeSet, UnitOfWorkImpl unitOfWork){
Map list = ((CollectionChangeRecord)changeRecord).getAddObjectList();
ObjectChangeSet sourceSet = parentUOWChangeSet.getCloneToObjectChangeSet().get(source);
if (list.containsKey(sourceSet)){
ObjectChangeSet targetSet = ((UnitOfWorkChangeSet)unitOfWork.getUnitOfWorkChangeSet()).findOrCreateLocalObjectChangeSet(target, mapping.getReferenceDescriptor(), unitOfWork.isCloneNewObject(target));
parentUOWChangeSet.addObjectChangeSetForIdentity(targetSet, target);
list.remove(sourceSet);
list.put(targetSet, targetSet);
return;
}
List overFlow = ((CollectionChangeRecord)changeRecord).getAddOverFlow();
int index = 0;
for (ObjectChangeSet changeSet: overFlow){
if (changeSet == sourceSet){
overFlow.set(index,((UnitOfWorkChangeSet)unitOfWork.getUnitOfWorkChangeSet()).findOrCreateLocalObjectChangeSet(target, mapping.getReferenceDescriptor(), unitOfWork.isCloneNewObject(target)));
return;
}
++index;
}
}
/**
* INTERNAL:
* This method is used to load a relationship from a list of PKs. This list
* may be available if the relationship has been cached.
*/
@Override
public Object valueFromPKList(Object[] pks, AbstractRecord foreignKeys, ForeignReferenceMapping mapping, AbstractSession session){
Object result = containerInstance(pks.length);
Map