blob: 1f0c5374313e7d93766a1f604483c70723fc2c3f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010, 2018 Willink Transformations and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v20.html
*
* Contributors:
* E.D.Willink - Initial API and implementation
*******************************************************************************/
package org.eclipse.ocl.pivot.internal.evaluation;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.ocl.pivot.BooleanLiteralExp;
import org.eclipse.ocl.pivot.CollectionItem;
import org.eclipse.ocl.pivot.CollectionLiteralExp;
import org.eclipse.ocl.pivot.CollectionLiteralPart;
import org.eclipse.ocl.pivot.CollectionRange;
import org.eclipse.ocl.pivot.CollectionType;
import org.eclipse.ocl.pivot.CompleteInheritance;
import org.eclipse.ocl.pivot.Element;
import org.eclipse.ocl.pivot.EnumLiteralExp;
import org.eclipse.ocl.pivot.EnumerationLiteral;
import org.eclipse.ocl.pivot.ExpressionInOCL;
import org.eclipse.ocl.pivot.IfExp;
import org.eclipse.ocl.pivot.IntegerLiteralExp;
import org.eclipse.ocl.pivot.InvalidLiteralExp;
import org.eclipse.ocl.pivot.IterateExp;
import org.eclipse.ocl.pivot.Iteration;
import org.eclipse.ocl.pivot.IteratorExp;
import org.eclipse.ocl.pivot.LetExp;
import org.eclipse.ocl.pivot.MapLiteralExp;
import org.eclipse.ocl.pivot.MapLiteralPart;
import org.eclipse.ocl.pivot.MapType;
import org.eclipse.ocl.pivot.MessageExp;
import org.eclipse.ocl.pivot.NullLiteralExp;
import org.eclipse.ocl.pivot.OCLExpression;
import org.eclipse.ocl.pivot.Operation;
import org.eclipse.ocl.pivot.OperationCallExp;
import org.eclipse.ocl.pivot.OppositePropertyCallExp;
import org.eclipse.ocl.pivot.Parameter;
import org.eclipse.ocl.pivot.Property;
import org.eclipse.ocl.pivot.PropertyCallExp;
import org.eclipse.ocl.pivot.RealLiteralExp;
import org.eclipse.ocl.pivot.ShadowExp;
import org.eclipse.ocl.pivot.StateExp;
import org.eclipse.ocl.pivot.StringLiteralExp;
import org.eclipse.ocl.pivot.TupleLiteralExp;
import org.eclipse.ocl.pivot.TupleLiteralPart;
import org.eclipse.ocl.pivot.TupleType;
import org.eclipse.ocl.pivot.Type;
import org.eclipse.ocl.pivot.TypeExp;
import org.eclipse.ocl.pivot.TypedElement;
import org.eclipse.ocl.pivot.UnlimitedNaturalLiteralExp;
import org.eclipse.ocl.pivot.UnspecifiedValueExp;
import org.eclipse.ocl.pivot.Variable;
import org.eclipse.ocl.pivot.VariableDeclaration;
import org.eclipse.ocl.pivot.VariableExp;
import org.eclipse.ocl.pivot.evaluation.EvaluationEnvironment;
import org.eclipse.ocl.pivot.evaluation.EvaluationHaltedException;
import org.eclipse.ocl.pivot.evaluation.EvaluationVisitor;
import org.eclipse.ocl.pivot.evaluation.IterationManager;
import org.eclipse.ocl.pivot.ids.CollectionTypeId;
import org.eclipse.ocl.pivot.ids.TuplePartId;
import org.eclipse.ocl.pivot.internal.messages.PivotMessagesInternal;
import org.eclipse.ocl.pivot.internal.utilities.PivotUtilInternal;
import org.eclipse.ocl.pivot.labels.ILabelGenerator;
import org.eclipse.ocl.pivot.library.EvaluatorMultipleIterationManager;
import org.eclipse.ocl.pivot.library.EvaluatorMultipleMapIterationManager;
import org.eclipse.ocl.pivot.library.EvaluatorSingleIterationManager;
import org.eclipse.ocl.pivot.library.EvaluatorSingleMapIterationManager;
import org.eclipse.ocl.pivot.library.LibraryBinaryOperation;
import org.eclipse.ocl.pivot.library.LibraryFeature;
import org.eclipse.ocl.pivot.library.LibraryIteration;
import org.eclipse.ocl.pivot.library.LibraryOperation;
import org.eclipse.ocl.pivot.library.LibraryOperation.LibraryOperationExtension;
import org.eclipse.ocl.pivot.messages.PivotMessages;
import org.eclipse.ocl.pivot.util.Visitable;
import org.eclipse.ocl.pivot.utilities.ClassUtil;
import org.eclipse.ocl.pivot.utilities.PivotUtil;
import org.eclipse.ocl.pivot.utilities.StringUtil;
import org.eclipse.ocl.pivot.utilities.ValueUtil;
import org.eclipse.ocl.pivot.values.CollectionValue;
import org.eclipse.ocl.pivot.values.IntegerRange;
import org.eclipse.ocl.pivot.values.IntegerValue;
import org.eclipse.ocl.pivot.values.InvalidValueException;
import org.eclipse.ocl.pivot.values.IterableValue;
import org.eclipse.ocl.pivot.values.MapValue;
import org.eclipse.ocl.pivot.values.NullValue;
import org.eclipse.ocl.pivot.values.Unlimited;
import org.eclipse.ocl.pivot.values.UnlimitedNaturalValue;
/**
* An evaluation visitor implementation for OCL expressions.
* @since 1.1
*/
public class BasicEvaluationVisitor extends AbstractEvaluationVisitor
{
public static boolean isSimpleRange(@NonNull CollectionLiteralExp cl) {
List<CollectionLiteralPart> partsList = cl.getOwnedParts();
int size = partsList.size();
if (size == 1) {
CollectionLiteralPart part = partsList.get(0);
return part instanceof CollectionRange;
}
return false;
}
/**
* Constructor
*
* @param evalEnv
* an evaluation environment (map of variable names to values)
*/
public BasicEvaluationVisitor(@NonNull ExecutorInternal executor) {
super(executor);
}
/** @deprecated Evaluator no longer nests */
@Deprecated
@Override
public @NonNull EvaluationVisitor createNestedEvaluator() {
return this;
}
/** @deprecated Evaluator no longer nest and so no longer dispose */
@Deprecated
@Override
public void dispose() {}
@Override
public @Nullable Object evaluate(@NonNull OCLExpression body) {
Object value = ((Element) body).accept(undecoratedVisitor);
assert ValueUtil.isBoxed(value); // Make sure Integer/Real are boxed, invalid is an exception, null is null
return value;
}
/** @deprecated No longer used */
@Deprecated
public @NonNull LibraryFeature lookupImplementation(org.eclipse.ocl.pivot.@NonNull Class dynamicType, @NonNull Operation staticOperation) {
CompleteInheritance inheritance = environmentFactory.getMetamodelManager().getInheritance(dynamicType);
return inheritance.getPivotClass().lookupImplementation(standardLibrary, staticOperation);
}
@Override
public Object safeVisit(@Nullable Visitable v) {
if (v == null) {
throw new InvalidValueException("null expression");
}
try {
Object result = v.accept(undecoratedVisitor);
assert ValueUtil.isBoxed(result); // Make sure Integer/Real are boxed, invalid is an exception, null is null
return result;
} catch (InvalidValueException e) {
throw e;
} catch (Exception e) {
throw new InvalidValueException(e, "Evaluation Failure");
} catch (AssertionError e) {
throw new InvalidValueException(e, "Evaluation Failure");
}
}
/**
* Callback for an AssociationClassCallExp visit. Evaluates the source of the
* expression and then reflectively gets the value of the reference on the
* result. For example, in "self.foo", "self" is the source and would be
* evaluated first, then the value of the reference "foo" would be derived
* on that object.
*
@Override
public Object visitAssociationClassCallExp(@NonNull AssociationClassCallExp ae) {
Object context = ae.getOwnedSource().accept(undecoratedVisitor);
// if ((context == null) || ValuesUtil.isUndefined(context)) {
// return evaluationEnvironment.throwInvalidEvaluation("Undefined context for AssociationClassCall", ae);
// }
// context = ValuesUtil.asValidValue(context);
// evaluate attribute on source value
return evaluationEnvironment.navigateAssociationClass(
ae.getReferredAssociationClass(),
ae.getNavigationSource(),
context);
} */
/**
* Callback for a BooleanLiteralExp visit.
*
* @return the value of the boolean literal as a java.lang.Boolean.
*/
@Override
public Object visitBooleanLiteralExp(@NonNull BooleanLiteralExp booleanLiteralExp) {
boolean value = booleanLiteralExp.isBooleanSymbol();
return value;
}
@Override
public Object visitCollectionItem(@NonNull CollectionItem item) {
throw new UnsupportedOperationException("evaluation of CollectionItem"); //$NON-NLS-1$
}
/**
* Callback for a CollectionLiteralExp visit.
*/
@Override
public Object visitCollectionLiteralExp(@NonNull CollectionLiteralExp cl) {
// construct the appropriate collection from the parts
// based on the collection kind.
List<CollectionLiteralPart> parts = cl.getOwnedParts();
CollectionType type = (CollectionType) cl.getType();
boolean isOrdered = type.isOrdered();
if (isOrdered && isSimpleRange(cl)) {
// literal is of the form: Sequence{first..last}.
// construct a list with a lazy iterator for it.
CollectionRange collRange = (CollectionRange) parts.get(0);
OCLExpression first = collRange.getOwnedFirst();
OCLExpression last = collRange.getOwnedLast();
Object firstVal = first.accept(undecoratedVisitor);
Object lastVal = last.accept(undecoratedVisitor);
IntegerValue firstInteger = ValueUtil.asIntegerValue(firstVal);
IntegerValue lastInteger = ValueUtil.asIntegerValue(lastVal);
// construct a lazy integer list for the range
CollectionTypeId typeId = type.getTypeId();
IntegerRange range = ValueUtil.createRange(firstInteger, lastInteger);
if (type.isUnique()) {
return ValueUtil.createOrderedSetRange(typeId, range);
}
else {
return ValueUtil.createSequenceRange(typeId, range);
}
} else
{
List<Object> orderedResults = new ArrayList<Object>();
Set<Object> uniqueResults = type.isUnique() ? new HashSet<Object>() : null;
// not a sequence or not a simple range
for (CollectionLiteralPart part : parts) {
if (part instanceof CollectionItem) {
// CollectionItem part
CollectionItem item = (CollectionItem) part;
OCLExpression itemExp = item.getOwnedItem();
Object itemVal = itemExp.accept(undecoratedVisitor);
// Object itemValue = ValuesUtil.asValidValue(itemVal);
if ((uniqueResults == null) || uniqueResults.add(itemVal)) {
orderedResults.add(itemVal);
}
} else {
// Collection range
CollectionRange range = (CollectionRange) part;
OCLExpression first = range.getOwnedFirst();
OCLExpression last = range.getOwnedLast();
Object firstVal = first.accept(undecoratedVisitor);
Object lastVal = last.accept(undecoratedVisitor);
IntegerValue firstInteger = ValueUtil.asIntegerValue(firstVal);
IntegerValue lastInteger = ValueUtil.asIntegerValue(lastVal);
Integer firstInt = firstInteger.asInteger();
Integer lastInt = lastInteger.asInteger();
// TODO: enhance IntegerRangeList to support multiple ranges
// add values between first and last inclusive
if (firstInt <= lastInt) {
for (int i = firstInt; true; i++) {
IntegerValue integerValue = ValueUtil.integerValueOf(i);
if ((uniqueResults == null) || uniqueResults.add(integerValue)) {
orderedResults.add(integerValue);
}
if (i >= lastInt) {
break;
}
}
}
} // end of collection range
} // end of parts iterator
return idResolver.createCollectionOfAll(type.isOrdered(), type.isUnique(), ClassUtil.nonNullModel(type.getElementType()).getTypeId(), orderedResults);
} // end of not-simple range case
} // end of Set, OrderedSet, Bag Literals
@Override
public Object visitCollectionRange(@NonNull CollectionRange range) {
throw new UnsupportedOperationException("evaluation of CollectionRange"); //$NON-NLS-1$
}
/**
* Callback for an EnumLiteralExp visit. Get the referred enum literal and
* return it as an Integer.
*
* @param el
* the enumeration literal expresion
* @return the enumeration literal as an Integer
*/
@Override
public Object visitEnumLiteralExp(@NonNull EnumLiteralExp el) {
EnumerationLiteral enumLiteral = el.getReferredLiteral();
assert enumLiteral != null;
return enumLiteral.getEnumerationLiteralId();
}
// private static int depth = 0;
@Override
public Object visitExpressionInOCL(@NonNull ExpressionInOCL expression) {
if (isCanceled()) {
throw new EvaluationHaltedException("Canceled");
}
// Object object = getEvaluationEnvironment().getValueOf(expression.getContextVariable());
// System.out.println(++depth + " " + expression.getContextVariable() + " = " + object + "\n\t" + expression);
// try {
Object result = safeVisit(expression.getOwnedBody());
// System.out.println(depth + "\t=> " + result);
return result;
// }
// catch (RuntimeException e) {
// System.out.println(depth + "\t=> " + e);
// throw e;
// }
// finally {
// --depth;
// }
}
/**
* Callback for an IfExp visit.
*/
@Override
public Object visitIfExp(@NonNull IfExp ifExp) {
OCLExpression condition = ifExp.getOwnedCondition();
Object acceptedValue = condition.accept(undecoratedVisitor);
Object evaluatedCondition = ValueUtil.asBoolean(acceptedValue);
OCLExpression expression = null;
if (evaluatedCondition == ValueUtil.TRUE_VALUE) {
expression = ifExp.getOwnedThen();
}
else {
expression = ifExp.getOwnedElse();
}
return expression.accept(undecoratedVisitor);
}
/**
* Callback for an IntegerLiteralExp visit.
*
* @return the value of the integer literal as a java.lang.Integer.
*/
@Override
public Object visitIntegerLiteralExp(@NonNull IntegerLiteralExp integerLiteralExp) {
Number integerSymbol = integerLiteralExp.getIntegerSymbol();
return integerSymbol != null ? ValueUtil.integerValueOf(integerSymbol) : null;
}
@Override
public Object visitInvalidLiteralExp(@NonNull InvalidLiteralExp invalidLiteralExp) {
throw ValueUtil.INVALID_VALUE;
}
/**
* Callback for an IterateExp visit.
*/
@Override
public Object visitIterateExp(@NonNull IterateExp iterateExp) {
if (isCanceled()) {
throw new EvaluationHaltedException("Canceled");
}
Iteration staticIteration = ClassUtil.nonNullModel(iterateExp.getReferredIteration());
OCLExpression source = iterateExp.getOwnedSource();
Object acceptedValue = source.accept(undecoratedVisitor);
IterableValue sourceValue = ValueUtil.asIterableValue(acceptedValue);
if (iterateExp.isIsSafe()) {
if (sourceValue instanceof MapValue) {
throw new InvalidValueException(PivotMessages.MapValueForbidden);
}
sourceValue = sourceValue.excluding(null);
}
org.eclipse.ocl.pivot.Class dynamicSourceType = idResolver.getClass(sourceValue.getTypeId(), null);
LibraryIteration implementation = (LibraryIteration) dynamicSourceType.lookupImplementation(standardLibrary, staticIteration);
/* Operation dynamicIteration = metamodelManager.getDynamicOperation((org.eclipse.ocl.pivot.Type) dynamicSourceType, staticIteration);
if (dynamicIteration == null) {
dynamicIteration = staticIteration;
}
LibraryIteration implementation1;
try {
implementation = (LibraryIteration) metamodelManager.getImplementation(dynamicIteration);
} catch (Exception e) {
String implementationClass = dynamicIteration.getImplementationClass();
if (implementationClass != null) {
return evaluationEnvironment.throwInvalidEvaluation(e, iterateExp, null, EvaluatorMessages.ImplementationClassLoadFailure, implementationClass);
}
else {
return evaluationEnvironment.throwInvalidEvaluation(e, iterateExp, null, "Failed to load implementation for '" + dynamicIteration + "'");
}
} */
Object result = null;
try {
Variable accumulator = iterateExp.getOwnedResult();
Object initValue = accumulator.getOwnedInit().accept(undecoratedVisitor);
// if ((initValue == null) || ValuesUtil.isUndefined(initValue)) {
// return evaluationEnvironment.throwInvalidEvaluation(null, iterateExp, initValue, EvaluatorMessages.UndefinedInitializer);
// }
// initValue = ValuesUtil.asValidValue(initValue);
IterationManager iterationManager;
VariableDeclaration accumulatorVariable = accumulator;
OCLExpression body = ClassUtil.nonNullModel(iterateExp.getOwnedBody());
List<@NonNull Variable> iterators = PivotUtilInternal.getOwnedIteratorsList(iterateExp);
int iSize = iterators.size();
if (sourceValue instanceof MapValue) {
List<Variable> coIterators = iterateExp.getOwnedCoIterators();
if (iSize == 1) {
VariableDeclaration keyIterator = ClassUtil.nonNullModel(iterators.get(0));
VariableDeclaration valueIterator = coIterators.size() >= 1 ? coIterators.get(0) : null;
iterationManager = new EvaluatorSingleMapIterationManager(context, iterateExp, body, (MapValue)sourceValue, accumulatorVariable, initValue, keyIterator, valueIterator);
}
else {
@NonNull VariableDeclaration[] keyIterators = new @NonNull VariableDeclaration[iSize];
@Nullable VariableDeclaration[] valueIterators = new @Nullable VariableDeclaration[iSize];
for (int i = 0; i < iSize; i++) {
keyIterators[i] = iterators.get(i);
valueIterators[i] = i < coIterators.size() ? coIterators.get(i) : null;
}
iterationManager = new EvaluatorMultipleMapIterationManager(context, iterateExp, body, (MapValue)sourceValue, accumulatorVariable, initValue, keyIterators, valueIterators);
}
}
else {
if (iSize == 1) {
VariableDeclaration firstIterator = ClassUtil.nonNullModel(iterators.get(0));
iterationManager = new EvaluatorSingleIterationManager(context, iterateExp, body, (CollectionValue)sourceValue, accumulatorVariable, initValue, firstIterator);
}
else {
VariableDeclaration[] variables = new VariableDeclaration[iSize];
for (int i = 0; i < iSize; i++) {
variables[i] = iterators.get(i);
}
iterationManager = new EvaluatorMultipleIterationManager(context, iterateExp, body, (CollectionValue)sourceValue, accumulatorVariable, initValue, variables);
}
}
result = implementation.evaluateIteration(iterationManager);
}
catch (InvalidValueException e) {
throw e;
}
catch (Exception e) {
// This is a backstop. Library iterations should catch their own exceptions
// and produce a better reason as a result.
throw new InvalidValueException(e, StringUtil.bind(PivotMessagesInternal.FailedToEvaluate_ERROR_, staticIteration, sourceValue, iterateExp)); // FIXME dymamicIteration throughout
}
catch (AssertionError e) {
// This is a backstop. Library iterations should catch their own exceptions
// and produce a better reason as a result.
throw new InvalidValueException(e, StringUtil.bind(PivotMessagesInternal.FailedToEvaluate_ERROR_, staticIteration, sourceValue, iterateExp)); // FIXME dymamicIteration throughout
}
return result;
}
/**
* Callback for an IteratorExp visit.
*/
@Override
public Object visitIteratorExp(@NonNull IteratorExp iteratorExp) {
if (isCanceled()) {
throw new EvaluationHaltedException("Canceled");
}
Iteration staticIteration = ClassUtil.nonNullModel(iteratorExp.getReferredIteration());
IterableValue sourceValue;
// try {
OCLExpression source = iteratorExp.getOwnedSource();
Object sourceVal = source.accept(undecoratedVisitor);
// if (sourceVal == null) {
// return evaluationEnvironment.throwInvalidEvaluation("null iterator source");
// }
sourceValue = ValueUtil.asIterableValue(sourceVal);
if (iteratorExp.isIsSafe()) {
if (sourceValue instanceof MapValue) {
throw new InvalidValueException(PivotMessages.MapValueForbidden);
}
sourceValue = sourceValue.excluding(null);
}
// } catch (InvalidValueException e) {
// return evaluationEnvironment.throwInvalidEvaluation(e);
// }
org.eclipse.ocl.pivot.Class dynamicSourceType = idResolver.getClass(sourceValue.getTypeId(), null);
LibraryIteration.LibraryIterationExtension implementation = (LibraryIteration.LibraryIterationExtension) dynamicSourceType.lookupImplementation(standardLibrary, staticIteration);
/* Operation dynamicIteration = metamodelManager.getDynamicOperation((org.eclipse.ocl.pivot.Type) dynamicSourceType, staticIteration);
if (dynamicIteration == null) {
dynamicIteration = staticIteration;
}
LibraryIteration implementation;
try {
implementation = (LibraryIteration) metamodelManager.getImplementation(dynamicIteration);
} catch (Exception e) {
String implementationClass = dynamicIteration.getImplementationClass();
if (implementationClass != null) {
return evaluationEnvironment.throwInvalidEvaluation(e, iteratorExp, null, EvaluatorMessages.ImplementationClassLoadFailure, implementationClass);
}
else {
return evaluationEnvironment.throwInvalidEvaluation(e, iteratorExp, null, "Failed to load implementation for '" + dynamicIteration + "'");
}
} */
Object result = null;
try {
IterationManager iterationManager;
OCLExpression body = PivotUtil.getOwnedBody(iteratorExp);
Type iterationType = PivotUtil.getBehavioralType(PivotUtil.getType(iteratorExp));
Type bodyType = PivotUtil.getBehavioralType(PivotUtil.getType(body));
Object accumulatorValue = implementation.createAccumulatorValue(context, iterationType.getTypeId(), bodyType.getTypeId());
List<@NonNull Variable> iterators = PivotUtilInternal.getOwnedIteratorsList(iteratorExp);
int iSize = iterators.size();
if (sourceValue instanceof MapValue) {
List<Variable> coIterators = iteratorExp.getOwnedCoIterators();
if (iSize == 1) {
VariableDeclaration keyIterator = ClassUtil.nonNullModel(iterators.get(0));
VariableDeclaration valueIterator = coIterators.size() >= 1 ? coIterators.get(0) : null;
iterationManager = new EvaluatorSingleMapIterationManager(context, iteratorExp, body, (MapValue)sourceValue, null, accumulatorValue, keyIterator, valueIterator);
}
else {
@NonNull VariableDeclaration[] keyIterators = new @NonNull VariableDeclaration[iSize];
@Nullable VariableDeclaration[] valueIterators = new @Nullable VariableDeclaration[iSize];
for (int i = 0; i < iSize; i++) {
keyIterators[i] = iterators.get(i);
valueIterators[i] = i < coIterators.size() ? coIterators.get(i) : null;
}
iterationManager = new EvaluatorMultipleMapIterationManager(context, iteratorExp, body, (MapValue)sourceValue, null, accumulatorValue, keyIterators, valueIterators);
}
}
else {
if (iSize == 1) {
VariableDeclaration firstIterator = ClassUtil.nonNullModel(iterators.get(0));
iterationManager = new EvaluatorSingleIterationManager(context, iteratorExp, body, (CollectionValue)sourceValue, null, accumulatorValue, firstIterator);
}
else {
VariableDeclaration[] variables = new VariableDeclaration[iSize];
for (int i = 0; i < iSize; i++) {
variables[i] = iterators.get(i);
}
iterationManager = new EvaluatorMultipleIterationManager(context, iteratorExp, body, (CollectionValue)sourceValue, null, accumulatorValue, variables);
}
}
result = implementation.evaluateIteration(iterationManager);
}
catch (InvalidValueException e) {
throw e;
}
catch (Exception e) {
// This is a backstop. Library iterations should catch their own exceptions
// and produce a better reason as a result.
throw new InvalidValueException(e, PivotMessagesInternal.FailedToEvaluate_ERROR_, staticIteration, sourceValue, iteratorExp);
}
catch (AssertionError e) {
// This is a backstop. Library iterations should catch their own exceptions
// and produce a better reason as a result.
throw new InvalidValueException(e, PivotMessagesInternal.FailedToEvaluate_ERROR_, staticIteration, sourceValue, iteratorExp);
}
return result;
}
/**
* Callback for LetExp visit.
*/
@Override
public Object visitLetExp(@NonNull LetExp letExp) {
OCLExpression expression = letExp.getOwnedIn(); // Never null when valid
Variable variable = letExp.getOwnedVariable(); // Never null when valid
assert variable != null;
Object value;
try {
value = variable.accept(undecoratedVisitor);
}
catch (EvaluationHaltedException e) {
throw e;
}
catch (InvalidValueException e) {
value = e;
}
// value = ValuesUtil.asValue(value);
assert expression != null;
EvaluationEnvironment nestedEvaluationEnvironment = context.pushEvaluationEnvironment(expression, (TypedElement)letExp);
nestedEvaluationEnvironment.add(variable, value);
try {
return expression.accept(undecoratedVisitor);
}
finally {
context.popEvaluationEnvironment();
// nestedVisitor.dispose();
}
}
/**
* Callback for a CollectionLiteralExp visit.
*/
@Override
public Object visitMapLiteralExp(@NonNull MapLiteralExp mapLiteralExp) {
List<MapLiteralPart> parts = mapLiteralExp.getOwnedParts();
MapType type = (MapType) mapLiteralExp.getType();
Map<Object, Object> mapEntries = new HashMap<Object, Object>();
for (MapLiteralPart part : parts) {
OCLExpression key = part.getOwnedKey();
OCLExpression value = part.getOwnedValue();
Object keyVal = key.accept(undecoratedVisitor);
Object valueVal = value.accept(undecoratedVisitor);
mapEntries.put(keyVal, valueVal);
}
return idResolver.createMapOfAll(ClassUtil.nonNullModel(type.getKeyType()).getTypeId(), ClassUtil.nonNullModel(type.getValueType()).getTypeId(), mapEntries);
}
@Override
public Object visitMessageExp(@NonNull MessageExp m) {
throw new UnsupportedOperationException("evaluation of MessageExp"); //$NON-NLS-1$
/* T targetResult = safeVisit(messageExp.getTarget());
List<T> argumentResults;
List<OCLExpression> arguments = messageExp.getArgument();
if (arguments.isEmpty()) {
argumentResults = Collections.emptyList();
} else {
argumentResults = new java.util.ArrayList<T>(arguments.size());
for (OCLExpression qual : arguments) {
argumentResults.add(safeVisit(qual));
}
}
return handleMessageExp(messageExp, targetResult, argumentResults);
*/ }
@Override
public Object visitNullLiteralExp(@NonNull NullLiteralExp nullLiteralExp) {
return null;
}
/**
* Callback for an OperationCallExp visit.
*/
@Override
public Object visitOperationCallExp(@NonNull OperationCallExp operationCallExp) {
if (isCanceled()) {
throw new EvaluationHaltedException("Canceled");
}
Operation apparentOperation = operationCallExp.getReferredOperation();
assert apparentOperation != null;
boolean isValidating = apparentOperation.isIsValidating();
//
// Resolve source value catching invalid values for validating operations.
//
OCLExpression source = operationCallExp.getOwnedSource();
Object sourceValue;
if (source == null) { // Static functions may have null source
sourceValue = null;
}
else if (!isValidating) {
sourceValue = source.accept(undecoratedVisitor);
}
else {
try {
sourceValue = source.accept(undecoratedVisitor);
assert ValueUtil.isBoxed(sourceValue); // Make sure Integer/Real are boxed, invalid is an exception, null is null
}
catch (EvaluationHaltedException e) {
throw e;
}
catch (InvalidValueException e) {
sourceValue = e; // FIXME ?? propagate part of environment
}
}
//
// Safe navigation of null source return null.
//
if (operationCallExp.isIsSafe()) {
if (sourceValue == null) {
return null;
}
if (sourceValue instanceof MapValue) {
throw new InvalidValueException(PivotMessages.MapValueForbidden);
}
if (sourceValue instanceof CollectionValue) {
sourceValue = ((CollectionValue)sourceValue).excluding(null);
}
}
Operation actualOperation;
if (apparentOperation.isIsStatic()) {
actualOperation = apparentOperation;
}
else {
assert source != null;
org.eclipse.ocl.pivot.Class actualSourceType = idResolver.getStaticTypeOfValue(source.getType(), sourceValue);
List<Parameter> ownedParameters = apparentOperation.getOwnedParameters();
if (ownedParameters.size() == 1) {
Parameter onlyParameter = ownedParameters.get(0);
Type onlyType = onlyParameter.getType();
if (onlyType == standardLibrary.getOclSelfType()) {
List<@NonNull OCLExpression> arguments = ClassUtil.nullFree(operationCallExp.getOwnedArguments());
Object onlyArgument = arguments.get(0).accept(undecoratedVisitor);
org.eclipse.ocl.pivot.Class actualArgType = idResolver.getStaticTypeOfValue(onlyType, onlyArgument);
actualSourceType = (org.eclipse.ocl.pivot.Class)actualSourceType.getCommonType(idResolver, actualArgType);
// FIXME direct evaluate using second argument
actualOperation = actualSourceType.lookupActualOperation(standardLibrary, apparentOperation);
LibraryBinaryOperation.LibraryBinaryOperationExtension implementation = (LibraryBinaryOperation.LibraryBinaryOperationExtension) environmentFactory.getMetamodelManager().getImplementation(actualOperation);
try {
Object result = implementation.evaluate(context, operationCallExp.getTypeId(), sourceValue, onlyArgument);
assert !(result instanceof NullValue);
return result;
}
catch (InvalidValueException e) {
throw e;
}
catch (Exception e) {
// This is a backstop. Library operations should catch their own exceptions
// and produce a better reason as a result.
throw new InvalidValueException(e, PivotMessagesInternal.FailedToEvaluate_ERROR_, apparentOperation, ILabelGenerator.Registry.INSTANCE.labelFor(sourceValue), operationCallExp);
}
catch (AssertionError e) {
// This is a backstop. Library operations should catch their own exceptions
// and produce a better reason as a result.
throw new InvalidValueException(e, PivotMessagesInternal.FailedToEvaluate_ERROR_, apparentOperation, ILabelGenerator.Registry.INSTANCE.labelFor(sourceValue), operationCallExp);
}
}
}
actualOperation = actualSourceType.lookupActualOperation(standardLibrary, apparentOperation);
}
LibraryOperation.LibraryOperationExtension implementation = (LibraryOperationExtension) environmentFactory.getMetamodelManager().getImplementation(actualOperation);
try {
Object result = implementation.dispatch(context, operationCallExp, sourceValue);
assert !(result instanceof NullValue);
return result;
}
catch (InvalidValueException e) {
throw e;
}
catch (Exception e) {
// This is a backstop. Library operations should catch their own exceptions
// and produce a better reason as a result.
throw new InvalidValueException(e, PivotMessagesInternal.FailedToEvaluate_ERROR_, apparentOperation, ILabelGenerator.Registry.INSTANCE.labelFor(sourceValue), operationCallExp);
}
catch (AssertionError e) {
// This is a backstop. Library operations should catch their own exceptions
// and produce a better reason as a result.
throw new InvalidValueException(e, PivotMessagesInternal.FailedToEvaluate_ERROR_, apparentOperation, ILabelGenerator.Registry.INSTANCE.labelFor(sourceValue), operationCallExp);
}
}
/**
* Callback for an OppositePropertyCallExp visit.
*/
@Override
public Object visitOppositePropertyCallExp(@NonNull OppositePropertyCallExp oppositePropertyCallExp) {
Property oppositeReferredProperty = oppositePropertyCallExp.getReferredProperty();
Property referredProperty = oppositeReferredProperty.getOpposite();
// assert referredProperty != null;
// return evaluatePropertyCallExp(oppositePropertyCallExp, referredProperty);
if (referredProperty != null) {
OCLExpression source = oppositePropertyCallExp.getOwnedSource();
if (source != null) {
Object sourceValue = source.accept(undecoratedVisitor);
if (sourceValue != null) {
return context.internalExecuteNavigationCallExp(oppositePropertyCallExp, referredProperty, sourceValue);
}
}
}
return null;
}
/**
* Callback for a PropertyCallExp visit.
*/
@Override
public Object visitPropertyCallExp(@NonNull PropertyCallExp propertyCallExp) {
Property referredProperty = propertyCallExp.getReferredProperty();
// assert referredProperty != null;
// return evaluatePropertyCallExp(propertyCallExp, referredProperty);
if (referredProperty != null) {
OCLExpression source = propertyCallExp.getOwnedSource();
if (source != null) {
Object sourceValue = source.accept(undecoratedVisitor);
return context.internalExecuteNavigationCallExp(propertyCallExp, referredProperty, sourceValue);
}
}
return null;
}
/**
* Callback for a RealLiteralExp visit.
*
* @return the value of the real literal as a java.lang.Double.
*/
@Override
public Object visitRealLiteralExp(@NonNull RealLiteralExp realLiteralExp) {
Number realSymbol = realLiteralExp.getRealSymbol();
return realSymbol != null ? ValueUtil.realValueOf(realSymbol) : null;
}
@Override
public Object visitShadowExp(@NonNull ShadowExp ce) {
return context.internalExecuteShadowExp(ce);
}
@Override
public Object visitStateExp(@NonNull StateExp s) {
return s.getReferredState();
}
/**
* Callback for a StringLiteralExp visit.
*
* @return the value of the string literal as a java.lang.String.
*/
@Override
public Object visitStringLiteralExp(@NonNull StringLiteralExp stringLiteralExp) {
String value = stringLiteralExp.getStringSymbol();
if (value == null) {
throw new InvalidValueException("Invalid String Value", stringLiteralExp);
}
return value;
}
/**
* Callback for a TupleLiteralExp visit.
*
* @param tl
* tuple literal expression
* @return String
*/
@Override
public Object visitTupleLiteralExp(@NonNull TupleLiteralExp tl) {
Type type = ClassUtil.nonNullModel(tl.getType());
Map<@NonNull TuplePartId, @Nullable Object> propertyValues = new HashMap<@NonNull TuplePartId, @Nullable Object>();
for (@NonNull TupleLiteralPart part : ClassUtil.nullFree(tl.getOwnedParts())) {
// Set the tuple field with the value of the init expression
propertyValues.put(ClassUtil.nonNull(part.getPartId()), part.accept(undecoratedVisitor));
}
// TupleType tupleType = metamodelManager.getTupleType(type.getName(), propertyValues.keySet());
return ValueUtil.createTupleValue(((TupleType) type).getTupleTypeId(), propertyValues);
}
@Override
public Object visitTupleLiteralPart(@NonNull TupleLiteralPart tp) {
return tp.getOwnedInit().accept(undecoratedVisitor);
}
/**
* Callback for a TypeExp visit.
*/
@Override
public Object visitTypeExp(@NonNull TypeExp t) {
// DomainMetaclass domainMetaclass = (DomainMetaclass)t.getType();
// DomainType instanceType = domainMetaclass.getInstanceType();
// assert instanceType != null;
// return ValuesUtil.createTypeValue(instanceType);
// DomainMetaclass domainMetaclass = (DomainMetaclass)t.getType();
// DomainType instanceType = domainMetaclass.getInstanceType();
// assert instanceType != null;
return t.getReferredType();
}
/**
* Callback for an UnlimitedNaturalLiteralExp visit.
*
* @return the value of the natural literal as a java.lang.Integer.
*/
@Override
public Object visitUnlimitedNaturalLiteralExp(@NonNull UnlimitedNaturalLiteralExp unlimitedNaturalLiteralExp) {
Number unlimitedNaturalSymbol = unlimitedNaturalLiteralExp.getUnlimitedNaturalSymbol();
if (unlimitedNaturalSymbol == null) {
return null;
}
if (unlimitedNaturalSymbol instanceof Unlimited) {
return ValueUtil.UNLIMITED_VALUE;
}
if (unlimitedNaturalSymbol instanceof UnlimitedNaturalValue) {
return unlimitedNaturalSymbol;
}
IntegerValue integerValue = ValueUtil.integerValueOf(unlimitedNaturalSymbol);
if (integerValue.signum() < 0) {
if (integerValue == ValueUtil.integerValueOf(-1)) {
return ValueUtil.UNLIMITED_VALUE;
}
}
return integerValue.asUnlimitedNaturalValue();
}
/**
* Callback for an UnspecifiedValueExp visit.
*/
@Override
public Object visitUnspecifiedValueExp(@NonNull UnspecifiedValueExp uv) {
// TODO: return a "random instance of the type of the expression"
throw new UnsupportedOperationException("evaluation of UnspecifiedValueExp"); //$NON-NLS-1$
}
/**
* Callback for a Variable visit.
*/
@Override
public Object visitVariable(@NonNull Variable variable) {
// return the initial (only) value
OCLExpression initExp = variable.getOwnedInit();
if (initExp == null) {
throw new InvalidValueException("Uninitialized variable", variable);
}
else {
return initExp.accept(undecoratedVisitor);
}
}
/**
* Callback for a VariableExp visit.
*
* @param variableExp
* the variable expression
* @return the value of the variable
*/
@Override
public Object visitVariableExp(@NonNull VariableExp variableExp) {
VariableDeclaration variableDeclaration = variableExp.getReferredVariable();
if (variableDeclaration == null) {
throw new InvalidValueException("Undefined variable", null, null, variableExp);
}
Object value = context.getValueOf(variableDeclaration);
if (value instanceof InvalidValueException) {
throw (InvalidValueException)value;
}
else {
return value;
}
}
@Override
public Object visiting(@NonNull Visitable visitable) {
throw new IllegalArgumentException("Unsupported " + visitable.eClass().getName() + " for " + getClass().getSimpleName());
}
}