package com.sap.runlet.abstractinterpreter;
import java.lang.reflect.InvocationTargetException;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import java.util.TreeMap;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.ResourceSet;
import com.sap.runlet.abstractinterpreter.objects.AbstractValueObject;
import com.sap.runlet.abstractinterpreter.objects.ClassTypedObject;
import com.sap.runlet.abstractinterpreter.objects.EntityObject;
import com.sap.runlet.abstractinterpreter.objects.Link;
import com.sap.runlet.abstractinterpreter.objects.MultiValuedObject;
import com.sap.runlet.abstractinterpreter.objects.RunletObject;
import com.sap.runlet.abstractinterpreter.repository.EntityCreation;
import com.sap.runlet.abstractinterpreter.repository.EntityDeletion;
import com.sap.runlet.abstractinterpreter.repository.FixedSnapshot;
import com.sap.runlet.abstractinterpreter.repository.Repository;
import com.sap.runlet.abstractinterpreter.repository.RepositoryObject;
import com.sap.runlet.abstractinterpreter.repository.Snapshot;
import com.sap.runlet.abstractinterpreter.repository.SnapshotIdentifier;
import com.sap.runlet.abstractinterpreter.util.Bag;
import com.sap.runlet.abstractinterpreter.util.ModelAdapter;
import com.sap.runlet.abstractinterpreter.util.Tuple.Pair;
/**
*
*/
abstract public class AbstractRunletInterpreter<MetaClass extends EObject,
TypeUsage extends EObject,
ClassUsage extends TypeUsage,
LinkMetaObject extends EObject,
LinkEndMetaObject extends EObject,
StatementType extends EObject,
ExpressionType extends EObject,
SignatureImplementationType extends EObject,
StackFrameType extends StackFrame<LinkEndMetaObject, TypeUsage, ClassUsage, SignatureImplementationType>,
NativeType extends SignatureImplementationType,
InterpreterType extends AbstractRunletInterpreter<MetaClass, TypeUsage, ClassUsage, LinkMetaObject, LinkEndMetaObject, StatementType, ExpressionType, SignatureImplementationType, StackFrameType, NativeType, InterpreterType>> {
private final EClassInterpreterFactory<ExpressionType, MetaClass, TypeUsage, ClassUsage, LinkMetaObject, LinkEndMetaObject, StatementType, ExpressionType, SignatureImplementationType, StackFrameType, NativeType, InterpreterType> expressionInterpreterFactory;
private final EClassInterpreterFactory<StatementType, MetaClass, TypeUsage, ClassUsage, LinkMetaObject, LinkEndMetaObject, StatementType, ExpressionType, SignatureImplementationType, StackFrameType, NativeType, InterpreterType> statementInterpreterFactory;
private final EClassInterpreterFactory<SignatureImplementationType, MetaClass, TypeUsage, ClassUsage, LinkMetaObject, LinkEndMetaObject, StatementType, ExpressionType, SignatureImplementationType, StackFrameType, NativeType, InterpreterType> signatureImplementationInterpreterFactory;
private final LinkContainer<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage, StatementType, ExpressionType, SignatureImplementationType, StackFrameType, NativeType, InterpreterType> linkContainer;
/**
* This is where interpreter clients may register interpreters that can
* handle all natively-implemented operations of a class. Keys are the names
* of the class whose native method implementations are handled by the
* respective value (the interpreter).
*/
private final InterpreterFactory<MetaClass, NativeType, MetaClass, TypeUsage, ClassUsage, LinkMetaObject, LinkEndMetaObject, StatementType, ExpressionType, SignatureImplementationType, StackFrameType, NativeType, InterpreterType> nativeInterpreterFactory;
/**
* The stack of {@link StackFrame}s. Always valid, but possibly empty when currently no call is active in this interpreter.
* The element obtained by {@link Stack#peek()} is the currently valid frame. Frames that are
* {@link StackFrame#getScopeParent() scope parent} of another frame are also contained on this stack. This means that the
* stack is a superset of frames corresponding to the call hierarchy. It moreover reflects block nesting.
*/
private Stack<StackFrameType> callstack;
/**
* It is useful for an interpreter to know the interpreter that spawned it.
* Whenever the child will start executing, it can enter itself into the
* parent interpreter's {@link #runningChildren} so that a parent always
* knows which of its children is currently executing. This is particularly
* useful for a debug view.
*/
private final InterpreterType parent;
/**
* An interpreter can {@link #spawn() spawn} children, usually meaning an
* own thread of execution. Each interpreter resultin from {@link #spawn()}
* is added to this set.
*/
private final Set<InterpreterType> runningChildren;
/**
* Tells if a call of one of {@link #evaluate(ExpressionType)},
* {@link #evaluate(SignatureImplementation)} or
* {@link #evaluate(StatementType)} is currently running. Iff <tt>true</tt>,
* this interpreter will show up in its {@link #parent}'s
* {@link #runningChildren}, if it has a parent.
*/
private boolean running;
/**
* Tells if this interpreter has been explicitly terminated by a call to
* {@link #terminate}. This may be particularly useful for an interpreter
* used for debugging when a launch has finally terminated.
*/
private boolean terminated;
/**
* If <tt>null</tt>, the interpreter operates in "run" mode; otherwise, it
* operates in "debug" mode and will negotiate with the debug session when
* to stop, suspend and resume execution.
*/
private DebugSession debugSession;
/**
* The snapshot to be used when reading from the repository without a
* specified snapshot. May be <tt>null</tt> which means that the next
* repository interaction is free to set this to what then is considered to
* be the "current" snapshot.
*/
private SnapshotIdentifier defaultSnapshot;
private final TransactionBuffer<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage> transactionBuffer;
private final Repository<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage> repository;
private final ResourceSet resourceSet;
private final ModelAdapter<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage> modelAdapter;
public AbstractRunletInterpreter(
ResourceSet conn,
Repository<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage> repository,
ModelAdapter<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage> modelAdapter,
InterpreterFactory<MetaClass, NativeType, MetaClass, TypeUsage, ClassUsage, LinkMetaObject, LinkEndMetaObject, StatementType, ExpressionType, SignatureImplementationType, StackFrameType, NativeType, InterpreterType> nativeInterpreterFactory,
LinkContainer<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage, StatementType, ExpressionType, SignatureImplementationType, StackFrameType, NativeType, InterpreterType> linkContainer) {
parent = null;
this.linkContainer = linkContainer;
this.resourceSet = conn;
this.repository = repository;
this.modelAdapter = modelAdapter;
expressionInterpreterFactory = new EClassInterpreterFactory<ExpressionType, MetaClass, TypeUsage, ClassUsage, LinkMetaObject, LinkEndMetaObject, StatementType, ExpressionType, SignatureImplementationType, StackFrameType, NativeType, InterpreterType>();
statementInterpreterFactory = new EClassInterpreterFactory<StatementType, MetaClass, TypeUsage, ClassUsage, LinkMetaObject, LinkEndMetaObject, StatementType, ExpressionType, SignatureImplementationType, StackFrameType, NativeType, InterpreterType>();
signatureImplementationInterpreterFactory = new EClassInterpreterFactory<SignatureImplementationType, MetaClass, TypeUsage, ClassUsage, LinkMetaObject, LinkEndMetaObject, StatementType, ExpressionType, SignatureImplementationType, StackFrameType, NativeType, InterpreterType>();
this.nativeInterpreterFactory = nativeInterpreterFactory;
runningChildren = new HashSet<InterpreterType>();
transactionBuffer = new TransactionBuffer<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage>(
modelAdapter);
setCallstack(new Stack<StackFrameType>());
initInterpreterFactories(conn);
setDefaultSnapshot(repository.getTrunkIdentifier());
}
public AbstractRunletInterpreter(InterpreterType parent) {
this.parent = parent;
this.linkContainer = parent.getLinkContainer();
this.resourceSet = parent.getResourceSet();
this.repository = parent.getRepository();
this.modelAdapter = parent.getModelAdapter();
defaultSnapshot = parent.getDefaultSnapshot();
expressionInterpreterFactory = parent.getExpressionInterpreterFactory();
statementInterpreterFactory = parent.getStatementInterpreterFactory();
signatureImplementationInterpreterFactory = parent.getSignatureImplementationInterpreterFactory();
this.nativeInterpreterFactory = parent.getNativeInterpreterFactory();
runningChildren = new HashSet<InterpreterType>();
transactionBuffer = parent.getTransactionBuffer();
setCallstack(new Stack<StackFrameType>());
if (parent.getCallstack().size() > 0) {
getCallstack().push(parent.getCallstack().peek());
}
setDebugSession(parent.getDebugSession());
}
private void initInterpreterFactories(ResourceSet conn) {
initExpressionInterpreterFactory(conn);
initStatementInterpreterFactory(conn);
initSignatureImplementationInterpreterFactory(conn);
initNativeInterpreterFactory(conn);
}
protected abstract void initExpressionInterpreterFactory(ResourceSet conn);
protected abstract void initStatementInterpreterFactory(ResourceSet conn);
protected abstract void initSignatureImplementationInterpreterFactory(ResourceSet conn);
protected abstract void initNativeInterpreterFactory(ResourceSet conn);
public ModelAdapter<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage> getModelAdapter() {
return modelAdapter;
}
@SuppressWarnings("unchecked")
InterpreterType getThis() {
return (InterpreterType) this;
}
protected LinkContainer<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage, StatementType, ExpressionType, SignatureImplementationType, StackFrameType, NativeType, InterpreterType> getLinkContainer() {
return linkContainer;
}
public void registerExpressionInterpreter(Class<Interpreter<ExpressionType, MetaClass, TypeUsage, ClassUsage, LinkMetaObject, LinkEndMetaObject, StatementType, ExpressionType, SignatureImplementationType, StackFrameType, NativeType, InterpreterType>> interpreter, EClass... classes) {
getExpressionInterpreterFactory().registerInterpreter(interpreter, classes);
}
public void registerStatementInterpreter(Class<Interpreter<StatementType, MetaClass, TypeUsage, ClassUsage, LinkMetaObject, LinkEndMetaObject, StatementType, ExpressionType, SignatureImplementationType, StackFrameType, NativeType, InterpreterType>> interpreter, EClass... classes) {
getStatementInterpreterFactory().registerInterpreter(interpreter, classes);
}
public void registerSignatureImplementationInterpreter(Class<Interpreter<SignatureImplementationType, MetaClass, TypeUsage, ClassUsage, LinkMetaObject, LinkEndMetaObject, StatementType, ExpressionType, SignatureImplementationType, StackFrameType, NativeType, InterpreterType>> interpreter, EClass... classes) {
getSignatureImplementationInterpreterFactory().registerInterpreter(interpreter, classes);
}
public void registerNativeImplementationInterpreter(Class<? extends Interpreter<? extends NativeType, MetaClass, TypeUsage, ClassUsage, LinkMetaObject, LinkEndMetaObject, StatementType, ExpressionType, SignatureImplementationType, StackFrameType, NativeType, InterpreterType>> interpreter,
MetaClass... classes) {
getNativeInterpreterFactory().registerInterpreter(interpreter, classes);
}
protected EClassInterpreterFactory<ExpressionType, MetaClass, TypeUsage, ClassUsage, LinkMetaObject, LinkEndMetaObject, StatementType, ExpressionType, SignatureImplementationType, StackFrameType, NativeType, InterpreterType> getExpressionInterpreterFactory() {
return expressionInterpreterFactory;
}
protected EClassInterpreterFactory<StatementType, MetaClass, TypeUsage, ClassUsage, LinkMetaObject, LinkEndMetaObject, StatementType, ExpressionType, SignatureImplementationType, StackFrameType, NativeType, InterpreterType> getStatementInterpreterFactory() {
return statementInterpreterFactory;
}
protected EClassInterpreterFactory<SignatureImplementationType, MetaClass, TypeUsage, ClassUsage, LinkMetaObject, LinkEndMetaObject, StatementType, ExpressionType, SignatureImplementationType, StackFrameType, NativeType, InterpreterType> getSignatureImplementationInterpreterFactory() {
return signatureImplementationInterpreterFactory;
}
private InterpreterFactory<MetaClass, NativeType, MetaClass, TypeUsage, ClassUsage, LinkMetaObject, LinkEndMetaObject, StatementType, ExpressionType, SignatureImplementationType, StackFrameType, NativeType, InterpreterType> getNativeInterpreterFactory() {
return nativeInterpreterFactory;
}
/**
* Returns the extension interpreter for natively-implemented methods of
* class <tt>classname</tt> or <tt>null</tt> if no such interpreter is
* currently registered.
*
* @see #registerNativeImplementation(String, Interpreter)
*/
public Interpreter<? extends NativeType, MetaClass, TypeUsage, ClassUsage, LinkMetaObject, LinkEndMetaObject, StatementType, ExpressionType, SignatureImplementationType, StackFrameType, NativeType, InterpreterType> getNativeInterpreterFor(
NativeType nativeImpl) throws IllegalArgumentException, InstantiationException, IllegalAccessException,
InvocationTargetException {
return getNativeInterpreterFactory().getInterpreterFor(nativeImpl);
}
/**
* Tells this interpreter that a child interpreter obtained from
* {@link #spawn()} started executing something. It will be added to the
* {@link #runningChildren} set.
*/
public void addRunningChild(InterpreterType child) {
runningChildren.add(child);
}
/**
* Tells this interpreter that a child interpreter obtained from
* {@link #spawn()} finished executing and therefore is no longer active. It
* will be removed from the {@link #runningChildren} set.
*/
public void removeRunningChild(InterpreterType child) {
runningChildren.remove(child);
}
/**
* The set of currently executing immediate child interpreters that were
* obtained by calling {@link #spawn()} on this interpreter.
*/
@SuppressWarnings("unchecked")
public InterpreterType[] getRunningChildren() {
InterpreterType[] result =
(InterpreterType[]) runningChildren.toArray(new AbstractRunletInterpreter<?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?>[0]);
return result;
}
private void setCallstack(Stack<StackFrameType> callstack) {
this.callstack = callstack;
}
/**
* Obtains the callstack, reflecting block nesting <em>and</em> call
* structure
*/
public Stack<StackFrameType> getCallstack() {
return callstack;
}
public void push(StackFrameType stackFrame) {
getCallstack().push(stackFrame);
}
public void pop() {
getCallstack().pop();
}
/**
* Retrieves the stack frame that is one up in the call hierarchy, skipping
* scope parents. In other words, if <tt>frame</tt> is the stack frame for a
* method, the frame resulting from this call will be the frame of the
* caller.
* <p>
*
* If <tt>frame</tt> is already at the top of the call hierarchy,
* representing the entry or "main" call into this interpreter, this method
* returns <tt>null</tt>.
*/
@SuppressWarnings("unchecked")
public StackFrameType oneUp(StackFrameType frame) {
StackFrameType result = null;
StackFrameType current = frame;
while (current.getScopeParent() != null) {
// this cast is safe if only stack frames of equal type are used as parents
current = (StackFrameType) current.getScopeParent();
}
int i = getCallstack().indexOf(current) - 1;
if (i >= 0) {
result = getCallstack().get(i);
}
return result;
}
/**
* Retrieves the stack frame that is one down in the call hierarchy,
* skipping scope parents. In other words, if <tt>frame</tt> is the stack
* frame for a method currently at a method call, the frame resulting from
* this call will be the frame of the called method.
* <p>
*
* If <tt>frame</tt> is already at the bottom of the call hierarchy, this
* method returns <tt>null</tt>.
*/
public StackFrameType oneDown(StackFrameType frame) {
int i = getCallstack().indexOf(frame) + 1;
StackFrameType result = null;
if (i < getCallstack().size()) {
result = getCallstack().get(i);
while (i < getCallstack().size() - 1 && getCallstack().get(i + 1).getScopeParent() != null) {
i++;
result = getCallstack().get(i);
}
}
return result;
}
/**
* Creates a link between the two objects <tt>left</tt> and <tt>right</tt>
* on behalf of the respective association and adds it to this
* {@link RunletLinkContainer}.
*/
@SuppressWarnings("unchecked")
public void addLink(ClassTypedObject<LinkEndMetaObject, TypeUsage, ClassUsage> left,
ClassTypedObject<LinkEndMetaObject, TypeUsage, ClassUsage> right, LinkMetaObject association, Integer at) {
getLinkContainer().addLinkFromObjectsForEnds(left, right, association, at, /* snapshotContext */getTransactionBuffer(),
(InterpreterType) this);
}
/**
* Removes a link between the two objects <tt>left</tt> and <tt>right</tt>
* on behalf of the respective association from this {@link RunletLinkContainer}.
* If multiple such links exist (non-unique association end), one of them is
* picked arbitrarily.
*/
public void removeLink(ClassTypedObject<LinkEndMetaObject, TypeUsage, ClassUsage> left,
ClassTypedObject<LinkEndMetaObject, TypeUsage, ClassUsage> right,
LinkMetaObject association, Integer at) {
getLinkContainer().removeLink(left, right, association, at, getTransactionBuffer(), getDefaultSnapshot(), this);
}
public void deleteEntity(EntityObject<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage> toDelete) {
assert toDelete.isPersistent();
ObjectAndRemoteAssociationEnd<LinkMetaObject, LinkEndMetaObject, TypeUsage, ClassUsage, MetaClass> parentAndChildEnd =
getLinkContainer().getCompositeParentObjectAndChildAssociationEnd(toDelete);
if (parentAndChildEnd != null) {
ClassTypedObject<LinkEndMetaObject, TypeUsage, ClassUsage> left;
ClassTypedObject<LinkEndMetaObject, TypeUsage, ClassUsage> right;
if (getModelAdapter().getSideOfEnd(parentAndChildEnd.getRemoteAssociationEnd()).equals(Side.LEFT)) {
left = toDelete;
right = parentAndChildEnd.getObject();
} else {
left = parentAndChildEnd.getObject();
right = toDelete;
}
removeLink(left, right, getModelAdapter().getAssociation(parentAndChildEnd.getRemoteAssociationEnd()),
/* should be only one such link, so position doesn't matter */ null);
}
Bag<RepositoryObject> expandedComposition = getLinkContainer().getCompositionTree(toDelete);
deleteRepositoryObjects(expandedComposition);
}
/**
* Records the deletions of all objects in <tt>expandedComposition</tt> in
* the {@link #getTransactionBuffer()}. For links of ordered associations the
* ordering of links of the same association attached to the same object
* with their unordered end needs to be determined so that during recording
* deletion in the transaction buffer indexes are not messed up.
*/
@SuppressWarnings("unchecked")
private void deleteRepositoryObjects(Bag<RepositoryObject> expandedComposition) {
Map<Pair<LinkMetaObject, ClassTypedObject<LinkEndMetaObject, TypeUsage, ClassUsage>>, TreeMap<Integer, Link<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage>>> links =
new HashMap<Pair<LinkMetaObject, ClassTypedObject<LinkEndMetaObject, TypeUsage, ClassUsage>>, TreeMap<Integer, Link<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage>>>();
List<EntityObject<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage>> entityDeletions = new LinkedList<EntityObject<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage>>();
for (RepositoryObject ro : expandedComposition) {
if (ro instanceof EntityObject) {
entityDeletions.add((EntityObject<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage>) ro);
} else {
Link<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage> link = (Link<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage>) ro;
if (link.isOrdered()) {
Pair<LinkMetaObject, ClassTypedObject<LinkEndMetaObject, TypeUsage, ClassUsage>> key = new Pair<LinkMetaObject, ClassTypedObject<LinkEndMetaObject, TypeUsage, ClassUsage>>(link.getAssociation(), link
.getAtOppositeOfOrderedEnd());
TreeMap<Integer, Link<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage>> tm = links.get(key);
if (tm == null) {
tm = new TreeMap<Integer, Link<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage>>();
links.put(key, tm);
}
tm.put(getLinkContainer().getLinkPosition(link), link);
} else {
getTransactionBuffer().linkDeleted(link, null);
}
}
}
for (Pair<LinkMetaObject, ClassTypedObject<LinkEndMetaObject, TypeUsage, ClassUsage>> key : links.keySet()) {
TreeMap<Integer, Link<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage>> tm = links.get(key);
int offset = 0;
for (Integer at : tm.keySet()) {
getTransactionBuffer().linkDeleted(tm.get(at), at-offset);
offset++; // deletion moves links that are behind deleted link towards lower indexes
}
}
// Delete entities last to avoid intermediate dangling links where possible
for (EntityObject<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage> eo : entityDeletions) {
getTransactionBuffer().entityDeleted(eo);
}
}
/**
* Returns a (possible multi-valued, possibly <tt>null</tt>) object
* resulting from navigating the association <tt>over</tt> from
* <tt>from</tt> on <tt>fromSide</tt> of that association to the other end.
* The collection will be empty if no links exist that match these criteria.
* The collection's type corresponds with the other association end's (the
* "to" end's) uniqueness and orderedness settings.
* <p>
*
* If <tt>from</tt> is <tt>null</tt>, <tt>null</tt> will result.
*/
public RunletObject<LinkEndMetaObject, TypeUsage, ClassUsage> navigate(ClassTypedObject<LinkEndMetaObject, TypeUsage, ClassUsage> from, Side fromSide, LinkMetaObject over) {
return getLinkContainer().navigate(from, fromSide, over);
}
/**
* Navigates from <tt>from</tt> to the <tt>to</tt>. For example, if
* <tt>from</tt> denotes a sales order object and the <tt>to</tt> denotes
* the <em>items</tt> end of an association between <tt>SalesOrder</tt> and
* <tt>SalesOrderItem</tt>, then the call will return the sales order items associated
* with the sales order passed as <tt>from</tt>.
*
* @see #navigate(ClassTypedObject, Side, Association)
*/
public RunletObject<LinkEndMetaObject, TypeUsage, ClassUsage> navigate(ClassTypedObject<LinkEndMetaObject, TypeUsage, ClassUsage> from, LinkEndMetaObject to) {
return navigate(from, getModelAdapter().getSideOfEnd(to).otherEnd(),
getModelAdapter().getAssociation(to));
}
public ResourceSet getResourceSet() {
return resourceSet;
}
protected InterpreterType getParent() {
return parent;
}
//FIXME: remove this method!!!
public TransactionBuffer<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage> getTransactionBuffer() {
return transactionBuffer;
}
public Repository<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage> getRepository() {
return repository;
}
/**
* Selects all instances of <tt>ofClass</tt> from all snapshots known by the
* {@link #repository}.
*
*/
public Iterable<EntityObject<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage>> all(MetaClass ofClass) {
Set<Snapshot> snapshots = getRepository().getAllSnapshots();
Set<EntityObject<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage>> result = new HashSet<EntityObject<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage>>();
for (Snapshot si : snapshots) {
result.addAll(all(ofClass, new FixedSnapshot(si)));
}
return result;
}
public Iterable<EntityObject<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage>> all(MetaClass ofClass, Date fromLastConcurrentSnapshotsBefore) {
Set<SnapshotIdentifier> snapshots = getRepository().getLastSnapshotsBeforeOrAt(fromLastConcurrentSnapshotsBefore);
Set<EntityObject<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage>> result = new HashSet<EntityObject<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage>>();
for (SnapshotIdentifier si : snapshots) {
result.addAll(all(ofClass, si));
}
return result;
}
/**
* Returns an {@link Iterable} of all {@link EntityObject}s that were
* changed. The {@link EntityObject}s are linked to the {@link Snapshot}
* they have been changed in.
*
* @param ofClass
* Specifies the {@link MetaClass} of the {@link EntityObject}s to
* retrieve
* @return {@link Iterable} of all {@link EntityObject}s that changed
*/
public Iterable<EntityObject<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage>> allChanged(MetaClass ofClass) {
Set<Snapshot> snapshots = getRepository().getAllSnapshots();
Map<Snapshot, Set<EntityObject<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage>>> entitiesPerSnapshot =
new HashMap<Snapshot, Set<EntityObject<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage>>>();
for (Snapshot si : snapshots) {
Set<EntityObject<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage>> set = new HashSet<EntityObject<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage>>();
set.addAll(all(ofClass, new FixedSnapshot(si)));
entitiesPerSnapshot.put(si, set);
}
return getLinkContainer().removeUnchangedEntities(entitiesPerSnapshot);
}
/**
* Mix the contents of the {@link #getTransactionBuffer()} with the contents of
* the repository for the current default snapshot
*
* @param ofClass
* fetch the instances of the class identified by
* <tt>ofClass</tt>
* @param fromSnapshot
* a non-<tt>null</tt> object; read from the snapshot specified
* or {@link #resolve(SnapshotIdentifier) resolve} in case the
* {@link SnapshotIdentifier#getSnapshot()} returns <tt>null</tt>
* , then read from that snapshot
*/
public Collection<EntityObject<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage>> all(MetaClass ofClass, SnapshotIdentifier fromSnapshot) {
Set<EntityObject<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage>> result =
new HashSet<EntityObject<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage>>(getRepository().all(ofClass, fromSnapshot));
if (transactionBufferUpdates(fromSnapshot)) {
for (Iterator<EntityCreation<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage>> i=getTransactionBuffer().getEntityCreations(); i.hasNext(); ) {
EntityObject<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage> createdObject = i.next().getObject();
if (getModelAdapter().getClazz(createdObject.getType()).equals(ofClass)) {
result.add(createdObject);
}
}
for (Iterator<EntityDeletion<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage>> i=getTransactionBuffer().getEntityDeletions(); i.hasNext(); ) {
EntityObject<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage> deletedObject = i.next().getObject();
if (getModelAdapter().getClazz(deletedObject.getType()).equals(ofClass)) {
result.remove(deletedObject);
}
}
}
return result;
}
/**
* @return <tt>true</tt> if the {@link #getTransactionBuffer()}, when
* {@link TransactionBuffer#commit(Repository) committed} to the
* {@link #repository}, creates a snapshot whose predecessor will be
* the one currently identified by <tt>fromSnapshot</tt>.
*/
private boolean transactionBufferUpdates(SnapshotIdentifier fromSnapshot) {
boolean result;
// if the transaction buffer has not yet been assigned a snapshot
// identifier,
// it doesn't contain anything yet and its empty "contents" can safely
// be
// ignored.
SnapshotIdentifier updating = getTransactionBuffer().getUpdatingTag();
if (updating != null) {
result = updating.equals(fromSnapshot);
} else {
result = false;
}
return result;
}
public void storeEntity(EntityObject<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage> toStore) {
Bag<RepositoryObject> expandedComposition = getLinkContainer().getTransientCompositionTree(toStore);
createRepositoryObjects(expandedComposition);
}
/**
* Records the creation of all objects in <tt>expandedComposition</tt> in
* the {@link #getTransactionBuffer()}. For links of ordered associations the
* ordering of links of the same association attached to the same object
* with their unordered end needs to be determined so that during insertion
* into the transaction buffer indexes are not messed up.
*/
@SuppressWarnings("unchecked")
private void createRepositoryObjects(Bag<RepositoryObject> expandedComposition) {
// collect all links; record entity creations first so that link creations
// are recorded only after their related entity creations have been recorded
Map<Pair<LinkMetaObject, ClassTypedObject<LinkEndMetaObject, TypeUsage, ClassUsage>>,
TreeMap<Integer, Link<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage>>> links =
new HashMap<Pair<LinkMetaObject, ClassTypedObject<LinkEndMetaObject, TypeUsage, ClassUsage>>,
TreeMap<Integer, Link<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage>>>();
List<Link<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage>> unorderedLinks = new LinkedList<Link<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage>>();
for (RepositoryObject ro : expandedComposition) {
if (ro instanceof EntityObject) {
getTransactionBuffer().entityCreated((EntityObject<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage>) ro);
} else {
Link<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage> link = (Link<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage>) ro;
if (link.isOrdered()) {
Pair<LinkMetaObject, ClassTypedObject<LinkEndMetaObject, TypeUsage, ClassUsage>> key =
new Pair<LinkMetaObject, ClassTypedObject<LinkEndMetaObject, TypeUsage, ClassUsage>>(link.getAssociation(), link
.getAtOppositeOfOrderedEnd());
TreeMap<Integer, Link<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage>> tm = links.get(key);
if (tm == null) {
tm = new TreeMap<Integer, Link<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage>>();
links.put(key, tm);
}
// won't overwrite an existing key because link positions
// for same assoc/entity on unordered end are unique
tm.put(getLinkContainer().getLinkPosition(link), link);
} else {
unorderedLinks.add(link);
}
}
}
for (Pair<LinkMetaObject, ClassTypedObject<LinkEndMetaObject, TypeUsage, ClassUsage>> key : links.keySet()) {
TreeMap<Integer, Link<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage>> tm = links.get(key);
for (Integer at : tm.keySet()) {
getTransactionBuffer().linkCreated(tm.get(at), at);
}
}
for (Link<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage> unorderedLink : unorderedLinks) {
getTransactionBuffer().linkCreated(unorderedLink, null);
}
}
/**
* To be called by every <tt>evaluate</tt> operation that "executes"
* something. This is to notify the interpreter that something is being
* executed which may transition the interpreter from the non-running into
* the running state. If this state transition happens, and the interpreter
* has a {@link #parent}, it will register as an active child with the
* parent.
* <p>
*
* At the end of the <tt>evaluate</tt> operation, {@link #exitEvaluate} must
* be called with the result returned by this operation.
*
* @param element
* TODO
*
* @return the old value of the {@link #running} flag
*/
private boolean enterEvaluate(EObject element) {
boolean result = running;
if (!result) {
if (getParent() != null) {
getParent().addRunningChild(getThis());
}
}
if (getDebugSession() != null) {
getDebugSession().aboutToEvaluate(this, element);
}
running = true;
return result;
}
/**
* Sets the {@link #running} flag to <tt>oldRunning</tt>. If the interpreter
* in in state <tt>running==true</tt> when this method is invoked, and the
* interpreter has a {@link #parent}, it deregisters this interpreter from
* its parent because it now is no longer active.
*/
private void exitEvaluate(boolean oldRunning) {
if (running && !oldRunning) {
if (getParent() != null) {
getParent().removeRunningChild(getThis());
}
if (getDebugSession() != null) {
getDebugSession().terminated(this);
}
}
running = oldRunning;
}
public RunletObject<LinkEndMetaObject, TypeUsage, ClassUsage> evaluate(ExpressionType e) throws SecurityException,
IllegalArgumentException, NoSuchMethodException, InstantiationException, IllegalAccessException,
InvocationTargetException {
boolean oldRunning = enterEvaluate(e);
if (e == null) {
throw new RuntimeException("Don't know how to evaluate a null expression");
}
Interpreter<? extends ExpressionType, MetaClass, TypeUsage, ClassUsage, LinkMetaObject, LinkEndMetaObject, StatementType, ExpressionType, SignatureImplementationType, StackFrameType, NativeType, InterpreterType> interpreter = getExpressionInterpreterFactory().getInterpreterFor(e);
if (interpreter == null) {
throw new RuntimeException("No interpreter for expression of type "
+ ((EClass) e.eClass()).getEPackage().getName()+"."+((EClass) e.eClass()).getName());
}
RunletObject<LinkEndMetaObject, TypeUsage, ClassUsage> result = interpreter.evaluate(getThis());
exitEvaluate(oldRunning);
return result;
}
/**
* Returns either <tt>null</tt> or a {@link ReturnInterpreter.ReturnResult}
* which wraps the real result expected to correspond with the signature's
* output specification, such as the return value explicitly returned by a
* block; or a regular {@link RunletObject} that is not a
* {@link ReturnInterpreter.ReturnResult} which indicates the last value
* computed inside the signature implementation. Such an object does not
* indicate a return value for the signature but rather a value that may be
* used during debugging or for display, e.g., in a console.
* <p>
*
* The reason for using wrapped return results for explicit returns is that
* nested blocks may either terminate normally (such as, e.g., in a
* <tt>foreach</tt> loop or a <tt>while</tt> loop) or by a <tt>return</tt>
* statement. In the latter case, the surrounding blocks are all expected to
* terminate as well, passing on the return result up to the signature to
* which this is really the implementation (a <tt>foreach</tt> block is not
* an implementation of a signature, although type-wise it still inherits
* from {@link SignatureImplementation}). The interpreter of the signature
* call will remove the {@link ReturnInterpreter.ReturnResult} wrapper and
* simply return the inner object.
*/
public RunletObject<LinkEndMetaObject, TypeUsage, ClassUsage> evaluateSignatureImplementation(
SignatureImplementationType implementation) throws SecurityException, IllegalArgumentException,
NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
boolean oldRunning = enterEvaluate(implementation);
if (getDebugSession() != null) {
getCallstack().peek().setCurrentlyExecutingImplementation(implementation);
}
Interpreter<? extends SignatureImplementationType, MetaClass, TypeUsage, ClassUsage, LinkMetaObject, LinkEndMetaObject, StatementType, ExpressionType, SignatureImplementationType, StackFrameType, NativeType, InterpreterType> interpreter = getSignatureImplementationInterpreterFactory().getInterpreterFor(implementation);
RunletObject<LinkEndMetaObject, TypeUsage, ClassUsage> result = interpreter.evaluate(getThis());
if (getDebugSession() != null) {
getCallstack().peek().setCurrentlyExecutingImplementation(null);
}
exitEvaluate(oldRunning);
return result;
}
public RunletObject<LinkEndMetaObject, TypeUsage, ClassUsage> evaluateStatement(StatementType statement) throws SecurityException, IllegalArgumentException, NoSuchMethodException,
InstantiationException, IllegalAccessException, InvocationTargetException {
boolean oldRunning = enterEvaluate(statement);
Interpreter<? extends StatementType, MetaClass, TypeUsage, ClassUsage, LinkMetaObject, LinkEndMetaObject, StatementType, ExpressionType, SignatureImplementationType, StackFrameType, NativeType, InterpreterType> interpreter =
getStatementInterpreterFactory().getInterpreterFor(statement);
RunletObject<LinkEndMetaObject, TypeUsage, ClassUsage> result = interpreter.evaluate(getThis());
exitEvaluate(oldRunning);
return result;
}
/**
* The "current default" snapshot that this interpreter will use if the
* application has requested data from the repository without specifying a
* particular snapshot. <tt>null</tt> as long as no interaction with the
* repository has taken place. Will be initialized to the
* {@link Repository#getCurrent()} snapshot upon the first interaction with
* the {@link #repository}.
*/
public SnapshotIdentifier getDefaultSnapshot() {
return defaultSnapshot;
}
/**
* Sets the "current default" snapshot for this interpreter. This is the one
* that will be used for {@link #repository} queries in case no snapshot is
* explicitly provided by the application or the {@link RepositoryObject
* objects} participating in the repository interaction.
*/
public void setDefaultSnapshot(SnapshotIdentifier snapshot) {
defaultSnapshot = snapshot;
}
public Snapshot commit() {
return getTransactionBuffer().commit(getRepository());
}
public void rollback() {
getTransactionBuffer().rollback();
}
/**
* Tells if this interpreter is currently executing or evaluating anything.
*
* @see #running
* @see #enterEvaluate(EObject)
* @see #exitEvaluate(boolean)
*/
public boolean isRunning() {
return !terminated && running;
}
private DebugSession getDebugSession() {
return debugSession;
}
/**
* By setting a non-<tt>null</tt> debug session, the interpreter is put in
* debug mode.
*/
public void setDebugSession(DebugSession debugSession) {
this.debugSession = debugSession;
}
public void terminate() {
terminated = true;
}
public boolean isTerminated() {
return terminated;
}
/**
* Creates a new entity object in this interpreter's
* {@link #getDefaultSnapshot() default snapshot} and tells the
* {@link #linkContainer} that there are no links for this object yet, and
* that the link container does not need to look up any such links in the
* persistence {@link #repository}.
*/
public EntityObject<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage> createEntityObject(ClassUsage type) {
assert !getModelAdapter().isValueType(getModelAdapter().getClazz(type));
EntityObject<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage> result =
new EntityObject<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage>(
type, getUpdatingTag(), getModelAdapter(), this);
getLinkContainer().newEntity(result);
return result;
}
protected SnapshotIdentifier getUpdatingTag() {
SnapshotIdentifier result = getTransactionBuffer().getUpdatingTag();
if (result == null) {
result = getDefaultSnapshot();
getTransactionBuffer().setUpdatingTag(result);
}
return result;
}
/**
* Assembles a value object in this interpreter's
* {@link #getDefaultSnapshot() default snapshot} and tells the
* {@link #linkContainer} equality-relevant links for this object.
*/
public AbstractValueObject<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage> createValueObject(
ClassUsage type, Map<LinkEndMetaObject, Collection<ClassTypedObject<LinkEndMetaObject, TypeUsage, ClassUsage>>> propertyValues) {
assert getModelAdapter().isValueType(getModelAdapter().getClazz(type));
AbstractValueObject<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage> result =
createValueObjectWithoutEqualityRelevantLinks(type, propertyValues);
loadEqualityRelevantLinksOfValue(result);
return result;
}
/**
* Assembles a value object in this interpreter's {@link #getDefaultSnapshot() default snapshot} but does not load the
* equality-relevant links for this object yet.
*/
protected abstract AbstractValueObject<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage> createValueObjectWithoutEqualityRelevantLinks(
ClassUsage type, Map<LinkEndMetaObject, Collection<ClassTypedObject<LinkEndMetaObject, TypeUsage, ClassUsage>>> propertyValues);
public static <LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage extends TypeUsage, T extends RunletObject<LinkEndMetaObject, TypeUsage, ClassUsage>>
RunletObject<LinkEndMetaObject, TypeUsage, ClassUsage> getRunletObjectFromIterable(
TypeUsage typeDefinition, final Iterable<T> iterable,
ModelAdapter<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage> modelAdapter, boolean ordered, boolean unique) {
return new MultiValuedObject<LinkEndMetaObject, TypeUsage, ClassUsage>(
typeDefinition, new Iterable<RunletObject<LinkEndMetaObject, TypeUsage, ClassUsage>>() {
@Override
public Iterator<RunletObject<LinkEndMetaObject, TypeUsage, ClassUsage>> iterator() {
final Iterator<T> innerIterator = iterable.iterator();
return new Iterator<RunletObject<LinkEndMetaObject, TypeUsage, ClassUsage>>() {
@Override
public boolean hasNext() {
return innerIterator.hasNext();
}
@Override
public RunletObject<LinkEndMetaObject, TypeUsage, ClassUsage> next() {
return innerIterator.next();
}
@Override
public void remove() {
innerIterator.remove();
}
};
}
}, ordered, unique);
}
public void loadEqualityRelevantLinksOfValue(AbstractValueObject<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage> valueObject) {
getLinkContainer().loadEqualityRelevantLinksOfValue(valueObject);
}
/**
* Spawns a new interpreter for parallel execution. This interpreter re-uses
* the registries for subinterpreters for expressions, statements etc., the
* {@link #resourceSet} as well as the shared state consisting of the
* {@link #linkContainer}. The spawned interpreter has a new
* {@link #callstack call stack} that is initialized with this interpreter's
* top stack frame.
* <p>
*
* This means that a spawned interpreter may not have all of the first stack
* frame's scope parent frames on its own stack. If this first frame happens
* to have a scope parent, this is referenced by a regular Java reference.
* <p>
*/
public abstract InterpreterType spawn();
}