Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright (c) 1998, 2024 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.OrderCorrectionType;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.descriptors.changetracking.CollectionChangeEvent;
import org.eclipse.persistence.exceptions.QueryException;
import org.eclipse.persistence.exceptions.ValidationException;
import org.eclipse.persistence.expressions.Expression;
import org.eclipse.persistence.indirection.IndirectCollection;
import org.eclipse.persistence.indirection.IndirectList;
import org.eclipse.persistence.internal.descriptors.ObjectBuilder;
import org.eclipse.persistence.internal.expressions.SQLSelectStatement;
import org.eclipse.persistence.internal.helper.ConversionManager;
import org.eclipse.persistence.internal.helper.DatabaseField;
import org.eclipse.persistence.internal.helper.DatabaseTable;
import org.eclipse.persistence.internal.helper.IndexedObject;
import org.eclipse.persistence.internal.identitymaps.CacheKey;
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.MergeManager;
import org.eclipse.persistence.internal.sessions.ObjectChangeSet;
import org.eclipse.persistence.internal.sessions.OrderedChangeObject;
import org.eclipse.persistence.internal.sessions.UnitOfWorkChangeSet;
import org.eclipse.persistence.internal.sessions.UnitOfWorkImpl;
import org.eclipse.persistence.mappings.CollectionMapping;
import org.eclipse.persistence.mappings.DatabaseMapping;
import org.eclipse.persistence.mappings.ForeignReferenceMapping;
import org.eclipse.persistence.queries.DataReadQuery;
import org.eclipse.persistence.queries.ObjectBuildingQuery;
import org.eclipse.persistence.queries.ObjectLevelReadQuery;
import org.eclipse.persistence.queries.ReadQuery;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
/**
*
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
*/
public class OrderedListContainerPolicy extends ListContainerPolicy {
protected static final String NOT_SET = "NOT_SET";
protected DatabaseField listOrderField;
protected OrderCorrectionType orderCorrectionType;
/**
* INTERNAL:
* Construct a new policy.
*/
public OrderedListContainerPolicy() {
super();
}
/**
* INTERNAL:
* Construct a new policy for the specified class.
*/
public OrderedListContainerPolicy(Class containerClass) {
super(containerClass);
}
/**
* INTERNAL:
* Construct a new policy for the specified class name.
*/
public OrderedListContainerPolicy(String containerClassName) {
super(containerClassName);
}
/**
* INTERNAL:
* Add a list of elements to container.
* This is used to add to a collection independent of JDK 1.1 and 1.2.
* The session may be required to wrap for the wrapper policy.
* The row may be required by subclasses
* Return whether the container changed
*/
@Override
public boolean addAll(List elements, Object container, AbstractSession session, List dbRows, ObjectBuildingQuery query, CacheKey parentCacheKey, boolean isTargetProtected) {
if(this.listOrderField == null) {
return super.addAll(elements, container, session, dbRows, query, parentCacheKey, isTargetProtected);
} else {
return addAll(elements, container, session, dbRows, query, parentCacheKey);
}
}
/**
* INTERNAL:
* Add a list of elements to container.
* This is used to add to a collection independent of JDK 1.1 and 1.2.
* The session may be required to wrap for the wrapper policy.
* The row may be required by subclasses
* Return whether the container changed
*/
@Override
public boolean addAll(List elements, Object container, AbstractSession session, List dbRows, DataReadQuery query, CacheKey parentCacheKey, boolean isTargetProtected) {
if(this.listOrderField == null) {
return super.addAll(elements, container, session, dbRows, query, parentCacheKey, isTargetProtected);
} else {
return addAll(elements, container, session, dbRows, query, parentCacheKey);
}
}
protected boolean addAll(List elements, Object container, AbstractSession session, List dbRows, ReadQuery query, CacheKey parentCacheKey) {
int size = dbRows.size();
if(this.elementDescriptor != null && this.elementDescriptor.getObjectBuilder().hasWrapperPolicy()) {
ObjectBuilder objectBuilder = this.elementDescriptor.getObjectBuilder();
List wrappedElements = new ArrayList(size);
for(int i=0; i < size; i++) {
wrappedElements.add(objectBuilder.wrapObject(elements.get(i), session));
}
elements = wrappedElements;
}
ConversionManager conversionManager = session.getDatasourcePlatform().getConversionManager();
// populate container with a dummy so the container.set(i, obj) could be used later.
for(int i=0; i < size; i++) {
((List)container).add(NOT_SET);
}
// insert the elements into container
boolean failed = false;
for(int i=0; i < size; i++) {
AbstractRecord row = dbRows.get(i);
Object orderValue = row.get(this.listOrderField);
// order value is null
if(orderValue == null) {
failed = true;
break;
}
int intOrderValue = conversionManager.convertObject(orderValue, Integer.class);
try {
// one or more elements have the same order value
if(NOT_SET != ((List)container).set(intOrderValue, elements.get(i))) {
failed = true;
break;
}
} catch(IndexOutOfBoundsException indexException) {
// order value negative or greater/equal to size
failed = true;
break;
}
}
if(failed) {
((List)container).clear();
// extract order list - it will be set into exception or used by order correction.
List orderList = new ArrayList(size);
for(int i=0; i < size; i++) {
AbstractRecord row = dbRows.get(i);
Object orderValue = row.get(this.listOrderField);
if(orderValue == null) {
orderList.add(null);
} else {
orderList.add(conversionManager.convertObject(orderValue, Integer.class));
}
}
if(this.orderCorrectionType == OrderCorrectionType.READ || this.orderCorrectionType == OrderCorrectionType.READ_WRITE) {
// pair each element with its order index
List indexedElements = new ArrayList(size);
for(int i=0; i < size; i++) {
indexedElements.add(new IndexedObject(orderList.get(i), elements.get(i)));
}
// put elements in order and add to container
((List)container).addAll(correctOrderList(indexedElements));
if(this.orderCorrectionType == OrderCorrectionType.READ_WRITE) {
// mark IndirectList to have broken order
((IndirectList)container).setIsListOrderBrokenInDb(true);
}
} else {
// this.orderCorrectionType == OrderCorrectionType.EXCEPTION
throw QueryException.listOrderFieldWrongValue(query, this.listOrderField, orderList);
}
}
return size > 0;
}
/**
* INTERNAL:
* Add element into a container which implements the List interface.
* Add at a particular index.
*/
protected void addIntoAtIndex(Integer index, Object object, Object container, AbstractSession session) {
if (hasElementDescriptor()) {
object = getElementDescriptor().getObjectBuilder().wrapObject(object, session);
}
try {
if (index == null || (index > 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, object);
}
} catch (ClassCastException | UnsupportedOperationException | IllegalArgumentException ex1) {
throw QueryException.cannotAddElement(object, container, ex1);
}
}
/**
* 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.
*/
@Override
public void compareCollectionsForChange(Object oldList, Object newList, CollectionChangeRecord changeRecord, AbstractSession session, ClassDescriptor referenceDescriptor) {
List orderedObjectsToAdd = new ArrayList();
Map indicesToRemove = new HashMap();
Map oldListIndexValue = new HashMap();
IdentityHashMap oldListValueIndex = new IdentityHashMap();
IdentityHashMap objectsToAdd = new IdentityHashMap();
Map