| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.openjpa.util; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Iterator; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.ListIterator; |
| import java.util.Vector; |
| |
| import org.apache.openjpa.kernel.Broker; |
| import org.apache.openjpa.kernel.BrokerImpl; |
| import org.apache.openjpa.kernel.DetachedValueStateManager; |
| import org.apache.openjpa.kernel.OpenJPAStateManager; |
| import org.apache.openjpa.kernel.StateManagerImpl; |
| |
| /** |
| * Utility methods used by collection proxies. |
| * |
| * @author Abe White |
| */ |
| public class ProxyCollections |
| extends Proxies { |
| |
| /** |
| * Call before invoking {@link List#add(int,Object)} on super. |
| */ |
| public static void beforeAdd(ProxyCollection coll, int index, Object value){ |
| assertAllowedType(value, coll.getElementType()); |
| dirty(coll, true); |
| } |
| |
| /** |
| * Call before invoking {@link Vector#insertElementAt(Object,int)} on super. |
| */ |
| public static void beforeInsertElementAt(ProxyCollection coll, Object value, |
| int index) { |
| beforeAdd(coll, index, value); |
| } |
| |
| /** |
| * Call before invoking {@link Collection#add(Object)} on super. |
| */ |
| public static void beforeAdd(ProxyCollection coll, Object value) { |
| assertAllowedType(value, coll.getElementType()); |
| // Must only dirty the collection outside of a delayed load |
| if (!isDirectAccess(coll)) { |
| dirty(coll, false); |
| } |
| } |
| |
| /** |
| * Call after invoking {@link Collection#add(Object)} on super. |
| * |
| * @param added whether the object was added |
| * @return <code>added</code>, for convenience |
| */ |
| public static boolean afterAdd(ProxyCollection coll, Object value, |
| boolean added) { |
| if (!isDirectAccess(coll) && added && coll.getChangeTracker() != null) { |
| setDirectAccess(coll,true); |
| ((CollectionChangeTracker) coll.getChangeTracker()).added(value); |
| setDirectAccess(coll,false); |
| } |
| return added; |
| } |
| |
| /** |
| * Call before invoking {@link Vector#addElement(Object)} on super. |
| */ |
| public static void beforeAddElement(ProxyCollection coll, Object value) { |
| beforeAdd(coll, value); |
| } |
| |
| /** |
| * Call after invoking {@link Vector#addElement(Object)} on super. |
| */ |
| public static void afterAddElement(ProxyCollection coll, Object value) { |
| afterAdd(coll, value, true); |
| } |
| |
| /** |
| * Call before invoking {@link LinkedList#addFirst(Object)} on super. |
| */ |
| public static void beforeAddFirst(ProxyCollection coll, Object value) { |
| beforeAdd(coll, 0, value); |
| } |
| |
| /** |
| * Call before invoking {@link LinkedList#addLast(Object)} on super. |
| */ |
| public static void beforeAddLast(ProxyCollection coll, Object value) { |
| beforeAdd(coll, value); |
| } |
| |
| /** |
| * Call after invoking {@link LinkedList#addLast(Object)} on super. |
| */ |
| public static void afterAddLast(ProxyCollection coll, Object value) { |
| afterAdd(coll, value, true); |
| } |
| |
| /** |
| * Call before invoking {@link Queue#offer(Object)} on super. |
| */ |
| public static void beforeOffer(ProxyCollection coll, Object value) { |
| beforeAdd(coll, value); |
| } |
| |
| /** |
| * Call after invoking {@link Queue#offer(Object)} on super. |
| * |
| * @param added whether the object was added |
| * @return <code>added</code>, for convenience |
| */ |
| public static boolean afterOffer(ProxyCollection coll, Object value, |
| boolean added) { |
| return afterAdd(coll, value, added); |
| } |
| |
| /** |
| * Override for {@link List#addAll(int, Collection)}. |
| */ |
| public static boolean addAll(ProxyCollection coll, int index, |
| Collection values) { |
| List list = (List) coll; |
| for (Iterator itr = values.iterator(); itr.hasNext(); index++) |
| list.add(index, itr.next()); |
| return values.size() > 0; |
| } |
| |
| /** |
| * Override for {@link Collection#addAll}. |
| */ |
| public static boolean addAll(ProxyCollection coll, Collection values) { |
| boolean added = false; |
| for (Object value : values) { |
| added |= coll.add(value); |
| } |
| return added; |
| } |
| |
| /** |
| * Call before clearing collection. |
| */ |
| public static void beforeClear(ProxyCollection coll) { |
| dirty(coll, true); |
| for (Object o : coll) { |
| removed(coll, o, false); |
| } |
| } |
| |
| /** |
| * Call before clearing vector. |
| */ |
| public static void beforeRemoveAllElements(ProxyCollection coll) { |
| beforeClear(coll); |
| } |
| |
| /** |
| * Wrap given iterator in a proxy. |
| */ |
| public static Iterator afterIterator(final ProxyCollection coll, |
| final Iterator itr) { |
| // check for proxied; some coll impls delegate iterator methods |
| if (itr instanceof ProxyIterator) |
| return itr; |
| return new ProxyIterator() { |
| private Object _last = null; |
| |
| @Override |
| public boolean hasNext() { |
| return itr.hasNext(); |
| } |
| |
| @Override |
| public Object next() { |
| _last = itr.next(); |
| return _last; |
| } |
| |
| @Override |
| public void remove() { |
| dirty(coll, false); |
| itr.remove(); |
| if (coll.getChangeTracker() != null) |
| ((CollectionChangeTracker) coll.getChangeTracker()). |
| removed(_last); |
| Proxies.removed(coll, _last, false); |
| } |
| }; |
| } |
| |
| /** |
| * Wrap given iterator in a proxy. |
| */ |
| public static ListIterator afterListIterator(final ProxyCollection coll, |
| int idx, final ListIterator itr) { |
| return afterListIterator(coll, itr); |
| } |
| |
| /** |
| * Wrap given iterator in a proxy. |
| */ |
| public static ListIterator afterListIterator(final ProxyCollection coll, |
| final ListIterator itr) { |
| // check for proxied; some coll impls delegate iterator methods |
| if (itr instanceof ProxyListIterator) |
| return itr; |
| return new ProxyListIterator() { |
| private Object _last = null; |
| |
| @Override |
| public boolean hasNext() { |
| return itr.hasNext(); |
| } |
| |
| @Override |
| public int nextIndex() { |
| return itr.nextIndex(); |
| } |
| |
| @Override |
| public Object next() { |
| _last = itr.next(); |
| return _last; |
| } |
| |
| @Override |
| public boolean hasPrevious() { |
| return itr.hasPrevious(); |
| } |
| |
| @Override |
| public int previousIndex() { |
| return itr.previousIndex(); |
| } |
| |
| @Override |
| public Object previous() { |
| _last = itr.previous(); |
| return _last; |
| } |
| |
| @Override |
| public void set(Object o) { |
| assertAllowedType(o, coll.getElementType()); |
| dirty(coll, false); |
| itr.set(o); |
| if (coll.getChangeTracker() != null) |
| coll.getChangeTracker().stopTracking(); |
| Proxies.removed(coll, _last, false); |
| _last = o; |
| } |
| |
| @Override |
| public void add(Object o) { |
| assertAllowedType(o, coll.getElementType()); |
| dirty(coll, false); |
| itr.add(o); |
| if (coll.getChangeTracker() != null) { |
| if (hasNext()) |
| coll.getChangeTracker().stopTracking(); |
| else |
| ((CollectionChangeTracker) coll.getChangeTracker()). |
| added(o); |
| } |
| _last = o; |
| } |
| |
| @Override |
| public void remove() { |
| dirty(coll, false); |
| itr.remove(); |
| if (coll.getChangeTracker() != null) |
| ((CollectionChangeTracker) coll.getChangeTracker()). |
| removed(_last); |
| Proxies.removed(coll, _last, false); |
| } |
| }; |
| } |
| |
| /** |
| * Call before invoking {@link List#remove(int)} on super. |
| */ |
| public static void beforeRemove(ProxyCollection coll, int index) { |
| dirty(coll, false); |
| } |
| |
| /** |
| * Call after invoking {@link List#remove(int)} on super. |
| * |
| * @param removed the removed object |
| * @return the removed object, for convenience |
| */ |
| public static Object afterRemove(ProxyCollection coll, int index, |
| Object removed) { |
| if (coll.getChangeTracker() != null) |
| ((CollectionChangeTracker) coll.getChangeTracker()). |
| removed(removed); |
| removed(coll, removed, false); |
| return removed; |
| } |
| |
| /** |
| * Call before invoking {@link Vector#removeElementAt(int)} on super. |
| */ |
| public static void beforeRemoveElementAt(ProxyCollection coll, int index) { |
| beforeRemove(coll, index); |
| } |
| |
| /** |
| * Call before invoking {@link Collection#remove} on super. |
| */ |
| public static void beforeRemove(ProxyCollection coll, Object o) { |
| // Must only dirty the collection outside of a delayed load |
| if (!isDirectAccess(coll)) { |
| dirty(coll, false); |
| } |
| } |
| |
| /** |
| * Call after invoking {@link Collection#remove} on super. |
| * |
| * @param removed whether the object was removed |
| * @return whether the object was removed, for convenience |
| */ |
| public static boolean afterRemove(ProxyCollection coll, Object o, |
| boolean removed){ |
| boolean isDelayed = isDelayed(coll); |
| boolean direct = isDirectAccess(coll); |
| if (!isDelayed) { |
| if (!removed) |
| return false; |
| } |
| if (!direct && coll.getChangeTracker() != null) { |
| // switch on direct access to prevent the removed op from |
| // inadvertently loading the collection |
| setDirectAccess(coll, true); |
| ((CollectionChangeTracker) coll.getChangeTracker()).removed(o); |
| setDirectAccess(coll, false); |
| } |
| if (!isDelayed) { |
| removed(coll, o, false); |
| } |
| return true; |
| } |
| |
| private static boolean isDirectAccess(ProxyCollection coll) { |
| if (coll instanceof DelayedProxy) { |
| DelayedProxy dpxy = (DelayedProxy)coll; |
| return dpxy.isDirectAccess(); |
| } |
| return false; |
| } |
| |
| private static void setDirectAccess(ProxyCollection coll, boolean direct) { |
| if (coll instanceof DelayedProxy) { |
| DelayedProxy dpxy = (DelayedProxy)coll; |
| dpxy.setDirectAccess(direct); |
| } |
| } |
| |
| /** |
| * Call before invoking {@link Vector#removeElement} on super. |
| */ |
| public static void beforeRemoveElement(ProxyCollection coll, Object o) { |
| beforeRemove(coll, o); |
| } |
| |
| /** |
| * Call after invoking {@link Vector#removeElement} on super. |
| */ |
| public static boolean afterRemoveElement(ProxyCollection coll, Object o, |
| boolean removed) { |
| return afterRemove(coll, o, removed); |
| } |
| |
| /** |
| * Call before invoking {@link LinkedList#removeFirst} on super. |
| */ |
| public static void beforeRemoveFirst(ProxyCollection coll) { |
| beforeRemove(coll, 0); |
| } |
| |
| /** |
| * Call after invoking {@link LinkedList#removeFirst} on super. |
| */ |
| public static Object afterRemoveFirst(ProxyCollection coll, Object removed){ |
| return afterRemove(coll, 0, removed); |
| } |
| |
| /** |
| * Call after invoking {@link LinkedList#removeLast} on super. |
| */ |
| public static void beforeRemoveLast(ProxyCollection coll) { |
| beforeRemove(coll, coll.size() - 1); |
| } |
| |
| /** |
| * Call after invoking {@link LinkedList#removeLast} on super. |
| */ |
| public static Object afterRemoveLast(ProxyCollection coll, Object removed) { |
| return afterRemove(coll, coll.size(), removed); |
| } |
| |
| /** |
| * Call before invoking {@link Queue#remove} on super. |
| */ |
| public static void beforeRemove(ProxyCollection coll) { |
| beforeRemove(coll, 0); |
| } |
| |
| /** |
| * Call after invoking {@link Queue#remove} on super. |
| */ |
| public static Object afterRemove(ProxyCollection coll, Object removed){ |
| return afterRemove(coll, 0, removed); |
| } |
| |
| /** |
| * Call before invoking {@link Queue#poll} on super. |
| */ |
| public static void beforePoll(ProxyCollection coll) { |
| if (!coll.isEmpty()) |
| beforeRemove(coll, 0); |
| } |
| |
| /** |
| * Call after invoking {@link Queue#poll} on super. |
| */ |
| public static Object afterPoll(ProxyCollection coll, Object removed) { |
| if (removed != null) |
| afterRemove(coll, 0, removed); |
| return removed; |
| } |
| |
| /** |
| * Override for {@link Collection#removeAll}. |
| */ |
| public static boolean removeAll(ProxyCollection coll, Collection<?> vals) { |
| boolean removed = false; |
| for (Object val : vals) { |
| removed |= coll.remove(val); |
| } |
| return removed; |
| } |
| |
| /** |
| * Override for {@link Collection#retainAll}. |
| */ |
| public static boolean retainAll(ProxyCollection coll, Collection<?> vals) { |
| int size = coll.size(); |
| coll.removeIf(o -> !vals.contains(o)); |
| return coll.size() < size; |
| } |
| |
| /** |
| * Call before invoking {@link List#set} on super. |
| */ |
| public static void beforeSet(ProxyCollection coll, int index, |
| Object element) { |
| assertAllowedType(element, coll.getElementType()); |
| dirty(coll, true); |
| } |
| |
| /** |
| * Call after invoking {@link List#set} on super. |
| * |
| * @param replaced the replaced object |
| * @return the replaced object, for convenience |
| */ |
| public static Object afterSet(ProxyCollection coll, int index, |
| Object element, Object replaced) { |
| if (replaced != element) |
| removed(coll, replaced, false); |
| return replaced; |
| } |
| |
| /** |
| * Call before invoking {@link Vector#setElementAt} on super. |
| */ |
| public static void beforeSetElementAt(ProxyCollection coll, Object element, |
| int index) { |
| beforeSet(coll, index, element); |
| } |
| |
| /** |
| * Call after invoking {@link Vector#setElementAt} on super. |
| */ |
| public static Object afterSetElementAt(ProxyCollection coll, Object element, |
| int index, Object replaced) { |
| return afterSet(coll, index, element, replaced); |
| } |
| |
| /** |
| * Marker interface for a proxied iterator. |
| */ |
| public interface ProxyIterator |
| extends Iterator { |
| } |
| |
| /** |
| * Marker interface for a proxied list iterator. |
| */ |
| public interface ProxyListIterator |
| extends ProxyIterator, ListIterator { |
| } |
| |
| public static void loadCollection(ProxyCollection proxy) { |
| loadCollection(proxy, false); |
| } |
| |
| public static void loadCollection(ProxyCollection proxy, boolean detaching) { |
| if (!isDelayed(proxy)) { |
| return; |
| } |
| DelayedProxy dProxy = (DelayedProxy)proxy; |
| if (dProxy.isDirectAccess()) { |
| return; |
| } |
| boolean state[] = new boolean[2]; |
| try { |
| dProxy.setDirectAccess(true); |
| state = checkState(proxy); |
| boolean tracking = false; |
| ChangeTracker ct = proxy.getChangeTracker(); |
| Collection<?> added = null; |
| Collection<?> removed = null; |
| if (ct != null && ct.isTracking() ) { |
| if (!ct.getAdded().isEmpty()) { |
| added = new ArrayList(ct.getAdded()); |
| } |
| if (!ct.getRemoved().isEmpty()) { |
| removed = new ArrayList(ct.getRemoved()); |
| } |
| tracking = true; |
| ct.stopTracking(); |
| } |
| if (proxy.size() > 0) { |
| proxy.clear(); |
| } |
| dProxy.getDelayedOwner().loadDelayedField(dProxy.getDelayedField()); |
| if (!detaching && tracking && !ct.isTracking()) { |
| ct.startTracking(); |
| } |
| // add new elements |
| if (added != null && added.size() > 0) { |
| dProxy.setDirectAccess(false); |
| proxy.addAll(added); |
| added.clear(); |
| } |
| // purge removed elements |
| if (removed != null && removed.size() > 0) { |
| dProxy.setDirectAccess(false); |
| proxy.removeAll(removed); |
| removed.clear(); |
| } |
| } finally { |
| dProxy.setDirectAccess(false); |
| if (state[0]) { |
| dProxy.closeBroker(); |
| } |
| if (state[1]) { |
| clearStateManager(proxy); |
| } |
| } |
| } |
| |
| public static boolean isDelayed(ProxyCollection proxy) { |
| if (proxy instanceof DelayedProxy) { |
| DelayedProxy dProxy = (DelayedProxy)proxy; |
| OpenJPAStateManager sm = dProxy.getDelayedOwner(); |
| return (sm != null && |
| sm.isDelayed(dProxy.getDelayedField())); |
| } |
| return false; |
| } |
| |
| private static boolean[] checkState(ProxyCollection proxy) { |
| boolean[] state = new boolean[2]; |
| DelayedProxy dProxy = (DelayedProxy)proxy; |
| |
| OpenJPAStateManager sm = dProxy.getDelayedOwner(); |
| if (sm != null) { |
| // If the broker assigned to this proxy is null, closed or no longer |
| // manages the pc, produce a new one |
| Broker broker = sm.getContext().getBroker(); |
| if (dProxy.isDetached() || broker == null || broker.isClosed() |
| || (!broker.isClosed() && !broker.isPersistent(sm.getPersistenceCapable()))) { |
| state[0] = true; |
| broker = dProxy.getBroker(); |
| ((StateManagerImpl)sm).setBroker((BrokerImpl)broker); |
| } |
| if (dProxy.isDetached() || sm.getPersistenceCapable().pcGetStateManager() == null) { |
| state[1] = true; |
| if (dProxy.getOwnerStateManager() != null) { |
| sm.getPersistenceCapable().pcReplaceStateManager(dProxy.getOwnerStateManager()); |
| ((StateManagerImpl)dProxy.getOwnerStateManager()).setBroker((BrokerImpl)broker); |
| } else { |
| sm.getPersistenceCapable().pcReplaceStateManager( |
| new DetachedValueStateManager(sm.getPersistenceCapable(), sm.getContext())); |
| } |
| } |
| } |
| return state; |
| } |
| |
| private static void clearStateManager(ProxyCollection proxy) { |
| OpenJPAStateManager sm = proxy.getOwner(); |
| if (sm != null) { |
| sm.getPersistenceCapable().pcReplaceStateManager(null); |
| } |
| } |
| } |