| /* |
| * Copyright (c) 2010-2013, 2015 Eike Stepper (Berlin, Germany) and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Eike Stepper - initial API and implementation |
| */ |
| package org.eclipse.emf.cdo.server.ocl; |
| |
| import org.eclipse.emf.cdo.common.branch.CDOBranch; |
| import org.eclipse.emf.cdo.common.commit.CDOChangeKind; |
| import org.eclipse.emf.cdo.common.commit.CDOChangeSetData; |
| import org.eclipse.emf.cdo.common.id.CDOID; |
| import org.eclipse.emf.cdo.common.model.CDOPackageRegistry; |
| import org.eclipse.emf.cdo.common.revision.CDOIDAndVersion; |
| import org.eclipse.emf.cdo.common.revision.CDORevision; |
| import org.eclipse.emf.cdo.common.revision.CDORevisionCacheAdder; |
| import org.eclipse.emf.cdo.common.revision.CDORevisionHandler; |
| import org.eclipse.emf.cdo.server.IStoreAccessor; |
| import org.eclipse.emf.cdo.server.StoreThreadLocal; |
| import org.eclipse.emf.cdo.spi.server.InternalRepository; |
| import org.eclipse.emf.cdo.util.ObjectNotFoundException; |
| import org.eclipse.emf.cdo.view.CDOView; |
| |
| import org.eclipse.emf.ecore.EClass; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.emf.spi.cdo.InternalCDOObject; |
| |
| import java.util.Collection; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.NoSuchElementException; |
| import java.util.Set; |
| import java.util.concurrent.CountDownLatch; |
| import java.util.concurrent.atomic.AtomicBoolean; |
| |
| /** |
| * An OCL {@link OCLExtentCreator extent creator} implementation for CDO. |
| * <p> |
| * The {@link #createExtent(EClass, AtomicBoolean) extent} of a {@link EClass class} X is the set of all {@link EObject objects} with <code>object.getEClass() == X</code>. |
| * |
| * @author Eike Stepper |
| */ |
| public class CDOExtentCreator implements OCLExtentCreator |
| { |
| private CDOView view; |
| |
| private CDOChangeSetData changeSetData; |
| |
| private CDORevisionCacheAdder revisionCacheAdder; |
| |
| public CDOExtentCreator(CDOView view) |
| { |
| this.view = view; |
| } |
| |
| public CDOView getView() |
| { |
| return view; |
| } |
| |
| public CDOChangeSetData getChangeSetData() |
| { |
| return changeSetData; |
| } |
| |
| public void setChangeSetData(CDOChangeSetData changeSetData) |
| { |
| this.changeSetData = changeSetData; |
| } |
| |
| public CDORevisionCacheAdder getRevisionCacheAdder() |
| { |
| return revisionCacheAdder; |
| } |
| |
| public void setRevisionCacheAdder(CDORevisionCacheAdder revisionCacheAdder) |
| { |
| this.revisionCacheAdder = revisionCacheAdder; |
| } |
| |
| public Set<EObject> createExtent(EClass eClass, AtomicBoolean canceled) |
| { |
| IStoreAccessor accessor = StoreThreadLocal.getAccessor(); |
| CDOBranch branch = view.getBranch(); |
| long timeStamp = view.getTimeStamp(); |
| return createExtent(eClass, accessor, branch, timeStamp, canceled); |
| } |
| |
| protected Set<EObject> createExtent(EClass eClass, IStoreAccessor accessor, CDOBranch branch, long timeStamp, |
| final AtomicBoolean canceled) |
| { |
| final Set<EObject> extent = new HashSet<EObject>(); |
| if (changeSetData != null) |
| { |
| List<CDOIDAndVersion> newObjects = changeSetData.getNewObjects(); |
| if (newObjects != null) |
| { |
| for (CDOIDAndVersion key : newObjects) |
| { |
| EObject object = getEObject(key.getID()); |
| if (object != null) |
| { |
| extent.add(object); |
| } |
| } |
| } |
| } |
| |
| CDORevisionHandler revisionHandler = new CDORevisionHandler.Filtered.Undetached(new CDORevisionHandler() |
| { |
| public boolean handleRevision(CDORevision revision) |
| { |
| if (revisionCacheAdder != null) |
| { |
| revisionCacheAdder.addRevision(revision); |
| } |
| |
| CDOID id = revision.getID(); |
| if (!isDetached(id)) |
| { |
| EObject object = getEObject(id); |
| if (object != null) |
| { |
| extent.add(object); |
| } |
| } |
| |
| return !canceled.get(); |
| } |
| }); |
| |
| createExtent(eClass, accessor, branch, timeStamp, canceled, revisionHandler); |
| return extent; |
| } |
| |
| /** |
| * @since 4.1 |
| */ |
| protected void createExtent(EClass eClass, IStoreAccessor accessor, CDOBranch branch, long timeStamp, |
| final AtomicBoolean canceled, CDORevisionHandler revisionHandler) |
| { |
| handleRevisions(eClass, accessor, branch, timeStamp, revisionHandler); |
| |
| CDOPackageRegistry packageRegistry = accessor.getStore().getRepository().getPackageRegistry(); |
| List<EClass> subTypes = packageRegistry.getSubTypes().get(eClass); |
| if (subTypes != null) |
| { |
| for (EClass subType : subTypes) |
| { |
| if (canceled.get()) |
| { |
| break; |
| } |
| |
| handleRevisions(subType, accessor, branch, timeStamp, revisionHandler); |
| } |
| } |
| } |
| |
| /** |
| * @since 4.2 |
| */ |
| protected void handleRevisions(EClass eClass, IStoreAccessor accessor, CDOBranch branch, long timeStamp, |
| CDORevisionHandler revisionHandler) |
| { |
| if (!eClass.isAbstract() && !eClass.isInterface()) |
| { |
| InternalRepository repository = (InternalRepository)accessor.getStore().getRepository(); |
| repository.handleRevisions(eClass, branch, false, timeStamp, false, revisionHandler); |
| } |
| } |
| |
| protected boolean isDetached(CDOID id) |
| { |
| if (changeSetData == null) |
| { |
| return false; |
| } |
| |
| CDOChangeKind changeKind = changeSetData.getChangeKind(id); |
| return changeKind == CDOChangeKind.DETACHED; |
| } |
| |
| protected EObject getEObject(CDOID id) throws ObjectNotFoundException |
| { |
| InternalCDOObject object = (InternalCDOObject)view.getObject(id); |
| if (object == null) |
| { |
| throw new ObjectNotFoundException(id); |
| } |
| |
| return object.cdoInternalInstance(); |
| } |
| |
| /** |
| * An {@link CDOExtentCreator extent creator} that creates extent sets which support a lazy populating iterator. |
| * |
| * @author Eike Stepper |
| */ |
| public static class Lazy extends CDOExtentCreator |
| { |
| public Lazy(CDOView view) |
| { |
| super(view); |
| } |
| |
| @Override |
| protected Set<EObject> createExtent(final EClass eClass, final IStoreAccessor accessor, final CDOBranch branch, |
| final long timeStamp, final AtomicBoolean canceled) |
| { |
| return new Set<EObject>() |
| { |
| private Iterator<EObject> emptyIterator; |
| |
| private Boolean empty; |
| |
| private CountDownLatch emptyKnown = new CountDownLatch(1); |
| |
| public synchronized boolean isEmpty() |
| { |
| if (empty != null) |
| { |
| return empty; |
| } |
| |
| emptyIterator = iterator(); |
| |
| try |
| { |
| emptyKnown.await(); |
| return empty; |
| } |
| catch (InterruptedException ex) |
| { |
| throw new Error("Interrupted"); |
| } |
| } |
| |
| public synchronized Iterator<EObject> iterator() |
| { |
| if (emptyIterator != null) |
| { |
| Iterator<EObject> it = emptyIterator; |
| emptyIterator = null; |
| return it; |
| } |
| |
| final Object mutex = new Object(); |
| final LinkedList<CDOID> ids = new LinkedList<CDOID>(); |
| final boolean[] done = { false }; |
| |
| Thread thread = new Thread("OCLExtentIterator") |
| { |
| @Override |
| public void run() |
| { |
| handleDirtyState(); |
| handlePersistentState(); |
| |
| synchronized (mutex) |
| { |
| done[0] = true; |
| mutex.notify(); |
| } |
| |
| if (empty == null) |
| { |
| empty = true; |
| emptyKnown.countDown(); |
| } |
| } |
| |
| private void handleDirtyState() |
| { |
| CDOChangeSetData changeSetData = getChangeSetData(); |
| if (changeSetData != null) |
| { |
| List<CDOIDAndVersion> newObjects = changeSetData.getNewObjects(); |
| if (newObjects != null) |
| { |
| for (CDOIDAndVersion key : newObjects) |
| { |
| enqueue(key.getID()); |
| } |
| } |
| } |
| } |
| |
| private void handlePersistentState() |
| { |
| CDORevisionHandler revisionHandler = new CDORevisionHandler.Filtered.Undetached(new CDORevisionHandler() |
| { |
| public boolean handleRevision(CDORevision revision) |
| { |
| empty = false; |
| emptyKnown.countDown(); |
| |
| CDORevisionCacheAdder revisionCacheAdder = getRevisionCacheAdder(); |
| if (revisionCacheAdder != null) |
| { |
| revisionCacheAdder.addRevision(revision); |
| } |
| |
| CDOID id = revision.getID(); |
| if (!isDetached(id)) |
| { |
| enqueue(id); |
| } |
| |
| return !canceled.get(); |
| } |
| }); |
| |
| createExtent(eClass, accessor, branch, timeStamp, canceled, revisionHandler); |
| } |
| |
| private void enqueue(CDOID id) |
| { |
| synchronized (mutex) |
| { |
| ids.addLast(id); |
| mutex.notify(); |
| } |
| } |
| }; |
| |
| thread.setDaemon(true); |
| thread.start(); |
| |
| return new Iterator<EObject>() |
| { |
| private CDOID next; |
| |
| public boolean hasNext() |
| { |
| while (next == null) |
| { |
| if (canceled.get()) |
| { |
| return false; |
| } |
| |
| synchronized (mutex) |
| { |
| if (ids.isEmpty()) |
| { |
| if (done[0]) |
| { |
| return false; |
| } |
| |
| try |
| { |
| mutex.wait(500L); |
| } |
| catch (InterruptedException ex) |
| { |
| throw new Error(ex); |
| } |
| } |
| else |
| { |
| next = ids.removeFirst(); |
| } |
| } |
| } |
| |
| return true; |
| } |
| |
| public EObject next() |
| { |
| if (!hasNext()) |
| { |
| throw new NoSuchElementException(); |
| } |
| |
| try |
| { |
| return getEObject(next); |
| } |
| finally |
| { |
| next = null; |
| } |
| } |
| |
| public void remove() |
| { |
| throw new UnsupportedOperationException(); |
| } |
| }; |
| } |
| |
| public int size() |
| { |
| throw new Error("Not supported"); // RuntimeException gets swallowed up the stack! |
| } |
| |
| public boolean contains(Object o) |
| { |
| throw new Error("Not supported"); |
| } |
| |
| public Object[] toArray() |
| { |
| throw new Error("Not supported"); |
| } |
| |
| public <T> T[] toArray(T[] a) |
| { |
| throw new Error("Not supported"); |
| } |
| |
| public boolean add(EObject o) |
| { |
| throw new Error("Not supported"); |
| } |
| |
| public boolean remove(Object o) |
| { |
| throw new Error("Not supported"); |
| } |
| |
| public boolean containsAll(Collection<?> c) |
| { |
| throw new Error("Not supported"); |
| } |
| |
| public boolean addAll(Collection<? extends EObject> c) |
| { |
| throw new Error("Not supported"); |
| } |
| |
| public boolean retainAll(Collection<?> c) |
| { |
| throw new Error("Not supported"); |
| } |
| |
| public boolean removeAll(Collection<?> c) |
| { |
| throw new Error("Not supported"); |
| } |
| |
| public void clear() |
| { |
| throw new Error("Not supported"); |
| } |
| }; |
| } |
| } |
| } |