blob: 64c30d45c766ef9f073afcc3155116b6f866cb30 [file] [log] [blame]
/*
* 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");
}
};
}
}
}