| /* |
| * Copyright (c) 1998, 2021 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 |
| package org.eclipse.persistence.indirection; |
| |
| import java.beans.PropertyChangeListener; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Comparator; |
| import java.util.Enumeration; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.ListIterator; |
| import java.util.Spliterator; |
| import java.util.Vector; |
| import java.util.function.Consumer; |
| import java.util.function.Predicate; |
| import java.util.function.UnaryOperator; |
| import java.util.stream.Stream; |
| |
| import org.eclipse.persistence.descriptors.changetracking.CollectionChangeEvent; |
| import org.eclipse.persistence.descriptors.changetracking.CollectionChangeTracker; |
| import org.eclipse.persistence.internal.descriptors.changetracking.AttributeChangeListener; |
| import org.eclipse.persistence.internal.indirection.UnitOfWorkQueryValueHolder; |
| import org.eclipse.persistence.internal.indirection.UnitOfWorkValueHolder; |
| import org.eclipse.persistence.mappings.CollectionMapping; |
| import org.eclipse.persistence.mappings.DatabaseMapping; |
| |
| /** |
| * IndirectList allows a domain class to take advantage of TopLink indirection |
| * without having to declare its instance variable as a ValueHolderInterface. |
| * <p>To use an IndirectList:<ul> |
| * <li> Declare the appropriate instance variable with type Collection/List/Vector (jdk1.2). |
| * <li> Send the message #useTransparentCollection() to the appropriate |
| * CollectionMapping. |
| * </ul> |
| * EclipseLink will place an |
| * IndirectList in the instance variable when the containing domain object is read from |
| * the database. With the first message sent to the IndirectList, the contents |
| * are fetched from the database and normal Collection/List/Vector behavior is resumed. |
| * |
| * @param <E> the type of elements maintained by this list |
| * @see org.eclipse.persistence.mappings.CollectionMapping |
| * @see org.eclipse.persistence.indirection.IndirectMap |
| * @author Big Country |
| * @since TOPLink/Java 2.5 |
| */ |
| public class IndirectList<E> extends Vector<E> implements CollectionChangeTracker, IndirectCollection<E, List<E>> { |
| |
| /** Reduce type casting. */ |
| protected volatile Vector<E> delegate; |
| |
| /** Delegate indirection behavior to a value holder. */ |
| protected volatile ValueHolderInterface<List<E>> valueHolder; |
| |
| /** Change tracking listener. */ |
| private transient PropertyChangeListener changeListener = null; |
| |
| /** The mapping attribute name, used to raise change events. */ |
| private transient String attributeName = null; |
| |
| /** Store added elements to avoid instantiation on add. */ |
| private transient List<E> addedElements = null; |
| |
| /** Store removed elements to avoid instantiation on remove. */ |
| private transient List<E> removedElements = null; |
| |
| /** Store initial size for lazy init. */ |
| protected int initialCapacity; |
| |
| /** PERF: Quick check flag if has been registered in a unit of work. */ |
| protected boolean isRegistered; |
| |
| /** |
| * If the mapping using IndirectList has listOrderfield != null then this flag indicates |
| * whether the list in the db has invalid order: |
| * either row(s) with null order value(s) or/and "holes" in order. |
| * The flag may be set to true when the objects are read from the db. |
| * When collection is updated the flag set to true causes updating of listOrderField of all rows in the db. |
| * After update is complete the flag is set back to false. |
| **/ |
| private boolean isListOrderBrokenInDb; |
| |
| /** |
| * This value is used to determine if we should attempt to do adds and removes from the list without |
| * actually instantiating the list from the database. By default this is set to true. |
| */ |
| private boolean useLazyInstantiation = true; |
| |
| /** |
| * PUBLIC: |
| * Construct an empty IndirectList so that its internal data array |
| * has size <code>10</code> and its standard capacity increment is zero. |
| */ |
| public IndirectList() { |
| super(0, 0); |
| this.initialCapacity = 10; |
| } |
| |
| /** |
| * PUBLIC: |
| * Construct an empty IndirectList with the specified initial capacity and |
| * with its capacity increment equal to zero. |
| * |
| * @param initialCapacity the initial capacity of the vector |
| * @exception IllegalArgumentException if the specified initial capacity |
| * is negative |
| */ |
| public IndirectList(int initialCapacity) { |
| super(0, 0); |
| this.initialCapacity = initialCapacity; |
| } |
| |
| /** |
| * PUBLIC: |
| * Construct an empty IndirectList with the specified initial capacity and |
| * capacity increment. |
| * |
| * @param initialCapacity the initial capacity of the vector |
| * @param capacityIncrement the amount by which the capacity is |
| * increased when the vector overflows |
| * @exception IllegalArgumentException if the specified initial capacity |
| * is negative |
| */ |
| public IndirectList(int initialCapacity, int capacityIncrement) { |
| super(0, capacityIncrement); |
| this.initialCapacity = initialCapacity; |
| } |
| |
| /** |
| * PUBLIC: |
| * Construct an IndirectList containing the elements of the specified |
| * collection, in the order they are returned by the collection's |
| * iterator. |
| * @param collection a collection containing the elements to construct this IndirectList with. |
| */ |
| public IndirectList(Collection<? extends E> collection) { |
| super(0); |
| this.valueHolder = new ValueHolder<>(new Vector<>(collection)); |
| } |
| |
| /** |
| * @see java.util.Vector#add(int, java.lang.Object) |
| */ |
| @Override |
| public void add(int index, E element) { |
| getDelegate().add(index, element); |
| raiseAddChangeEvent(element, index); |
| } |
| |
| /** |
| * Raise the add change event and relationship maintenance. |
| */ |
| protected void raiseAddChangeEvent(E element, Integer index) { |
| raiseAddChangeEvent(element, index, false); |
| } |
| protected void raiseAddChangeEvent(E element, Integer index, boolean isSet) { |
| if (hasTrackedPropertyChangeListener()) { |
| _persistence_getPropertyChangeListener().propertyChange(new CollectionChangeEvent(this, getTrackedAttributeName(), this, element, CollectionChangeEvent.ADD, index, isSet, true)); |
| } |
| if (isRelationshipMaintenanceRequired()) { |
| ((UnitOfWorkQueryValueHolder)getValueHolder()).updateForeignReferenceSet(element, null); |
| } |
| } |
| |
| protected boolean isRelationshipMaintenanceRequired() { |
| if (this.valueHolder instanceof UnitOfWorkQueryValueHolder) { |
| DatabaseMapping mapping = ((UnitOfWorkQueryValueHolder)this.valueHolder).getMapping(); |
| return (mapping != null) && (mapping.getRelationshipPartner() != null); |
| } |
| return false; |
| } |
| |
| /** |
| * Raise the remove change event. |
| */ |
| protected void raiseRemoveChangeEvent(Object element, Integer index) { |
| raiseRemoveChangeEvent(element, index, false); |
| } |
| protected void raiseRemoveChangeEvent(Object element, Integer index, boolean isSet) { |
| if (hasTrackedPropertyChangeListener()) { |
| _persistence_getPropertyChangeListener().propertyChange(new CollectionChangeEvent(this, getTrackedAttributeName(), this, element, CollectionChangeEvent.REMOVE, index, isSet, true)); |
| } |
| if (isRelationshipMaintenanceRequired()) { |
| ((UnitOfWorkQueryValueHolder)getValueHolder()).updateForeignReferenceRemove(element); |
| } |
| } |
| |
| /** |
| * @see java.util.Vector#add(java.lang.Object) |
| */ |
| @Override |
| public boolean add(E element) { |
| if (!this.isRegistered) { |
| return getDelegate().add(element); |
| } |
| boolean added = true; |
| // PERF: If not instantiated just record the add to avoid the instantiation. |
| if (shouldAvoidInstantiation()) { |
| if (hasRemovedElements() && getRemovedElements().contains(element)) { |
| getRemovedElements().remove(element); |
| } else if (isRelationshipMaintenanceRequired() && getAddedElements().contains(element)) { |
| // Must avoid recursion for relationship maintenance. |
| return false; |
| } else { |
| getAddedElements().add(element); |
| } |
| } else { |
| added = getDelegate().add(element); |
| } |
| raiseAddChangeEvent(element, null); |
| return added; |
| } |
| |
| /** |
| * @see java.util.Vector#addAll(int, java.util.Collection) |
| */ |
| @Override |
| public boolean addAll(int index, Collection<? extends E> c) { |
| Iterator<? extends E> objects = c.iterator(); |
| // Must trigger add events if tracked or uow. |
| if (hasBeenRegistered() || hasTrackedPropertyChangeListener()) { |
| while (objects.hasNext()) { |
| this.add(index, objects.next()); |
| index++; |
| } |
| return true; |
| } |
| |
| return getDelegate().addAll(index, c); |
| |
| } |
| |
| /** |
| * @see java.util.Vector#addAll(java.util.Collection) |
| */ |
| @Override |
| public boolean addAll(Collection<? extends E> c) { |
| // Must trigger add events if tracked or uow. |
| if (hasBeenRegistered() || hasTrackedPropertyChangeListener()) { |
| Iterator<? extends E> objects = c.iterator(); |
| while (objects.hasNext()) { |
| this.add(objects.next()); |
| } |
| return true; |
| } |
| |
| return getDelegate().addAll(c); |
| } |
| |
| /** |
| * @see java.util.Vector#addElement(java.lang.Object) |
| */ |
| @Override |
| public void addElement(E obj) { |
| add(obj); |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the freshly-built delegate. |
| */ |
| protected Vector<E> buildDelegate() { |
| Vector<E> newDelegate = (Vector<E>) getValueHolder().getValue(); |
| if (newDelegate == null) { |
| newDelegate = new Vector<>(this.initialCapacity, this.capacityIncrement); |
| } |
| // This can either be another indirect list or a Vector. |
| // It can be another indirect list because the mapping's query uses the same container policy. |
| // Unwrap any redundant indirection layers, which can cause issues and impact performance. |
| while (newDelegate instanceof IndirectList) { |
| if(((IndirectList) newDelegate).isListOrderBrokenInDb()) { |
| this.isListOrderBrokenInDb = true; |
| } |
| newDelegate = ((IndirectList<E>) newDelegate).getDelegate(); |
| } |
| // First add/remove any cached changes. |
| if (hasAddedElements()) { |
| for (E element: getAddedElements()) { |
| // On a flush or resume the element may already be in the database. |
| if (!newDelegate.contains(element)) { |
| newDelegate.add(element); |
| } |
| } |
| this.addedElements = null; |
| } |
| if (hasRemovedElements()) { |
| for (E element: getRemovedElements()) { |
| newDelegate.remove(element); |
| } |
| this.removedElements = null; |
| } |
| return newDelegate; |
| } |
| |
| /** |
| * @see java.util.Vector#capacity() |
| */ |
| @Override |
| public int capacity() { |
| return getDelegate().capacity(); |
| } |
| |
| /** |
| * @see java.util.Vector#clear() |
| */ |
| @Override |
| public void clear() { |
| removeAllElements(); |
| } |
| |
| /** |
| * INTERNAL: |
| * clear any changes that have been deferred to instantiation. |
| * Indirect collections with change tracking avoid instantiation on add/remove. |
| */ |
| @Override |
| public void clearDeferredChanges(){ |
| addedElements = null; |
| removedElements = null; |
| } |
| |
| /** |
| * PUBLIC: |
| * @see java.util.Vector#clone() |
| * This will result in a database query if necessary. |
| */ |
| |
| /* |
| There are 3 situations when clone() is called: |
| 1. The developer actually wants to clone the collection (typically to modify one |
| of the 2 resulting collections). In which case the contents must be read from |
| the database. |
| 2. A UnitOfWork needs a clone (or backup clone) of the collection. But the |
| UnitOfWork checks "instantiation" before cloning collections ("un-instantiated" |
| collections are not cloned). |
| 3. A MergeManager needs an extra copy of the collection (because the "backup" |
| and "target" are the same object?). But the MergeManager checks "instantiation" |
| before merging collections (again, "un-instantiated" collections are not merged). |
| */ |
| @Override |
| public synchronized Object clone() { |
| IndirectList<E> result = (IndirectList<E>)super.clone(); |
| result.delegate = (Vector<E>)this.getDelegate().clone(); |
| result.valueHolder = new ValueHolder<>(result.delegate); |
| result.attributeName = null; |
| result.changeListener = null; |
| return result; |
| } |
| |
| /** |
| * PUBLIC: |
| * @see java.util.Vector#contains(java.lang.Object) |
| */ |
| @Override |
| public boolean contains(Object element) { |
| // PERF: Avoid instantiation if not required. |
| if (hasAddedElements()) { |
| if (getAddedElements().contains(element)) { |
| return true; |
| } |
| } |
| if (hasRemovedElements()) { |
| if (getRemovedElements().contains(element)) { |
| return false; |
| } |
| } |
| return getDelegate().contains(element); |
| } |
| |
| /** |
| * @see java.util.Vector#containsAll(java.util.Collection) |
| */ |
| @Override |
| public boolean containsAll(Collection<?> c) { |
| return getDelegate().containsAll(c); |
| } |
| |
| /** |
| * @see java.util.Vector#copyInto(java.lang.Object[]) |
| */ |
| @Override |
| public synchronized void copyInto(Object[] anArray) { |
| getDelegate().copyInto(anArray); |
| } |
| |
| /** |
| * @see java.util.Vector#elementAt(int) |
| */ |
| @Override |
| public E elementAt(int index) { |
| return getDelegate().elementAt(index); |
| } |
| |
| /** |
| * @see java.util.Vector#elements() |
| */ |
| @Override |
| public Enumeration<E> elements() { |
| return getDelegate().elements(); |
| } |
| |
| /** |
| * @see java.util.Vector#ensureCapacity(int) |
| */ |
| @Override |
| public void ensureCapacity(int minCapacity) { |
| getDelegate().ensureCapacity(minCapacity); |
| } |
| |
| /** |
| * @see java.util.Vector#equals(java.lang.Object) |
| */ |
| @Override |
| public boolean equals(Object o) { |
| return getDelegate().equals(o); |
| } |
| |
| /** |
| * @see java.util.Vector#firstElement() |
| */ |
| @Override |
| public E firstElement() { |
| return getDelegate().firstElement(); |
| } |
| |
| /** |
| * @see java.util.Vector#get(int) |
| */ |
| @Override |
| public E get(int index) { |
| return getDelegate().get(index); |
| } |
| |
| /** |
| * INTERNAL: |
| * Check whether the contents have been read from the database. |
| * If they have not, read them and set the delegate. |
| * This method used to be synchronized, which caused deadlock. |
| */ |
| protected Vector<E> getDelegate() { |
| Vector<E> v = this.delegate; |
| if (v == null) { |
| synchronized(this){ |
| v = this.delegate; |
| if (v == null) { |
| this.delegate = v = this.buildDelegate(); |
| } |
| } |
| } |
| return v; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the real collection object. |
| * This will force instantiation. |
| */ |
| @Override |
| public List<E> getDelegateObject() { |
| return getDelegate(); |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the valueHolder. |
| * This method used to be synchronized, which caused deadlock. |
| */ |
| @Override |
| public ValueHolderInterface<List<E>> getValueHolder() { |
| ValueHolderInterface<List<E>> vh = this.valueHolder; |
| // PERF: lazy initialize value holder and vector as are normally set after creation. |
| if (vh == null) { |
| synchronized(this) { |
| vh = this.valueHolder; |
| if (vh == null) { |
| this.valueHolder = vh = new ValueHolder<>(new Vector<>(this.initialCapacity, this.capacityIncrement)); |
| } |
| } |
| } |
| return vh; |
| } |
| |
| /** |
| * INTERNAL: |
| * return whether this IndirectList has been registered with the UnitOfWork |
| */ |
| public boolean hasBeenRegistered() { |
| return getValueHolder() instanceof UnitOfWorkQueryValueHolder; |
| } |
| |
| /** |
| * INTERNAL: |
| * @see java.util.Vector#hashCode() |
| */ |
| @Override |
| public int hashCode() { |
| return this.getDelegate().hashCode(); |
| } |
| |
| /** |
| * @see java.util.Vector#indexOf(java.lang.Object) |
| */ |
| @Override |
| public int indexOf(Object elem) { |
| return this.getDelegate().indexOf(elem); |
| } |
| |
| /** |
| * @see java.util.Vector#indexOf(java.lang.Object, int) |
| */ |
| @Override |
| public int indexOf(Object elem, int index) { |
| return this.getDelegate().indexOf(elem, index); |
| } |
| |
| /** |
| * @see java.util.Vector#insertElementAt(java.lang.Object, int) |
| */ |
| @Override |
| public void insertElementAt(E obj, int index) { |
| this.getDelegate().insertElementAt(obj, index); |
| this.raiseAddChangeEvent(obj, index); |
| } |
| |
| /** |
| * @see java.util.Vector#isEmpty() |
| */ |
| @Override |
| public boolean isEmpty() { |
| return this.getDelegate().isEmpty(); |
| } |
| |
| /** |
| * PUBLIC: |
| * Return whether the contents have been read from the database. |
| */ |
| @Override |
| public boolean isInstantiated() { |
| return getValueHolder().isInstantiated(); |
| } |
| |
| /** |
| * @see java.util.AbstractList#iterator() |
| */ |
| @Override |
| public Iterator<E> iterator() { |
| // Must wrap the interator to raise the remove event. |
| return listIterator(0); |
| } |
| |
| /** |
| * @see java.util.Vector#lastElement() |
| */ |
| @Override |
| public E lastElement() { |
| return getDelegate().lastElement(); |
| } |
| |
| /** |
| * @see java.util.Vector#lastIndexOf(java.lang.Object) |
| */ |
| @Override |
| public int lastIndexOf(Object elem) { |
| return getDelegate().lastIndexOf(elem); |
| } |
| |
| /** |
| * @see java.util.Vector#lastIndexOf(java.lang.Object, int) |
| */ |
| @Override |
| public int lastIndexOf(Object elem, int index) { |
| return getDelegate().lastIndexOf(elem, index); |
| } |
| |
| /** |
| * @see java.util.AbstractList#listIterator() |
| */ |
| @Override |
| public ListIterator<E> listIterator() { |
| return listIterator(0); |
| } |
| |
| /** |
| * @see java.util.AbstractList#listIterator(int) |
| */ |
| @Override |
| public ListIterator<E> listIterator(final int index) { |
| // Must wrap the interator to raise the remove event. |
| return new ListIterator<E>() { |
| ListIterator<E> delegateIterator = IndirectList.this.getDelegate().listIterator(index); |
| E currentObject; |
| |
| @Override |
| public boolean hasNext() { |
| return this.delegateIterator.hasNext(); |
| } |
| |
| @Override |
| public boolean hasPrevious() { |
| return this.delegateIterator.hasPrevious(); |
| } |
| |
| @Override |
| public int previousIndex() { |
| return this.delegateIterator.previousIndex(); |
| } |
| |
| @Override |
| public int nextIndex() { |
| return this.delegateIterator.nextIndex(); |
| } |
| |
| @Override |
| public E next() { |
| this.currentObject = this.delegateIterator.next(); |
| return this.currentObject; |
| } |
| |
| @Override |
| public E previous() { |
| this.currentObject = this.delegateIterator.previous(); |
| return this.currentObject; |
| } |
| |
| @Override |
| public void remove() { |
| this.delegateIterator.remove(); |
| IndirectList.this.raiseRemoveChangeEvent(this.currentObject, this.delegateIterator.nextIndex()); |
| } |
| |
| @Override |
| public void set(E object) { |
| this.delegateIterator.set(object); |
| Integer index = this.delegateIterator.previousIndex(); |
| IndirectList.this.raiseRemoveChangeEvent(this.currentObject, index, true); |
| IndirectList.this.raiseAddChangeEvent(object, index, true); |
| } |
| |
| @Override |
| public void add(E object) { |
| this.delegateIterator.add(object); |
| IndirectList.this.raiseAddChangeEvent(object, this.delegateIterator.previousIndex()); |
| } |
| |
| @Override |
| public void forEachRemaining(Consumer<? super E> action) { |
| this.delegateIterator.forEachRemaining(action); |
| } |
| }; |
| } |
| |
| /** |
| * @see java.util.Vector#remove(int) |
| */ |
| @Override |
| public E remove(int index) { |
| E value = getDelegate().remove(index); |
| this.raiseRemoveChangeEvent(value, index); |
| return value; |
| } |
| |
| /** |
| * @see java.util.Vector#remove(java.lang.Object) |
| */ |
| @Override |
| public boolean remove(Object element) { |
| if (!this.isRegistered) { |
| return getDelegate().remove(element); |
| } |
| // PERF: If not instantiated just record the removal to avoid the instantiation. |
| if (shouldAvoidInstantiation()) { |
| if (hasAddedElements() && getAddedElements().contains(element)) { |
| getAddedElements().remove(element); |
| } else if (getRemovedElements().contains(element)) { |
| // Must avoid recursion for relationship maintenance. |
| return false; |
| } else { |
| getRemovedElements().add((E) element); |
| } |
| this.raiseRemoveChangeEvent(element, null); |
| return true; |
| } else { |
| int index = this.getDelegate().indexOf(element); |
| if(index > -1) { |
| this.getDelegate().remove(index); |
| this.raiseRemoveChangeEvent(element, index); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * @see java.util.Vector#removeAll(java.util.Collection) |
| */ |
| @Override |
| public boolean removeAll(Collection<?> c) { |
| // Must trigger remove events if tracked or uow. |
| if (hasBeenRegistered() || hasTrackedPropertyChangeListener()) { |
| boolean hasChanged = false; |
| Iterator<?> objects = c.iterator(); |
| while (objects.hasNext()) { |
| hasChanged |= remove(objects.next()); |
| } |
| return hasChanged; |
| } |
| return getDelegate().removeAll(c); |
| } |
| |
| /** |
| * @see java.util.Vector#removeAllElements() |
| */ |
| @Override |
| public void removeAllElements() { |
| // Must trigger remove events if tracked or uow. |
| if (hasBeenRegistered() || hasTrackedPropertyChangeListener()) { |
| Iterator<? extends E> objects = iterator(); |
| while (objects.hasNext()) { |
| objects.next(); |
| objects.remove(); |
| } |
| return; |
| } |
| getDelegate().removeAllElements(); |
| } |
| |
| /** |
| * @see java.util.Vector#removeElement(java.lang.Object) |
| */ |
| @Override |
| public boolean removeElement(Object obj) { |
| return remove(obj); |
| } |
| |
| /** |
| * @see java.util.Vector#removeElementAt(int) |
| */ |
| @Override |
| public void removeElementAt(int index) { |
| remove(index); |
| } |
| |
| /** |
| * @see java.util.Vector#retainAll(java.util.Collection) |
| */ |
| @Override |
| public boolean retainAll(Collection<?> c) { |
| // Must trigger remove events if tracked or uow. |
| if (hasBeenRegistered() || hasTrackedPropertyChangeListener()) { |
| Iterator<E> objects = getDelegate().iterator(); |
| while (objects.hasNext()) { |
| E object = objects.next(); |
| if (!c.contains(object)) { |
| objects.remove(); |
| raiseRemoveChangeEvent(object, null); |
| } |
| } |
| return true; |
| } |
| return getDelegate().retainAll(c); |
| } |
| |
| /** |
| * @see java.util.Vector#set(int, java.lang.Object) |
| */ |
| @Override |
| public E set(int index, E element) { |
| E oldValue = getDelegate().set(index, element); |
| Integer bigIntIndex = index; |
| raiseRemoveChangeEvent(oldValue, bigIntIndex, true); |
| raiseAddChangeEvent(element, bigIntIndex, true); |
| return oldValue; |
| } |
| |
| /** |
| * @see java.util.Vector#setElementAt(java.lang.Object, int) |
| */ |
| @Override |
| public void setElementAt(E obj, int index) { |
| set(index, obj); |
| } |
| |
| /** |
| * @see java.util.Vector#setSize(int) |
| */ |
| @Override |
| public void setSize(int newSize) { |
| // Must trigger remove events if tracked or uow. |
| if (hasBeenRegistered() || hasTrackedPropertyChangeListener()) { |
| if (newSize > size()) { |
| for (int index = size(); index > newSize; index--) { |
| this.remove(index - 1); |
| } |
| } |
| } |
| getDelegate().setSize(newSize); |
| } |
| |
| /** |
| * INTERNAL |
| * Set whether this collection should attempt do deal with adds and removes without retrieving the |
| * collection from the dB |
| */ |
| @Override |
| public void setUseLazyInstantiation(boolean useLazyInstantiation){ |
| this.useLazyInstantiation = useLazyInstantiation; |
| } |
| |
| /** |
| * INTERNAL: |
| * Set the value holder. |
| */ |
| @Override |
| public void setValueHolder(ValueHolderInterface<List<E>> valueHolder) { |
| this.delegate = null; |
| this.valueHolder = valueHolder; |
| if (valueHolder instanceof UnitOfWorkQueryValueHolder) { |
| this.isRegistered = true; |
| } |
| } |
| |
| /** |
| * @see java.util.Vector#size() |
| */ |
| @Override |
| public int size() { |
| return getDelegate().size(); |
| } |
| |
| /** |
| * Return whether this collection should attempt do deal with adds and removes without retrieving the |
| * collection from the dB |
| */ |
| protected boolean shouldUseLazyInstantiation(){ |
| return useLazyInstantiation; |
| } |
| |
| /** |
| * @see java.util.Vector#subList(int, int) |
| */ |
| @Override |
| public List<E> subList(int fromIndex, int toIndex) { |
| return getDelegate().subList(fromIndex, toIndex); |
| } |
| |
| /** |
| * @see java.util.Vector#toArray() |
| */ |
| @Override |
| public Object[] toArray() { |
| return getDelegate().toArray(); |
| } |
| |
| /** |
| * @see java.util.Vector#toArray(java.lang.Object[]) |
| */ |
| @Override |
| public <T> T[] toArray(T[] a) { |
| return getDelegate().toArray(a); |
| } |
| |
| @Override |
| public Spliterator<E> spliterator() { |
| return getDelegate().spliterator(); |
| } |
| |
| @Override |
| public synchronized void replaceAll(UnaryOperator<E> operator) { |
| // Must trigger remove/add events if tracked or uow. |
| if (hasBeenRegistered() || hasTrackedPropertyChangeListener()) { |
| List<E> del = getDelegate(); |
| for (int i = 0; i < del.size(); i++) { |
| set(i, operator.apply(del.get(i))); |
| } |
| } else { |
| getDelegate().replaceAll(operator); |
| } |
| } |
| |
| @Override |
| public synchronized boolean removeIf(Predicate<? super E> filter) { |
| // Must trigger remove events if tracked or uow. |
| if (hasBeenRegistered() || hasTrackedPropertyChangeListener()) { |
| boolean hasChanged = false; |
| Iterator<E> objects = iterator(); |
| while (objects.hasNext()) { |
| if (filter.test(objects.next())) { |
| objects.remove(); |
| hasChanged |= true; |
| } |
| } |
| return hasChanged; |
| } |
| return getDelegate().removeIf(filter); |
| } |
| |
| @Override |
| public void forEach(Consumer<? super E> action) { |
| getDelegate().forEach(action); |
| } |
| |
| @Override |
| public Stream<E> parallelStream() { |
| return getDelegate().parallelStream(); |
| } |
| |
| @Override |
| public Stream<E> stream() { |
| return getDelegate().stream(); |
| } |
| |
| @Override |
| public void sort(Comparator<? super E> c) { |
| getDelegate().sort(c); |
| } |
| |
| /** |
| * PUBLIC: |
| * Use the java.util.Vector#toString(); but wrap it with braces to indicate |
| * there is a bit of indirection. |
| * Don't allow this method to trigger a database read. |
| * @see java.util.Vector#toString() |
| */ |
| @Override |
| public String toString() { |
| if (ValueHolderInterface.shouldToStringInstantiate) { |
| return getDelegate().toString(); |
| } |
| if (this.isInstantiated()) { |
| return "{" + getDelegate().toString() + "}"; |
| } else { |
| return "{" + org.eclipse.persistence.internal.helper.Helper.getShortClassName(this.getClass()) + ": not instantiated}"; |
| } |
| } |
| |
| /** |
| * @see java.util.Vector#trimToSize() |
| */ |
| @Override |
| public void trimToSize() { |
| getDelegate().trimToSize(); |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the property change listener for change tracking. |
| */ |
| @Override |
| public PropertyChangeListener _persistence_getPropertyChangeListener() { |
| return changeListener; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return if the collection has a property change listener for change tracking. |
| */ |
| public boolean hasTrackedPropertyChangeListener() { |
| return this.changeListener != null; |
| } |
| |
| /** |
| * INTERNAL: |
| * Set the property change listener for change tracking. |
| */ |
| @Override |
| public void _persistence_setPropertyChangeListener(PropertyChangeListener changeListener) { |
| this.changeListener = changeListener; |
| if (changeListener != null) { |
| this.isRegistered = true; |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the mapping attribute name, used to raise change events. |
| */ |
| @Override |
| public String getTrackedAttributeName() { |
| return attributeName; |
| } |
| |
| /** |
| * INTERNAL: |
| * Set the mapping attribute name, used to raise change events. |
| * This is required if the change listener is set. |
| */ |
| @Override |
| public void setTrackedAttributeName(String attributeName) { |
| this.attributeName = attributeName; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the elements that have been removed before instantiation. |
| */ |
| @Override |
| public List<E> getRemovedElements() { |
| if (removedElements == null) { |
| removedElements = new ArrayList<>(); |
| } |
| return removedElements; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the elements that have been added before instantiation. |
| */ |
| @Override |
| public List<E> getAddedElements() { |
| if (addedElements == null) { |
| addedElements = new ArrayList<>(); |
| } |
| return addedElements; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return if any elements that have been added before instantiation. |
| */ |
| public boolean hasAddedElements() { |
| return (addedElements != null) && (!addedElements.isEmpty()); |
| } |
| |
| /** |
| * INTERNAL: |
| * Return if any elements that have been removed before instantiation. |
| */ |
| public boolean hasRemovedElements() { |
| return (removedElements != null) && (!removedElements.isEmpty()); |
| } |
| |
| /** |
| * INTERNAL: |
| * Return if any elements that have been added or removed before instantiation. |
| */ |
| @Override |
| public boolean hasDeferredChanges() { |
| return hasRemovedElements() || hasAddedElements(); |
| } |
| |
| /** |
| * INTERNAL: |
| * Return if add/remove should trigger instantiation or avoid. |
| * Current instantiation is avoided is using change tracking. |
| */ |
| protected boolean shouldAvoidInstantiation() { |
| return (!isInstantiated()) && (shouldUseLazyInstantiation()) && (_persistence_getPropertyChangeListener() instanceof AttributeChangeListener) && !usesListOrderField() && ((WeavedAttributeValueHolderInterface)getValueHolder()).shouldAllowInstantiationDeferral(); |
| } |
| |
| /** |
| * INTERNAL: |
| * Returns whether the mapping has listOrderField. |
| */ |
| protected boolean usesListOrderField() { |
| if(this.valueHolder instanceof UnitOfWorkValueHolder) { |
| return ((CollectionMapping)((UnitOfWorkValueHolder)this.valueHolder).getMapping()).getListOrderField() != null; |
| } else { |
| return false; |
| } |
| } |
| |
| public boolean isListOrderBrokenInDb() { |
| return this.isListOrderBrokenInDb; |
| } |
| public void setIsListOrderBrokenInDb(boolean isBroken) { |
| this.isListOrderBrokenInDb = isBroken; |
| } |
| } |