| /******************************************************************************* |
| * Copyright (c) 2007 Borland Software Corporation |
| * |
| * 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: |
| * Borland Software Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.m2m.internal.qvt.oml.evaluator; |
| |
| import java.io.PrintWriter; |
| import java.io.StringWriter; |
| import java.text.MessageFormat; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.LinkedHashSet; |
| import java.util.LinkedList; |
| import java.util.List; |
| |
| import org.eclipse.emf.common.util.EList; |
| import org.eclipse.emf.ecore.EAnnotation; |
| import org.eclipse.emf.ecore.EClass; |
| import org.eclipse.emf.ecore.EClassifier; |
| import org.eclipse.emf.ecore.EDataType; |
| import org.eclipse.emf.ecore.EEnumLiteral; |
| import org.eclipse.emf.ecore.EModelElement; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.emf.ecore.EOperation; |
| import org.eclipse.emf.ecore.EPackage; |
| import org.eclipse.emf.ecore.EParameter; |
| import org.eclipse.emf.ecore.EStructuralFeature; |
| import org.eclipse.m2m.internal.qvt.oml.QvtPlugin; |
| import org.eclipse.m2m.internal.qvt.oml.ast.env.IVirtualOperationTable; |
| import org.eclipse.m2m.internal.qvt.oml.ast.env.InternalEvaluationEnv; |
| import org.eclipse.m2m.internal.qvt.oml.ast.env.ModelParameterExtent; |
| import org.eclipse.m2m.internal.qvt.oml.ast.env.QvtEvaluationResult; |
| import org.eclipse.m2m.internal.qvt.oml.ast.env.QvtOperationalEnv; |
| import org.eclipse.m2m.internal.qvt.oml.ast.env.QvtOperationalEnvFactory; |
| import org.eclipse.m2m.internal.qvt.oml.ast.env.QvtOperationalEvaluationEnv; |
| import org.eclipse.m2m.internal.qvt.oml.ast.parser.QvtOperationalParserUtil; |
| import org.eclipse.m2m.internal.qvt.oml.ast.parser.QvtOperationalUtil; |
| import org.eclipse.m2m.internal.qvt.oml.compiler.IntermediateClassFactory; |
| import org.eclipse.m2m.internal.qvt.oml.emf.util.Logger; |
| import org.eclipse.m2m.internal.qvt.oml.evaluator.TransformationInstance.InternalTransformation; |
| import org.eclipse.m2m.internal.qvt.oml.evaluator.iterators.QvtIterationTemplateCollectSelect; |
| import org.eclipse.m2m.internal.qvt.oml.evaluator.iterators.QvtIterationTemplateForExp; |
| import org.eclipse.m2m.internal.qvt.oml.evaluator.iterators.QvtIterationTemplateXCollect; |
| import org.eclipse.m2m.internal.qvt.oml.evaluator.iterators.QvtIterationTemplateXSelect; |
| import org.eclipse.m2m.internal.qvt.oml.expressions.AltExp; |
| import org.eclipse.m2m.internal.qvt.oml.expressions.AssertExp; |
| import org.eclipse.m2m.internal.qvt.oml.expressions.AssignExp; |
| import org.eclipse.m2m.internal.qvt.oml.expressions.BlockExp; |
| import org.eclipse.m2m.internal.qvt.oml.expressions.Class; |
| import org.eclipse.m2m.internal.qvt.oml.expressions.ComputeExp; |
| import org.eclipse.m2m.internal.qvt.oml.expressions.ContextualProperty; |
| import org.eclipse.m2m.internal.qvt.oml.expressions.DictLiteralExp; |
| import org.eclipse.m2m.internal.qvt.oml.expressions.DictLiteralPart; |
| import org.eclipse.m2m.internal.qvt.oml.expressions.DirectionKind; |
| import org.eclipse.m2m.internal.qvt.oml.expressions.EntryOperation; |
| import org.eclipse.m2m.internal.qvt.oml.expressions.ExpressionsPackage; |
| import org.eclipse.m2m.internal.qvt.oml.expressions.ForExp; |
| import org.eclipse.m2m.internal.qvt.oml.expressions.Helper; |
| import org.eclipse.m2m.internal.qvt.oml.expressions.ImperativeIterateExp; |
| import org.eclipse.m2m.internal.qvt.oml.expressions.ImperativeLoopExp; |
| import org.eclipse.m2m.internal.qvt.oml.expressions.ImperativeOperation; |
| import org.eclipse.m2m.internal.qvt.oml.expressions.InstantiationExp; |
| import org.eclipse.m2m.internal.qvt.oml.expressions.Library; |
| import org.eclipse.m2m.internal.qvt.oml.expressions.LogExp; |
| import org.eclipse.m2m.internal.qvt.oml.expressions.MappingBody; |
| import org.eclipse.m2m.internal.qvt.oml.expressions.MappingCallExp; |
| import org.eclipse.m2m.internal.qvt.oml.expressions.MappingOperation; |
| import org.eclipse.m2m.internal.qvt.oml.expressions.MappingParameter; |
| import org.eclipse.m2m.internal.qvt.oml.expressions.ModelParameter; |
| import org.eclipse.m2m.internal.qvt.oml.expressions.ModelType; |
| import org.eclipse.m2m.internal.qvt.oml.expressions.Module; |
| import org.eclipse.m2m.internal.qvt.oml.expressions.ModuleImport; |
| import org.eclipse.m2m.internal.qvt.oml.expressions.ObjectExp; |
| import org.eclipse.m2m.internal.qvt.oml.expressions.OperationBody; |
| import org.eclipse.m2m.internal.qvt.oml.expressions.OperationalTransformation; |
| import org.eclipse.m2m.internal.qvt.oml.expressions.Rename; |
| import org.eclipse.m2m.internal.qvt.oml.expressions.ResolveExp; |
| import org.eclipse.m2m.internal.qvt.oml.expressions.ResolveInExp; |
| import org.eclipse.m2m.internal.qvt.oml.expressions.ReturnExp; |
| import org.eclipse.m2m.internal.qvt.oml.expressions.SeverityKind; |
| import org.eclipse.m2m.internal.qvt.oml.expressions.SwitchExp; |
| import org.eclipse.m2m.internal.qvt.oml.expressions.VarParameter; |
| import org.eclipse.m2m.internal.qvt.oml.expressions.VariableInitExp; |
| import org.eclipse.m2m.internal.qvt.oml.expressions.WhileExp; |
| import org.eclipse.m2m.internal.qvt.oml.expressions.impl.ImperativeOperationImpl; |
| import org.eclipse.m2m.internal.qvt.oml.expressions.impl.OperationBodyImpl; |
| import org.eclipse.m2m.internal.qvt.oml.library.Context; |
| import org.eclipse.m2m.internal.qvt.oml.library.EObjectEStructuralFeaturePair; |
| import org.eclipse.m2m.internal.qvt.oml.library.IContext; |
| import org.eclipse.m2m.internal.qvt.oml.library.LateResolveInTask; |
| import org.eclipse.m2m.internal.qvt.oml.library.LateResolveTask; |
| import org.eclipse.m2m.internal.qvt.oml.library.QvtResolveUtil; |
| import org.eclipse.m2m.internal.qvt.oml.stdlib.CallHandler; |
| import org.eclipse.m2m.internal.qvt.oml.stdlib.DictionaryImpl; |
| import org.eclipse.m2m.internal.qvt.oml.trace.TraceRecord; |
| import org.eclipse.m2m.qvt.oml.util.Dictionary; |
| import org.eclipse.m2m.qvt.oml.util.EvaluationMonitor; |
| import org.eclipse.m2m.qvt.oml.util.Log; |
| import org.eclipse.ocl.Environment; |
| import org.eclipse.ocl.EnvironmentFactory; |
| import org.eclipse.ocl.EvaluationEnvironment; |
| import org.eclipse.ocl.EvaluationVisitor; |
| import org.eclipse.ocl.ParserException; |
| import org.eclipse.ocl.ecore.CallOperationAction; |
| import org.eclipse.ocl.ecore.Constraint; |
| import org.eclipse.ocl.ecore.EcoreFactory; |
| import org.eclipse.ocl.ecore.SendSignalAction; |
| import org.eclipse.ocl.expressions.CollectionKind; |
| import org.eclipse.ocl.expressions.EnumLiteralExp; |
| import org.eclipse.ocl.expressions.IfExp; |
| import org.eclipse.ocl.expressions.OCLExpression; |
| import org.eclipse.ocl.expressions.OperationCallExp; |
| import org.eclipse.ocl.expressions.PropertyCallExp; |
| import org.eclipse.ocl.expressions.Variable; |
| import org.eclipse.ocl.expressions.VariableExp; |
| import org.eclipse.ocl.internal.evaluation.EvaluationVisitorImpl; |
| import org.eclipse.ocl.internal.l10n.OCLMessages; |
| import org.eclipse.ocl.types.BagType; |
| import org.eclipse.ocl.types.CollectionType; |
| import org.eclipse.ocl.types.PrimitiveType; |
| import org.eclipse.ocl.types.SetType; |
| import org.eclipse.ocl.types.TupleType; |
| import org.eclipse.ocl.types.VoidType; |
| import org.eclipse.ocl.util.Bag; |
| import org.eclipse.ocl.util.CollectionUtil; |
| import org.eclipse.ocl.util.Tuple; |
| import org.eclipse.ocl.utilities.ASTNode; |
| import org.eclipse.ocl.utilities.PredefinedType; |
| import org.eclipse.ocl.utilities.UMLReflection; |
| import org.eclipse.osgi.util.NLS; |
| |
| public class QvtOperationalEvaluationVisitorImpl |
| extends EvaluationVisitorImpl<EPackage, EClassifier, EOperation, EStructuralFeature, EEnumLiteral, EParameter, |
| EObject, CallOperationAction, SendSignalAction, Constraint, EClass, EObject> |
| implements QvtOperationalEvaluationVisitor, InternalEvaluator, DeferredAssignmentListener { |
| |
| private static int tempCounter = 0; |
| |
| private QvtOperationalEvaluationEnv myEvalEnv; |
| // FIXME - move me to the root environment? |
| private OCLAnnotationSupport oclAnnotationSupport; |
| |
| public QvtOperationalEvaluationVisitorImpl(QvtOperationalEnv env, QvtOperationalEvaluationEnv evalEnv) { |
| super(env, evalEnv, evalEnv.createExtentMap(null)); |
| |
| myEvalEnv = evalEnv; |
| } |
| |
| protected QvtOperationalEvaluationVisitorImpl(QvtOperationalEvaluationVisitorImpl parent, QvtOperationalEvaluationEnv nestedEvalEnv) { |
| this(parent.getOperationalEnv(), nestedEvalEnv); |
| |
| oclAnnotationSupport = parent.getOCLAnnotationSupport(); |
| } |
| |
| protected QvtOperationalEvaluationVisitorImpl createNestedEvaluationVisitor(QvtOperationalEvaluationVisitorImpl parent, QvtOperationalEvaluationEnv nestedEvalEnv) { |
| return new QvtOperationalEvaluationVisitorImpl(parent, nestedEvalEnv); |
| } |
| |
| public Object visitDictLiteralExp(DictLiteralExp dictLiteralExp) { |
| Dictionary<Object, Object> result = new DictionaryImpl<Object, Object>(); |
| for (DictLiteralPart part : dictLiteralExp.getPart()) { |
| Object key = part.getKey().accept(getVisitor()); |
| Object value = part.getValue().accept(getVisitor()); |
| if(key != getOclInvalid() && value != getOclInvalid()) |
| result.put(key, value); |
| } |
| |
| return result; |
| } |
| |
| @Override |
| public Object visitVariableExp(VariableExp<EClassifier, EParameter> v) { |
| QvtOperationalEvaluationEnv evalEnv = getOperationalEvaluationEnv(); |
| Variable<EClassifier, EParameter> vd = v.getReferredVariable(); |
| String varName = vd.getName(); |
| Object value = evalEnv.getValueOf(varName); |
| |
| if(QvtOperationalEnv.THIS.equals(varName)) { |
| EClassifier varType = v.getType(); |
| if(varType instanceof Module) { |
| Module referredThisModule = (Module)varType; |
| ModuleInstance thisObj = evalEnv.getThisOfType(referredThisModule); |
| // only if not null, the variables is part of the QVT type system. |
| // otherwise, it may be a custom variable in non-QVT execution context, like ImperativeOCL |
| if(thisObj != null) { |
| return thisObj; |
| } |
| } |
| } else if(vd instanceof ModelParameter) { |
| OperationalTransformation transformation = (OperationalTransformation) vd.eContainer(); |
| TransformationInstance transformationInstance = (TransformationInstance)evalEnv.getThisOfType(transformation); |
| assert transformationInstance != null; |
| |
| ModelInstance model = transformationInstance.getModel((ModelParameter)vd); |
| assert model != null; |
| return model; |
| } |
| |
| return value; |
| } |
| |
| /** |
| * Creates evaluation visitor for an non-transformation execution client. |
| * <p> |
| * No main entry operation is available, the execution flow is undefined and |
| * it is the responsibility of the executor client. |
| * </p> |
| * <code>Note:</code>Only helper operation can be executed on the resulting |
| * visitor by an external clients. |
| * |
| * @see #executeHelperOperation(Helper, Object, List) |
| */ |
| public static QvtOperationalEvaluationVisitorImpl createNonTransformationExecutionContextVisitor(Context context, ImportToNonTransformCtxHelper importProvider) { |
| QvtOperationalEnv env = (QvtOperationalEnv)QvtOperationalEnvFactory.INSTANCE.createEnvironment(); |
| QvtOperationalEvaluationEnv evalEnv = QvtOperationalEnvFactory.INSTANCE.createEvaluationEnvironment(context, null); |
| return createNonTransformationExecutionContextVisitor(env, evalEnv, importProvider); |
| } |
| |
| public static QvtOperationalEvaluationVisitorImpl createNonTransformationExecutionContextVisitor( |
| QvtOperationalEnv env, QvtOperationalEvaluationEnv evalEnv, ImportToNonTransformCtxHelper importsProvider) { |
| |
| QvtOperationalEvaluationVisitorImpl visitor = new QvtOperationalEvaluationVisitorImpl(env, evalEnv); |
| InternalEvaluationEnv internalEvalEnv = evalEnv.getAdapter(InternalEvaluationEnv.class); |
| |
| ThisInstanceResolver importsByAccess = importsProvider.createImportedInstanceResolver(); |
| internalEvalEnv.setThisResolver(importsByAccess); |
| |
| // initialize those module only newly instantiated, therefore uninitialized yet |
| for (ModuleInstance nextModuleToInit : importsProvider.getModuleInstances(false)) { |
| // the call bellow makes sure that all of its imported modules gets initialized |
| // if not done already due to other dependent modules |
| ModuleInstance.Internal internalModule = nextModuleToInit.getAdapter(ModuleInstance.Internal.class); |
| // check if initialized as the module we iterate through might cross-refence, so |
| // get initialized in the meantime |
| if(!internalModule.isInitialized()) { |
| visitor.initModule(nextModuleToInit, null); |
| } |
| } |
| |
| return visitor; |
| } |
| |
| public static QvtOperationalEvaluationVisitor createVisitor(QvtOperationalEnv env, QvtOperationalEvaluationEnv evalEnv) { |
| return new QvtOperationalEvaluationVisitorImpl(env, evalEnv).createInterruptibleVisitor(); |
| } |
| |
| @Override |
| public EvaluationEnvironment<EClassifier, EOperation, EStructuralFeature, EClass, EObject> getEvaluationEnvironment() { |
| return myEvalEnv; |
| } |
| |
| public void setOperationalEvaluationEnv(QvtOperationalEvaluationEnv evalEnv) { |
| myEvalEnv = evalEnv; |
| } |
| |
| protected void pushedStack(QvtOperationalEvaluationEnv env) { |
| } |
| |
| protected void poppedStack() { |
| } |
| |
| |
| public IContext getContext() { |
| return getOperationalEvaluationEnv().getContext(); |
| } |
| |
| public void notifyAfterDeferredAssign(final AssignExp assignExp, Object leftValue) { |
| // do nothing special here, subclasses may customize |
| } |
| |
| @Override |
| public Object visitIfExp(IfExp<EClassifier> ie) { |
| // get condition |
| OCLExpression<EClassifier> condition = ie.getCondition(); |
| |
| // evaluate condition` |
| Object condRawVal = condition.accept(getVisitor()); |
| Boolean condVal = (condRawVal != getOclInvalid()) ? (Boolean) condRawVal : Boolean.FALSE; |
| |
| if (condVal != null && condVal.booleanValue()) { |
| return ie.getThenExpression().accept(getVisitor()); |
| } |
| return ie.getElseExpression().accept(getVisitor()); |
| } |
| |
| @Override |
| public Object visitExpression(OCLExpression<EClassifier> expression) { |
| // Override the super implementation as we need our supported exception to be propagated |
| // but super type catches it |
| // TODO - needs to be secured in a general contract from MDT OCL as any place at OCL might |
| // decide to perform the catch |
| return expression.accept(getVisitor()); |
| } |
| |
| public Object visitAssignExp(final AssignExp assignExp) { |
| QvtOperationalEvaluationEnv env = getOperationalEvaluationEnv(); |
| InternalEvaluationEnv internEnv = env.getAdapter(InternalEvaluationEnv.class); |
| |
| boolean isDeferred = false; |
| // TODO: modify the following code for more complex l-values |
| OCLExpression<EClassifier> lValue = assignExp.getLeft(); |
| if (assignExp.getValue().size() == 1) { |
| isDeferred = QvtResolveUtil.hasDeferredRightSideValue(assignExp); |
| if(isDeferred) { |
| Object ownerObj = getAssignExpLValueOwner(lValue); |
| if (ownerObj instanceof EObject) { |
| PropertyCallExp<EClassifier, EStructuralFeature> lvalueExp = (PropertyCallExp<EClassifier, EStructuralFeature>) assignExp.getLeft(); |
| EStructuralFeature referredProperty = getRenamedProperty(lvalueExp.getReferredProperty()); |
| internEnv.setLastAssignmentLvalueEval(new EObjectEStructuralFeaturePair((EObject) ownerObj, referredProperty)); |
| } |
| } |
| } |
| |
| Object exprValue = null; |
| for (OCLExpression<EClassifier> exp : assignExp.getValue()) { |
| exprValue = exp.accept(getVisitor()); |
| } |
| |
| if(isDeferred) { |
| // the source of resolve calls has been evaluated above -> assignment of the left value is not executed now |
| // but at deferred time in the end of the transformation |
| return null; |
| } |
| |
| if (lValue instanceof VariableExp) { |
| @SuppressWarnings("unchecked") |
| VariableExp<EClassifier, EParameter> varExp = (VariableExp<EClassifier, EParameter>) lValue; |
| Variable<EClassifier, EParameter> referredVariable = varExp.getReferredVariable(); |
| if (referredVariable != null) { |
| String varName = referredVariable.getName(); |
| Object oldValue = getRuntimeValue(varName); |
| EClassifier variableType = lValue.getType(); |
| if ((variableType instanceof CollectionType) && (oldValue instanceof Collection)) { |
| @SuppressWarnings("unchecked") |
| Collection<Object> oldOclCollection = (Collection<Object>) oldValue; |
| Collection<Object> leftOclCollection; |
| if (assignExp.isIsReset()) { |
| leftOclCollection = EvaluationUtil.createNewCollectionOfSameKind(oldOclCollection); |
| } else { |
| leftOclCollection = oldOclCollection; |
| } |
| |
| final CollectionKind leftCollectionKind = getCollectionKind(leftOclCollection); |
| if (exprValue instanceof Collection) { |
| if(leftCollectionKind == CollectionKind.ORDERED_SET_LITERAL) { |
| // can't use CollectionUtil.union(), the result type is not OrderedSet |
| LinkedHashSet<Object> values = new LinkedHashSet<Object>(); |
| values.addAll(leftOclCollection); |
| values.addAll((Collection<?>)exprValue); |
| leftOclCollection = CollectionUtil.createNewCollection(((CollectionType<?, ?>)variableType).getKind(), values); |
| } else if(leftCollectionKind != null){ |
| leftOclCollection = CollectionUtil.union(leftOclCollection, (Collection<?>) exprValue); |
| } else { |
| // QVT mutable types |
| leftOclCollection.addAll((Collection<?>)exprValue); |
| } |
| } else if (leftCollectionKind == CollectionKind.ORDERED_SET_LITERAL |
| || leftCollectionKind == CollectionKind.SEQUENCE_LITERAL) { |
| leftOclCollection = CollectionUtil.append(leftOclCollection, exprValue); |
| } else if(leftCollectionKind != null) { |
| leftOclCollection = CollectionUtil.including(leftOclCollection, exprValue); |
| } else { |
| // QVT mutable types |
| leftOclCollection.add(exprValue); |
| } |
| |
| replaceInEnv(varName, leftOclCollection, variableType); |
| } else { |
| replaceInEnv(varName, exprValue, variableType); |
| } |
| } |
| } else if (lValue instanceof PropertyCallExp) { |
| Object ownerObj = getAssignExpLValueOwner(lValue); |
| if (ownerObj instanceof EObject) { |
| EObject oldIP = setCurrentEnvInstructionPointer(assignExp); |
| env.callSetter( |
| (EObject) ownerObj, |
| getRenamedProperty(((PropertyCallExp<EClassifier, EStructuralFeature>) lValue).getReferredProperty()), |
| exprValue, isUndefined(exprValue), assignExp.isIsReset()); |
| setCurrentEnvInstructionPointer(oldIP); |
| } |
| } else { |
| throw new UnsupportedOperationException("Unsupported LValue type: " + ((lValue == null) ? null : lValue.getType())); //$NON-NLS-1$ |
| } |
| |
| return exprValue; |
| } |
| |
| private Object getAssignExpLValueOwner(OCLExpression<EClassifier> lValue) { |
| if (lValue instanceof PropertyCallExp<?, ?>) { |
| @SuppressWarnings("unchecked") |
| PropertyCallExp<EClassifier, EParameter> propertyCallExp = (PropertyCallExp<EClassifier, EParameter>) lValue; |
| OCLExpression<EClassifier> sourceExp = propertyCallExp.getSource(); |
| Object owner = sourceExp.accept(getVisitor()); |
| return owner; |
| } |
| return null; |
| } |
| |
| private Object visitConfigProperty(EStructuralFeature configProperty) { |
| setCurrentEnvInstructionPointer(configProperty); |
| |
| IContext context = getOperationalEvaluationEnv().getContext(); |
| |
| Object rawValue = context.getConfigProperty(configProperty.getName()); |
| EClassifier propertyType = configProperty.getEType(); |
| |
| Object value = rawValue; |
| if(rawValue instanceof String && propertyType != getEnvironment().getOCLStandardLibrary().getString()) { |
| value = createFromString(propertyType, (String) rawValue); |
| } |
| |
| if(value == getOclInvalid()) { |
| // we failed to parse the value |
| throwQVTException(new QvtRuntimeException(NLS.bind(EvaluationMessages.QvtOperationalEvaluationVisitorImpl_invalidConfigPropertyValue, configProperty.getName(), rawValue))); |
| } |
| |
| return value; |
| } |
| |
| public Object visitEntryOperation(EntryOperation entryOperation) { |
| visitImperativeOperation(entryOperation); |
| return new OperationCallResult(visitOperationBody(entryOperation.getBody()), getOperationalEvaluationEnv()); |
| } |
| |
| public Object visitHelper(Helper helper) { |
| visitImperativeOperation(helper); |
| return new OperationCallResult(visitOperationBody(helper.getBody()), getOperationalEvaluationEnv()); |
| } |
| |
| public Object visitImperativeOperation(ImperativeOperation imperativeOperation) { |
| if (imperativeOperation.isIsBlackbox()) { |
| throw new IllegalArgumentException( |
| "Blackbox rules are not supported: " + imperativeOperation.getName()); //$NON-NLS-1$ |
| } |
| |
| List<Object> args = getOperationalEvaluationEnv().getOperationArgs(); |
| Iterator<Object> argIt = args.iterator(); |
| |
| QvtOperationalEvaluationEnv env = getOperationalEvaluationEnv(); |
| for (EParameter nextParam : imperativeOperation.getEParameters()) { |
| if (!argIt.hasNext()) { |
| throw new IllegalArgumentException("arguments mismatch: got" + args + ", expected " + //$NON-NLS-1$ //$NON-NLS-2$ |
| imperativeOperation.getEParameters()); |
| } |
| |
| VarParameter param = (VarParameter) nextParam; |
| Object arg = argIt.next(); |
| |
| addToEnv(param.getName(), arg, param.getEType()); |
| } |
| |
| EClassifier contextType = QvtOperationalParserUtil.getContextualType(imperativeOperation); |
| if (contextType != null) { |
| addToEnv(Environment.SELF_VARIABLE_NAME, env.getOperationSelf(), contextType); |
| } |
| |
| pushedStack(getOperationalEvaluationEnv()); |
| return null; |
| } |
| |
| public Object visitLibrary(Library library) { |
| return null; |
| } |
| |
| public Object visitLocalProperty(EStructuralFeature property) { |
| OCLExpression<EClassifier> initExp = QvtOperationalParserUtil.getInitExpression(property); |
| if(initExp != null) { |
| return initExp.accept(getVisitor()); |
| } |
| |
| return null; |
| } |
| |
| public Object visitContextualProperty(ContextualProperty contextualProperty) { |
| if(contextualProperty.getInitExpression() != null) { |
| return contextualProperty.getInitExpression().accept(getVisitor()); |
| } |
| return null; |
| } |
| |
| protected boolean isWhenPreconditionSatisfied(MappingOperation mappingOperation) { |
| if(mappingOperation.getWhen().isEmpty()) { |
| return true; |
| } |
| for (OCLExpression<EClassifier> nextCond : mappingOperation.getWhen()) { |
| if(!Boolean.TRUE.equals(nextCond.accept(getVisitor()))) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| private void setupInitialResultVariables(MappingBody mappingBody) { |
| ImperativeOperation operation = mappingBody.getOperation(); |
| QvtOperationalEvaluationEnv evalEnv = getOperationalEvaluationEnv(); |
| // Note: the variables for result parameters may not be set or existing yet |
| // define variables if not done already by the reusing mapping caller so |
| // avoid overriding values |
| for (VarParameter resultParam : operation.getResult()) { |
| if(evalEnv.getValueOf(resultParam.getName()) == null) { |
| replaceInEnv(resultParam.getName(), null, resultParam.getEType()); |
| } |
| } |
| |
| if(operation.getResult().size() > 1) { |
| if(evalEnv.getValueOf(Environment.RESULT_VARIABLE_NAME) == null) { |
| replaceInEnv(Environment.RESULT_VARIABLE_NAME, null, operation.getEType()); |
| } |
| } |
| } |
| |
| public Object visitMappingBody(MappingBody mappingBody) { |
| boolean hasResultVar = ! mappingBody.getOperation().getResult().isEmpty(); |
| QvtOperationalEvaluationEnv evalEnv = getOperationalEvaluationEnv(); |
| |
| setupInitialResultVariables(mappingBody); |
| |
| for (OCLExpression<EClassifier> initExp : mappingBody.getInitSection()) { |
| initExp.accept(getVisitor()); |
| } |
| |
| Object result = null; |
| |
| if(hasResultVar) { |
| result = createOrGetResult((MappingOperation) mappingBody.getOperation()); |
| } |
| |
| MappingOperation currentMappingCalled = (MappingOperation) evalEnv.getOperation(); |
| |
| // call inherited mappings |
| if(!currentMappingCalled.getInherited().isEmpty()) { |
| for (MappingOperation extendedMapping : currentMappingCalled.getInherited()) { |
| executeImperativeOperation(extendedMapping, evalEnv.getOperationSelf(), evalEnv.getOperationArgs(), true); |
| } |
| } |
| |
| Object bodyResult = visitOperationBody(mappingBody); |
| if (hasResultVar && bodyResult != null) { |
| //result = bodyResult; |
| } |
| |
| // TODO investigate possibility to modify result |
| for (OCLExpression<EClassifier> endExp : mappingBody.getEndSection()) { |
| endExp.accept(getVisitor()); |
| } |
| |
| // call merged mappings |
| if(!currentMappingCalled.getMerged().isEmpty()) { |
| for (MappingOperation extendedMapping : currentMappingCalled.getMerged()) { |
| executeImperativeOperation(extendedMapping, evalEnv.getOperationSelf(), evalEnv.getOperationArgs(), true); |
| } |
| } |
| |
| return result; |
| } |
| |
| public Object visitMappingCallExp(MappingCallExp mappingCallExp) { |
| return visitOperationCallExp(mappingCallExp); |
| } |
| |
| @Override |
| public Object visitOperationCallExp(OperationCallExp<EClassifier, EOperation> operationCallExp) { |
| // TODO - review the note bellow |
| // set IP of the current stack (represented by the top operation fEnv) |
| // to the this operation call in order to refeflect this call position |
| // in possible QVT stack, in case an exception is thrown |
| EObject oldIP = setCurrentEnvInstructionPointer(operationCallExp); |
| Object result = doVisitOperationCallExp(operationCallExp); |
| setCurrentEnvInstructionPointer(oldIP); |
| return result; |
| } |
| |
| protected Object doVisitOperationCallExp(OperationCallExp<EClassifier, EOperation> operationCallExp) { |
| EOperation referredOperation = operationCallExp.getReferredOperation(); |
| |
| boolean isImperative = QvtOperationalUtil.isImperativeOperation(referredOperation); |
| if (isImperative && !CallHandler.Access.hasHandler(referredOperation)) { |
| Object source = operationCallExp.getSource().accept(getVisitor()); |
| List<Object> args = makeArgs(operationCallExp); |
| // does not make sense continue at all, call on null or invalid results in invalid |
| if(isUndefined(source)) { |
| return getOclInvalid(); |
| } |
| |
| ImperativeOperation method = null; |
| if (QvtOperationalParserUtil.isOverloadableMapping(referredOperation, getOperationalEnv())) { |
| EOperation actualOperation = findOperationByActualSourceType(source, referredOperation); |
| if(actualOperation instanceof ImperativeOperation) { |
| method = (ImperativeOperation) actualOperation; |
| } |
| } |
| |
| if((method == null) && referredOperation instanceof ImperativeOperation) { |
| // we can't dispatch non-imperative as we already evaluated the source |
| method = (ImperativeOperation)referredOperation; |
| } |
| |
| if(method != null) { |
| return executeImperativeOperation(method, source, args, false).myResult; |
| } |
| } |
| |
| Object result = null; |
| try { |
| result = super.visitOperationCallExp(operationCallExp); |
| } |
| catch (QvtRuntimeException e) { |
| throw e; |
| } |
| catch (RuntimeException ex) { |
| if(canBePropagated(ex)) { |
| throw ex; |
| } |
| Logger.getLogger().log(Logger.WARNING, "QvtEvaluator: failed to evaluate oclOperationCall", ex);//$NON-NLS-1$ |
| result = getOclInvalid(); |
| } |
| |
| // Note: we have to check for QVT exception caught by MDT OCL, those turned into invalid result, resolved |
| // as failed operation calls, but QVT need some exceptions to propagate to the main running transformation, |
| // for instance QVTInterruptedExecutionException |
| if(result == getOclInvalid()) { |
| // check whether we have got exception from explicit call to 'Transformation::transform()' |
| QvtRuntimeException e = getOperationalEvaluationEnv().getAdapter(InternalEvaluationEnv.class).getException(); |
| if(e != null) { |
| // propagate upward the stack in the current transformation |
| throw e; |
| } |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Finds operation based on the actual type of the source object as per |
| * virtual call semantics. |
| * |
| * @param source |
| * the self instance of the contextual type |
| * @param referredOperation |
| * the operation referred by the call expresion in the AST |
| * @return the actual operation to call, which is either the passed referred |
| * operation or another one defined in the bottom-most class in the |
| * type hierarchy of the source object |
| */ |
| private EOperation findOperationByActualSourceType(Object source, EOperation referredOperation) { |
| EOperation actualOperation = referredOperation; |
| IVirtualOperationTable vTable = getVirtualTable(referredOperation); |
| if(vTable != null) { |
| EClassifier sourceEClass = getEvaluationEnvironment().getType(source); |
| if(sourceEClass != null) { |
| actualOperation = vTable.lookupActualOperation(sourceEClass, getEnvironment()); |
| } |
| } |
| return actualOperation; |
| } |
| |
| @Override |
| public Object visitPropertyCallExp(PropertyCallExp<EClassifier, EStructuralFeature> pc) { |
| EStructuralFeature renamedProperty = getRenamedProperty(pc.getReferredProperty()); |
| if (renamedProperty != pc.getReferredProperty()) { |
| // TODO possible 'pc' should be cloned in case it's readonly |
| pc.setReferredProperty(renamedProperty); |
| } |
| return super.visitPropertyCallExp(pc); |
| } |
| |
| @Override |
| public Object visitEnumLiteralExp(EnumLiteralExp<EClassifier, EEnumLiteral> el) { |
| return el.getReferredEnumLiteral().getInstance(); |
| } |
| |
| /** |
| * @return result object or <code>null</code> in case that no result |
| * variable is declared and <code>OclInvalid</code> in case the |
| * precondition failed and the body was not executed. |
| */ |
| public Object visitMappingOperation(MappingOperation mappingOperation) { |
| visitImperativeOperation(mappingOperation); |
| |
| QvtOperationalEvaluationEnv evalEnv = getOperationalEvaluationEnv(); |
| if (!isWhenPreconditionSatisfied(mappingOperation)) { |
| // return Invalid, to indicate precondition failure to the caller. It is used instead of |
| // of 'null' as it is a legal value for no result inout mapping, even |
| // the precondition holds |
| |
| return new MappingCallResult(null, evalEnv, MappingCallResult.PRECOND_FAILED); |
| } |
| |
| if(!mappingOperation.getDisjunct().isEmpty()) { |
| return dispatchDisjuctMapping(mappingOperation); |
| } |
| |
| // check the traces whether the relation already holds |
| TraceRecord traceRecord = TraceUtil.getTraceRecord(evalEnv, mappingOperation); |
| if (traceRecord != null) { |
| return new MappingCallResult(TraceUtil.fetchResultFromTrace(evalEnv, traceRecord), evalEnv, MappingCallResult.FETCHED_FROM_TRACE); |
| } |
| |
| return new MappingCallResult(((OperationBodyImpl) mappingOperation.getBody()).accept(getVisitor()), |
| evalEnv, MappingCallResult.BODY_EXECUTED); |
| } |
| |
| public Object visitModule(Module module) { |
| if (module instanceof OperationalTransformation == false) { |
| throw new IllegalArgumentException(NLS.bind(EvaluationMessages.ExtendedOclEvaluatorVisitorImpl_ModuleNotExecutable, module.getName())); |
| } |
| |
| try { |
| return doVisitTransformation((OperationalTransformation) module); |
| } |
| catch (QvtRuntimeException e) { |
| StringWriter strWriter = new StringWriter(); |
| PrintWriter printWriter = new PrintWriter(strWriter); |
| e.printQvtStackTrace(printWriter); |
| getContext().getLog().log(strWriter.getBuffer().toString()); |
| throw e; |
| } |
| } |
| |
| /** |
| * Executes the given helper operation with actual arguments passed. |
| * |
| * @param method |
| * the helper operation to execute |
| * @param self |
| * the contextual instance in case of contextual operation, |
| * otherwise the default instance of the module which defines the |
| * called operation |
| * @param args |
| * the actual parameter values |
| * @return return the value directly returned by the operation |
| */ |
| public Object executeHelperOperation(Helper method, Object self, List<Object> args) { |
| Helper actualHelper = method; |
| EOperation actualOperation = findOperationByActualSourceType(self, method); |
| if(actualOperation instanceof Helper) { |
| actualHelper = (Helper) actualOperation; |
| } else { |
| // Remark - the case when the actual operation is not imperative but |
| // a meta-model operation the virtual call will be dispatched by using |
| // a normal operation call |
| return getEvaluationEnvironment().callOperation(method, -1, self, args.toArray(new Object[args.size()])); |
| } |
| |
| OperationCallResult result = executeImperativeOperation(actualHelper, self, args, false); |
| return result.myResult; |
| } |
| |
| public Object visitInstantiationExp(InstantiationExp objectExp) { |
| // should instantiate the module transformation |
| EClass _class = objectExp.getInstantiatedClass(); |
| assert _class instanceof OperationalTransformation; // do not support normal class constructor calls yet |
| |
| EList<OCLExpression<EClassifier>> formalArguments = objectExp.getArgument(); |
| List<ModelInstance> actualArguments = new ArrayList<ModelInstance>(formalArguments.size()); |
| |
| for (OCLExpression<EClassifier> nextArg : formalArguments) { |
| Object argVal = nextArg.accept(getVisitor()); |
| if(argVal instanceof ModelInstance == false) { |
| throwQVTException(new QvtRuntimeException("Undefined model passed to transformation")); |
| } |
| ModelInstance modelInstance = (ModelInstance) argVal; |
| actualArguments.add(modelInstance); |
| } |
| |
| OperationalTransformation targetTransf = (OperationalTransformation)_class; |
| QvtOperationalEvaluationEnv currentEnv = getOperationalEvaluationEnv(); |
| // create a nested environment for constructor invocation representing |
| // the root environment of explicitly called transformation |
| // Empty default context, no configuration property is available |
| // Remark: Can we set configuration property in the concrete syntax on the explicit transf object. |
| Context nestedContext = EvaluationUtil.createAggregatedContext(currentEnv); |
| |
| QvtOperationalEnvFactory envFactory = getOperationalEnv().getFactory(); |
| QvtOperationalEvaluationEnv nestedEvalEnv = envFactory.createEvaluationEnvironment(nestedContext, null); |
| // send arguments into the entry operation |
| nestedEvalEnv.getOperationArgs().addAll(actualArguments); |
| // Use per transformation instance visitor |
| InternalEvaluator nestedVisitor = createNestedEvaluationVisitor(this, nestedEvalEnv).createInterruptibleVisitor(); |
| try { |
| setOperationalEvaluationEnv(nestedEvalEnv); |
| |
| ModuleInstance moduleInstance = nestedVisitor.callTransformationImplicitConstructor(targetTransf, actualArguments); |
| //nestedEvalEnv.add(QvtOperationalEnv.THIS, moduleInstance); |
| moduleInstance.getAdapter(InternalTransformation.class).setEntryOperationHandler(createEntryOperationHandler(nestedVisitor)); |
| return moduleInstance; |
| } finally { |
| setOperationalEvaluationEnv(currentEnv); |
| } |
| } |
| |
| public TransformationInstance callTransformationImplicitConstructor(OperationalTransformation transformation, List<ModelInstance> transfArgs) { |
| ModuleInstanceFactory eFactory = (ModuleInstanceFactory)transformation.getEFactoryInstance(); |
| assert eFactory != null : "Module class must have factory"; //$NON-NLS-1$ |
| |
| TransformationInstance instance = (TransformationInstance) eFactory.create(transformation); |
| // Note: need to make the main executed transf available via this |
| // so all super module have access to env::getCurrentTransformation() |
| getEvaluationEnvironment().add(QvtOperationalEnv.THIS, instance); |
| |
| ModelParameterHelper modelParameters = new ModelParameterHelper(transformation, transfArgs); |
| initModule(instance, modelParameters); |
| // we are initialized set back the pointer to the module |
| setCurrentEnvInstructionPointer(transformation); |
| return instance; |
| } |
| |
| // FIXME - review the strange case of having various return types |
| private Object doVisitTransformation(OperationalTransformation transformation) { |
| ImperativeOperation entryPoint = QvtOperationalParserUtil.getMainOperation(transformation); |
| if (entryPoint == null) { |
| throw new IllegalArgumentException(NLS.bind(EvaluationMessages.ExtendedOclEvaluatorVisitorImpl_ModuleNotExecutable, transformation.getName())); |
| } |
| |
| QvtOperationalEvaluationEnv evaluationEnv = getOperationalEvaluationEnv(); |
| List<ModelInstance> modelArgs = EvaluationUtil.getTransfromationModelArguments(evaluationEnv, transformation); |
| |
| TransformationInstance moduleInstance = callTransformationImplicitConstructor(transformation, modelArgs); |
| CallHandler entryOperationHandler = createEntryOperationHandler(this); |
| InternalTransformation internTransf = moduleInstance.getAdapter(InternalTransformation.class); |
| internTransf.setEntryOperationHandler(entryOperationHandler); |
| |
| // call main entry operation |
| OperationCallResult callResult = (OperationCallResult) entryOperationHandler.invoke( |
| null, moduleInstance, |
| makeEntryOperationArgs(entryPoint, |
| transformation).toArray(), evaluationEnv); |
| |
| QvtEvaluationResult evalResult = EvaluationUtil.createEvaluationResult(callResult.myEvalEnv); |
| |
| if (evalResult.getModelExtents().isEmpty()) { |
| if (callResult.myResult instanceof EObject) { |
| // compatibility reason, make the main() operation return value available in an extent |
| ModelParameterExtent modelParameter = new ModelParameterExtent((EObject) callResult.myResult); |
| evalResult.getModelExtents().add(modelParameter.getContents()); |
| } else { |
| return callResult.myResult; |
| } |
| } |
| |
| return evalResult; |
| } |
| |
| private static CallHandler createEntryOperationHandler(final InternalEvaluator evaluator) { |
| return new CallHandler() { |
| public Object invoke(ModuleInstance module, Object source, Object[] args, QvtOperationalEvaluationEnv evalEnv) { |
| TransformationInstance transformation = (TransformationInstance) source; |
| try { |
| return evaluator.runMainEntry(transformation.getTransformation(), Arrays.asList(args)); |
| } finally { |
| transformation.getAdapter(InternalTransformation.class).dispose(); |
| } |
| } |
| }; |
| } |
| |
| protected void processDeferredTasks() { |
| InternalEvaluationEnv internalEvalEnv = getOperationalEvaluationEnv().getAdapter(InternalEvaluationEnv.class); |
| internalEvalEnv.processDeferredTasks(); |
| } |
| |
| public Object visitModuleImport(ModuleImport moduleImport) { |
| return null; |
| } |
| |
| public Object visitObjectExp(ObjectExp objectExp) { |
| InternalEvaluationEnv internEnv = getOperationalEvaluationEnv().getAdapter(InternalEvaluationEnv.class); |
| internEnv.setCurrentIP(objectExp); |
| |
| Object owner = getOutOwner(objectExp); |
| |
| internEnv.pushObjectExpOwner(owner); |
| |
| if(objectExp.getBody() != null) { |
| EList<OCLExpression<EClassifier>> contents = objectExp.getBody().getContent(); |
| for (OCLExpression<EClassifier> exp : contents) { |
| exp.accept(getVisitor()); |
| } |
| } |
| |
| internEnv.popObjectExpOwner(); |
| |
| if (getOperationalEnv().isTemporaryElement(objectExp.getName())) { |
| getOperationalEvaluationEnv().remove(objectExp.getName()); |
| } |
| |
| return owner; |
| } |
| |
| public Object visitReturnExp(ReturnExp returnExp) { |
| Object value = null; |
| OCLExpression<EClassifier> valueExp = returnExp.getValue(); |
| if(valueExp != null) { |
| value = valueExp.accept(getVisitor()); |
| } |
| |
| OperationBody body = QvtOperationalParserUtil.findParentElement(returnExp, OperationBody.class); |
| if(body != null) { |
| EList<OCLExpression<EClassifier>> content = body.getContent(); |
| if(!content.isEmpty() && content.get(content.size() - 1) == returnExp) { |
| // return is the last expression in the body, simply return the value |
| return value; |
| } |
| } |
| |
| // TODO - analyze more complex structured execution flow and |
| // avoid the cost of exception throwing |
| throw new ReturnExpEvent(value, getOperationalEvaluationEnv()); |
| } |
| |
| public Object visitOperationBody(OperationBody operationBody) { |
| Object result = null; |
| for (OCLExpression<EClassifier> exp : operationBody.getContent()) { |
| result = exp.accept(getVisitor()); |
| } |
| |
| ImperativeOperation operation = operationBody.getOperation(); |
| if(operation.getResult().size() > 1) { |
| return createTupleResult(operation); |
| } |
| return result; |
| } |
| |
| public Object visitClass(Class class_) { |
| return null; |
| } |
| |
| public Object visitRename(Rename rename) { |
| // nothing to do in runtime, all should be resolved during parsing time. |
| // https://bugs.eclipse.org/bugs/show_bug.cgi?id=230175 |
| return null; |
| /* EClassifier context = rename.getEType(); |
| |
| // if source is undefined, result is OclInvalid |
| if (isUndefined(context)) |
| return getOclInvalid(); |
| |
| EStructuralFeature origProperty = getEnvironment().lookupProperty(rename.getEType(), rename.getName()); |
| return origProperty; |
| */ |
| } |
| |
| public Object visitVarParameter(VarParameter varParameter) { |
| return null; |
| } |
| |
| public Object visitVariableInitExp(VariableInitExp variableInitExp) { |
| Object varValue = variableInitExp.getValue().accept(getVisitor()); |
| replaceInEnv(variableInitExp.getName(), varValue, variableInitExp.getType()); |
| return varValue; |
| } |
| |
| public Object visitBlockExp(BlockExp blockExp) { |
| List<String> scopeVars = null; |
| boolean isInImperativeOper = blockExp.eContainer() instanceof ImperativeOperation; |
| |
| for (OCLExpression<EClassifier> exp : blockExp.getBody()) { |
| if((exp instanceof VariableInitExp) && !isInImperativeOper) { |
| if(scopeVars == null) { |
| scopeVars = new LinkedList<String>(); |
| } |
| VariableInitExp varInitExp = (VariableInitExp) exp; |
| scopeVars.add(varInitExp.getName()); |
| } |
| |
| exp.accept(getVisitor()); |
| } |
| |
| if(scopeVars != null) { |
| EvaluationEnvironment<EClassifier, EOperation, EStructuralFeature, EClass, EObject> evalEnv = getEvaluationEnvironment(); |
| for (String varName : scopeVars) { |
| evalEnv.remove(varName); |
| } |
| } |
| |
| return null; |
| } |
| |
| public Object visitComputeExp(ComputeExp computeExp) { |
| Variable<EClassifier, EParameter> returnedElement = computeExp.getReturnedElement(); |
| Object initExpressionValue = null; |
| OCLExpression<EClassifier> initExpression = returnedElement.getInitExpression(); |
| if (initExpression != null) { |
| initExpressionValue = initExpression.accept(getVisitor()); |
| } |
| replaceInEnv(returnedElement.getName(), initExpressionValue, returnedElement.getType()); |
| |
| computeExp.getBody().accept(getVisitor()); |
| |
| return getEvaluationEnvironment().remove(returnedElement.getName()); |
| } |
| |
| public Object visitWhileExp(WhileExp whileExp) { |
| Variable<EClassifier, EParameter> resultVar = whileExp.getResultVar(); |
| if(resultVar != null) { |
| resultVar.accept(getVisitor()); |
| } |
| while (true) { |
| Object condition = whileExp.getCondition().accept(getVisitor()); |
| if (Boolean.TRUE.equals(condition)) { |
| whileExp.getBody().accept(getVisitor()); |
| } else { |
| break; |
| } |
| } |
| |
| if(resultVar != null) { |
| return getEvaluationEnvironment().remove(resultVar.getName()); |
| } else if(whileExp.getResult() != null) { |
| return whileExp.getResult().accept(getVisitor()); |
| } |
| |
| return null; |
| } |
| |
| private static class SwitchAltExpResult { |
| public Object myCondition; |
| public Object myResult; |
| } |
| public Object visitSwitchAltExp(AltExp switchAltExp) { |
| SwitchAltExpResult result = new SwitchAltExpResult(); |
| result.myCondition = switchAltExp.getCondition().accept(getVisitor()); |
| if (Boolean.TRUE.equals(result.myCondition)) { |
| result.myResult = switchAltExp.getBody().accept(getVisitor()); |
| } |
| return result; |
| } |
| |
| public Object visitSwitchExp(SwitchExp switchExp) { |
| for (AltExp altExp : switchExp.getAlternativePart()) { |
| Object altResult = altExp.accept(getVisitor()); |
| if (altResult instanceof SwitchAltExpResult) { |
| if (Boolean.TRUE.equals(((SwitchAltExpResult) altResult).myCondition)) { |
| return ((SwitchAltExpResult) altResult).myResult; |
| } |
| } |
| else if (Boolean.TRUE.equals(altResult)) { |
| return null; |
| } |
| } |
| OCLExpression<EClassifier> elsePart = switchExp.getElsePart(); |
| if (elsePart != null) { |
| return elsePart.accept(getVisitor()); |
| } |
| return null; |
| } |
| |
| /* resolve expressions family */ |
| |
| public Object visitResolveExp(ResolveExp resolveExp) { |
| if (resolveExp.isIsDeferred()) { |
| InternalEvaluationEnv internalEvalEnv = getOperationalEvaluationEnv().getAdapter(InternalEvaluationEnv.class); |
| if(!internalEvalEnv.isDeferredExecution()) { |
| LateResolveTask lateResolveTask = new LateResolveTask(resolveExp, internalEvalEnv.getLastAssignmentLvalueEval(), getQVTVisitor(), getOperationalEvaluationEnv(), this); |
| internalEvalEnv.addDeferredTask(lateResolveTask); |
| return null; |
| } |
| } |
| return QvtResolveUtil.resolveNow(resolveExp, this, getOperationalEvaluationEnv()); |
| } |
| |
| public Object visitResolveInExp(ResolveInExp resolveInExp) { |
| if (resolveInExp.isIsDeferred()) { |
| InternalEvaluationEnv internalEvalEnv = getOperationalEvaluationEnv().getAdapter(InternalEvaluationEnv.class); |
| if(!internalEvalEnv.isDeferredExecution()) { |
| LateResolveInTask lateResolveInTask = new LateResolveInTask(resolveInExp, internalEvalEnv.getLastAssignmentLvalueEval(), getQVTVisitor(), getOperationalEvaluationEnv(), this); |
| internalEvalEnv.addDeferredTask(lateResolveInTask); |
| return null; |
| } |
| } |
| return QvtResolveUtil.resolveInNow(resolveInExp, this, getOperationalEvaluationEnv()); |
| } |
| |
| public Object visitModelType(ModelType modelType) { |
| return null; |
| } |
| |
| public Object visitLogExp(LogExp logExp) { |
| doVisitLogExp(logExp, getContext().getLog(), null); |
| return null; |
| } |
| |
| private boolean doVisitLogExp(LogExp logExp, Log logger, String messagePrefix) { |
| if(logExp.getCondition() != null && !Boolean.TRUE.equals(logExp.getCondition().accept(getVisitor()))) { |
| return false; |
| } |
| InternalEvaluationEnv internalEnv = getOperationalEvaluationEnv().getAdapter(InternalEvaluationEnv.class); |
| Object invalid = internalEnv.getInvalid(); |
| String invalidRepr = "<Invalid>"; //$NON-NLS-1$ |
| // process logging level |
| Integer level = null; |
| EList<OCLExpression<EClassifier>> args = logExp.getArgument(); |
| if(args.size() > 2) { |
| Object levelObj = args.get(2).accept(getVisitor()); |
| level = NumberConversions.strictConvertNumber(levelObj, Integer.class); |
| } |
| |
| Object message = args.get(0).accept(getVisitor()); |
| if(message == null) { |
| message = "<null>"; //$NON-NLS-1$ |
| } else if(message == invalid) { |
| message = invalidRepr; |
| } |
| |
| if(messagePrefix != null) { |
| message = messagePrefix + " : " + message; |
| } |
| |
| Object element = null; |
| Object formatedElement = null; |
| if(args.size() > 1) { |
| element = args.get(1).accept(getVisitor()); |
| if(element == invalid) { |
| formatedElement = invalidRepr; //$NON-NLS-1$ |
| } else { |
| formatedElement = EvaluationUtil.formatLoggedElement(element); |
| } |
| } |
| |
| if(level == null) { |
| if(element == null) { |
| logger.log(String.valueOf(message)); |
| } else { |
| logger.log(String.valueOf(message), formatedElement); |
| } |
| } else { |
| if(element == null) { |
| logger.log(level, String.valueOf(message)); |
| } else { |
| logger.log(level, String.valueOf(message), formatedElement); |
| } |
| } |
| |
| return true; |
| } |
| |
| public Object visitAssertExp(AssertExp assertExp) { |
| if(assertExp.getAssertion() != null && !Boolean.TRUE.equals(assertExp.getAssertion().accept(getVisitor()))) { |
| setCurrentEnvInstructionPointer(assertExp); |
| EObject parent = assertExp; |
| while(parent != null && !(parent instanceof Module)) { |
| parent = parent.eContainer(); |
| if(parent instanceof ImperativeOperation) { |
| parent = QvtOperationalParserUtil.getOwningModule((ImperativeOperation) parent); |
| } |
| } |
| |
| String source = EvaluationMessages.UknownSourceLabel; |
| if(parent != null) { |
| String moduleName = ((Module)parent).getName(); |
| if(moduleName != null) { |
| source = moduleName; |
| } |
| } |
| |
| StringBuilder locationBuf = new StringBuilder(source); |
| if(assertExp.getLine() >= 0) { |
| locationBuf.append(':').append(assertExp.getLine()); |
| } |
| |
| String message = NLS.bind(EvaluationMessages.AssertFailedMessage, assertExp.getSeverity(), locationBuf.toString()); |
| Log logger = getContext().getLog(); |
| |
| //logger.print(message); |
| if(assertExp.getLog() != null) { |
| //logger.print(" : "); //$NON-NLS-1$ |
| doVisitLogExp(assertExp.getLog(), logger, message); |
| } else { |
| logger.log(message); |
| } |
| |
| //logger.println(); |
| if(SeverityKind.FATAL.equals(assertExp.getSeverity())) { |
| logger.log(EvaluationMessages.TerminatingExecution); |
| } |
| |
| if(SeverityKind.FATAL.equals(assertExp.getSeverity())) { |
| throwQVTException(new QvtAssertionFailed(EvaluationMessages.FatalAssertionFailed)); |
| } |
| |
| } |
| |
| |
| return null; |
| } |
| |
| public Object visitImperativeLoopExp(ImperativeLoopExp imperativeLoopExp) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| public Object visitForExp(ForExp forExp) { |
| Object sourceValue = forExp.getSource().accept(getVisitor()); |
| |
| if (!isUndefined(sourceValue)) { |
| // generate a name for the result variable and add it to the environment |
| String resultName = generateName(); |
| getEvaluationEnvironment().add(resultName, null); |
| |
| Collection<?> sourceCollection = (Collection<?>) sourceValue; |
| // get the list of ocl iterators |
| List<Variable<EClassifier, EParameter>> iterators = forExp.getIterator(); |
| // get the condition expression |
| OCLExpression<EClassifier> condition = forExp.getCondition(); |
| // get the body expression |
| OCLExpression<EClassifier> body = forExp.getBody(); |
| |
| // evaluate |
| QvtIterationTemplateForExp.getInstance(getVisitor()).evaluate( |
| sourceCollection, iterators, null, condition, body, resultName, "forOne".equals(forExp.getName())); //$NON-NLS-1$ |
| |
| // remove result name from environment |
| getEvaluationEnvironment().remove(resultName); |
| |
| } |
| |
| return null; |
| } |
| |
| public Object visitImperativeIterateExp(ImperativeIterateExp imperativeIterateExp) { |
| EClassifier sourceType = imperativeIterateExp.getSource().getType(); |
| |
| if (sourceType instanceof PredefinedType) { |
| Object sourceValue = imperativeIterateExp.getSource().accept(getVisitor()); |
| |
| // value of iteration expression is undefined if the source is |
| // null or OclInvalid |
| if (isUndefined(sourceValue)) { |
| return getOclInvalid(); |
| } |
| |
| // get initial result value based on the source type |
| @SuppressWarnings("unchecked") |
| CollectionType<EClassifier, EOperation> collType = (CollectionType<EClassifier, EOperation>) imperativeIterateExp.getSource().getType(); |
| |
| Object initResultVal = null; |
| if (imperativeIterateExp.getName().equals("xselect")) { //$NON-NLS-1$ |
| initResultVal = CollectionUtil.createNewCollection(collType.getKind()); |
| } else if (imperativeIterateExp.getName().equals("xcollect") //$NON-NLS-1$ |
| || imperativeIterateExp.getName().equals("collectselect")) { //$NON-NLS-1$ |
| initResultVal = ((collType instanceof SetType) || (collType instanceof BagType)) ? |
| CollectionUtil.createNewBag() : CollectionUtil.createNewSequence(); |
| } |
| |
| // generate a name for the result variable and add it to the environment |
| String resultName = generateName(); |
| getEvaluationEnvironment().add(resultName, initResultVal); |
| |
| Collection<?> sourceCollection = (Collection<?>) sourceValue; |
| // get the list of ocl iterators |
| List<Variable<EClassifier, EParameter>> iterators = imperativeIterateExp.getIterator(); |
| // get the target expression |
| Variable<EClassifier, EParameter> target = imperativeIterateExp.getTarget(); |
| // get the condition expression |
| OCLExpression<EClassifier> condition = imperativeIterateExp.getCondition(); |
| // get the body expression |
| OCLExpression<EClassifier> body = imperativeIterateExp.getBody(); |
| |
| // evaluate |
| Object result = null; |
| if ("xcollect".equals(imperativeIterateExp.getName())) { //$NON-NLS-1$ |
| result = QvtIterationTemplateXCollect.getInstance(getVisitor()).evaluate( |
| sourceCollection, iterators, target, condition, body, resultName, false); |
| } else if ("xselect".equals(imperativeIterateExp.getName())) { //$NON-NLS-1$ |
| result = QvtIterationTemplateXSelect.getInstance(getVisitor()).evaluate( |
| sourceCollection, iterators, target, condition, body, resultName, false); |
| } else if ("collectselect".equals(imperativeIterateExp.getName())) { //$NON-NLS-1$ |
| result = QvtIterationTemplateCollectSelect.getInstance(getVisitor()).evaluate( |
| sourceCollection, iterators, target, condition, body, resultName, false); |
| } else if ("collectOne".equals(imperativeIterateExp.getName())) { //$NON-NLS-1$ |
| result = QvtIterationTemplateXCollect.getInstance(getVisitor()).evaluate( |
| sourceCollection, iterators, target, condition, body, resultName, true); |
| } else if ("selectOne".equals(imperativeIterateExp.getName())) { //$NON-NLS-1$ |
| result = QvtIterationTemplateXSelect.getInstance(getVisitor()).evaluate( |
| sourceCollection, iterators, target, condition, body, resultName, true); |
| } else if ("collectselectOne".equals(imperativeIterateExp.getName())) { //$NON-NLS-1$ |
| result = QvtIterationTemplateCollectSelect.getInstance(getVisitor()).evaluate( |
| sourceCollection, iterators, target, condition, body, resultName, true); |
| } |
| |
| // remove result name from environment |
| getEvaluationEnvironment().remove(resultName); |
| |
| return result; |
| } |
| |
| String message = OCLMessages.bind(OCLMessages.IteratorNotImpl_ERROR_, imperativeIterateExp.getName()); |
| throw new UnsupportedOperationException(message); |
| } |
| |
| private static synchronized String generateName() { |
| return "__qvtresult__" + tempCounter++;//$NON-NLS-1$ |
| } |
| |
| /** |
| * Initializes the model parameters and properties of the given module |
| * instance and all instances associated via import. |
| * |
| * @param moduleInstance |
| * the instance to initialize |
| * @param modelParameters |
| * helper for binding transformation parameters |
| */ |
| private void initModule(ModuleInstance moduleInstance, ModelParameterHelper modelParameters) { |
| Module type = moduleInstance.getModule(); |
| QvtOperationalEnv env = (QvtOperationalEnv) getEnvironment(); |
| QvtOperationalEvaluationEnv currentEvalEnv = getOperationalEvaluationEnv(); |
| |
| QvtOperationalEvaluationEnv nestedEvalEnv = (QvtOperationalEvaluationEnv) env.getFactory().createEvaluationEnvironment(getOperationalEvaluationEnv()); |
| // ensure 'this' instance available in the initialization code |
| nestedEvalEnv.add(QvtOperationalEnv.THIS, moduleInstance); |
| // propagate arguments, provided it is a transformation module |
| nestedEvalEnv.getOperationArgs().addAll(currentEvalEnv.getOperationArgs()); |
| // point to the module we are initializing |
| nestedEvalEnv.getAdapter(InternalEvaluationEnv.class).setCurrentIP(type); |
| try { |
| setOperationalEvaluationEnv(nestedEvalEnv); |
| // eventually cause STO exception |
| EvaluationUtil.checkCurrentStackDepth(currentEvalEnv); |
| pushedStack(nestedEvalEnv); |
| |
| for (ModuleImport moduleImport : type.getModuleImport()) { |
| Module importedModule = moduleImport.getImportedModule(); |
| ModuleInstance importedInstance = moduleInstance.getThisInstanceOf(importedModule); |
| |
| ModuleInstance.Internal importedInternal = importedInstance.getAdapter(ModuleInstance.Internal.class); |
| if(!importedInternal.isInitialized()) { |
| initModule(importedInstance, modelParameters); |
| } |
| } |
| |
| doInitModule(moduleInstance, modelParameters); |
| moduleInstance.getAdapter(ModuleInstance.Internal.class).setInitialized(); |
| |
| } finally { |
| setOperationalEvaluationEnv(getOperationalEvaluationEnv().getParent()); |
| poppedStack(); |
| } |
| } |
| |
| private void doInitModule(ModuleInstance moduleInstance, ModelParameterHelper modelParameters) { |
| Module module = moduleInstance.getModule(); |
| |
| QvtOperationalEvaluationEnv env = getOperationalEvaluationEnv(); |
| |
| if(modelParameters != null && module.eClass() == ExpressionsPackage.eINSTANCE.getOperationalTransformation()) { |
| modelParameters.initModelParameters((TransformationInstance) moduleInstance); |
| } |
| |
| for (EStructuralFeature feature : module.getConfigProperty()) { |
| Object propValue = visitConfigProperty(feature); |
| env.callSetter(moduleInstance, feature, propValue, isUndefined(propValue), true); |
| } |
| |
| for (EStructuralFeature feature : module.getEStructuralFeatures()) { |
| if(feature instanceof ContextualProperty || module.getConfigProperty().contains(feature)) { |
| continue; |
| } |
| |
| setCurrentEnvInstructionPointer(feature); |
| |
| OCLExpression<EClassifier> initExp = QvtOperationalParserUtil.getInitExpression(feature); |
| Object propValue = null; |
| if(initExp != null) { |
| propValue = initExp.accept(getVisitor()); |
| } |
| // FIXME - should not be set if no init expression is defined |
| env.callSetter(moduleInstance, feature, propValue, isUndefined(propValue), true); |
| } |
| |
| // for (Rename rename : module.getOwnedRenaming()) { |
| // ((RenameImpl) rename).accept(getVisitor()); |
| // } |
| } |
| |
| private IVirtualOperationTable getVirtualTable(EOperation operation) { |
| return IVirtualOperationTable.Access.INSTANCE.getVirtualTable(operation); |
| } |
| |
| private Object createOrGetResult(MappingOperation mappingOperation) { |
| Object result = getRuntimeValue(Environment.RESULT_VARIABLE_NAME); |
| |
| if (isUndefined(result)) { // if nothing was assigned to the result in the init section |
| EList<VarParameter> resultParams = mappingOperation.getResult(); |
| if(resultParams.size() > 1) { |
| result = createTupleResult(mappingOperation); |
| } |
| else { |
| VarParameter type = (resultParams.isEmpty() ? null : resultParams.get(0)); |
| if (type != null && false == type.getEType() instanceof VoidType) { |
| result = createInstance(type.getEType(), ((MappingParameter) type).getExtent()); |
| } |
| } |
| |
| replaceInEnv(Environment.RESULT_VARIABLE_NAME, result, mappingOperation.getEType()); |
| } |
| TraceUtil.addTraceRecord(getOperationalEvaluationEnv(), mappingOperation); |
| return result; |
| } |
| |
| static class OperationCallResult { |
| public Object myResult; |
| public QvtOperationalEvaluationEnv myEvalEnv; |
| |
| OperationCallResult(Object myResult, QvtOperationalEvaluationEnv myEvalEnv) { |
| this.myResult = myResult; |
| this.myEvalEnv = myEvalEnv; |
| } |
| } |
| |
| private static class MappingCallResult extends OperationCallResult { |
| static final int BODY_EXECUTED = 0; |
| static final int PRECOND_FAILED = 2; |
| static final int FETCHED_FROM_TRACE = 4; |
| static final int NO_DISJUCT_SELECTED = 8; |
| |
| int myStatus; |
| |
| private MappingCallResult(Object myResult, QvtOperationalEvaluationEnv myEvalEnv, int status) { |
| super(myResult, myEvalEnv); |
| myStatus = status; |
| } |
| boolean isBodyExecuted() { return myStatus == BODY_EXECUTED; } |
| boolean isPreconditionFailed() { return (myStatus & PRECOND_FAILED) != 0; }; |
| boolean isFetchedFromTrace() { return (myStatus & FETCHED_FROM_TRACE) != 0; }; |
| boolean isNoDisjunctSelected() { return (myStatus & NO_DISJUCT_SELECTED) != 0; }; |
| } |
| |
| private OperationCallResult executeImperativeOperation(ImperativeOperation method, Object source, List<Object> args, boolean isReusingMappingCall) { |
| if(method.isIsBlackbox()) { |
| //getEvaluationEnvironment().callOperation(method, -1, source, args); |
| } |
| |
| boolean isMapping = method instanceof MappingOperation; |
| QvtOperationalEvaluationEnv oldEvalEnv = getOperationalEvaluationEnv(); |
| // eventually cause STO exception |
| EvaluationUtil.checkCurrentStackDepth(oldEvalEnv); |
| |
| // create a nested evaluation environment for this operation call |
| QvtOperationalEvaluationEnv nestedEnv = getOperationalEnv().getFactory().createEvaluationEnvironment( |
| oldEvalEnv.getContext(), oldEvalEnv); |
| nestedEnv.setOperation(method); |
| |
| nestedEnv.getOperationArgs().addAll(args); |
| if (!isUndefined(source)) { |
| nestedEnv.setOperationSelf(source); |
| } |
| |
| if(isReusingMappingCall) { |
| // let the reused mapping see the reusing caller's out/result parameters |
| EvaluationUtil.mapOperationOutAndResultParams(oldEvalEnv, nestedEnv); |
| } |
| |
| // setup 'this' module variable |
| Module targetModule = QvtOperationalParserUtil.getOwningModule(method); |
| assert targetModule != null; |
| // Resolves the target module instance to call from the currently executed module 'this' |
| ModuleInstance calledThisInstance = oldEvalEnv.getThisOfType(targetModule); |
| assert calledThisInstance != null; |
| |
| setOperationalEvaluationEnv(nestedEnv); |
| // add 'this' to the nested environment |
| addToEnv(QvtOperationalEnv.THIS, calledThisInstance, targetModule); |
| |
| // set IP initially to the method header |
| setCurrentEnvInstructionPointer(method); |
| |
| OperationCallResult callResult = null; |
| try { |
| Object result = ((ImperativeOperationImpl) method).accept(getVisitor()); |
| assert result instanceof OperationCallResult; |
| assert !isMapping || result instanceof MappingCallResult; |
| |
| callResult = (OperationCallResult)result; |
| } |
| catch (ReturnExpEvent e) { |
| callResult = e.getResult(); |
| } |
| catch (StackOverflowError e) { |
| throwQVTException(new QvtStackOverFlowError(e)); |
| } |
| catch (QvtRuntimeException e) { |
| throw e; |
| } |
| catch (RuntimeException e) { |
| if(canBePropagated(e)) { |
| throw e; |
| } else { |
| QvtPlugin.log(e); |
| } |
| if (e.getLocalizedMessage() != null) { |
| throwQVTException(new QvtRuntimeException(e.getLocalizedMessage())); |
| } |
| else { |
| throwQVTException(new QvtRuntimeException(e)); |
| } |
| } |
| finally { |
| if(isMapping && isReusingMappingCall && callResult != null) { |
| // reflect our output in the reusing mapping caller |
| if(((MappingCallResult)callResult).isBodyExecuted()) { |
| EvaluationUtil.mapOperationOutAndResultParams(nestedEnv, oldEvalEnv); |
| } |
| } |
| |
| setOperationalEvaluationEnv(oldEvalEnv); |
| poppedStack(); |
| } |
| |
| return callResult; |
| } |
| |
| private MappingCallResult dispatchDisjuctMapping(MappingOperation method) { |
| QvtOperationalEvaluationEnv evalEnv = getOperationalEvaluationEnv(); |
| Object source = evalEnv.getOperationSelf(); |
| List<Object> args = evalEnv.getOperationArgs(); |
| |
| for (MappingOperation nextDisjunct : method.getDisjunct()) { |
| EClassifier ctxType = QvtOperationalParserUtil.getContextualType(nextDisjunct); |
| if(ctxType != null) { |
| if(!evalEnv.isKindOf(source, nextDisjunct.getContext().getEType())) { |
| continue; |
| } |
| } |
| |
| EList<EParameter> params = nextDisjunct.getEParameters(); |
| if(params.size() != args.size()) { |
| continue; |
| } |
| |
| for (int i = 0; i < args.size(); i++) { |
| Object nextArg = args.get(i); |
| EClassifier nextParamType = params.get(i).getEType(); |
| if(!evalEnv.isKindOf(nextArg, nextParamType)) { |
| continue; |
| } |
| } |
| |
| MappingCallResult result = (MappingCallResult)executeImperativeOperation(nextDisjunct, source, args, false); |
| if(!result.isPreconditionFailed()) { |
| // precondition holds, mapping either executed, fetched from trace, or disjuncted |
| result.myStatus = MappingCallResult.BODY_EXECUTED; // from disjuncting mapping consider as executed |
| return result; |
| } |
| } |
| |
| return new MappingCallResult(null, myEvalEnv, MappingCallResult.NO_DISJUCT_SELECTED); |
| } |
| |
| |
| /** |
| * Subclasses may indicate whether the given runtime exception caught is known and |
| * should be propagated. |
| */ |
| protected boolean canBePropagated(RuntimeException exception) { |
| // Allow the return event to be propagated from Essential OCL expressions |
| // The current m2m QVT concrete syntax does not allow this but in principal, |
| // the QVT specification does not prohibit this |
| return exception instanceof ReturnExpEvent; |
| } |
| |
| protected final void throwQVTException(QvtRuntimeException exception) throws QvtRuntimeException { |
| getOperationalEvaluationEnv().getAdapter(InternalEvaluationEnv.class) |
| .throwQVTException(exception); |
| } |
| |
| @SuppressWarnings("unchecked") //$NON-NLS-1$ |
| @Override |
| protected Object call(EOperation operation, OCLExpression<EClassifier> body, Object target, Object[] args) { |
| if(target instanceof EObject) { |
| EObject eTarget = (EObject) target; |
| if(OCLAnnotationSupport.isDynamicInstance(eTarget)) { |
| if(operation.eClass() != eTarget.eClass()) { |
| // check if not overriden for a sub-class |
| EOperation actualOperation = getOCLAnnotationSupport().resolveDynamic(operation, eTarget); |
| if(actualOperation != null && actualOperation != operation) { |
| OCLExpression<EClassifier> actualOperBody = getOperationBody(actualOperation); |
| |
| if(actualOperBody != null) { |
| Environment myEnv = getEnvironment(); |
| EnvironmentFactory factory = myEnv.getFactory(); |
| // create a nested evaluation environment for this operation call |
| EvaluationEnvironment nested = factory.createEvaluationEnvironment(getEvaluationEnvironment()); |
| // bind "self" |
| nested.add(Environment.SELF_VARIABLE_NAME, target); |
| // add the parameter bindings to the local variables |
| if (args.length > 0) { |
| int i = 0; |
| UMLReflection<?, ?, EOperation, ?, ?, EParameter, ?, ?, ?, ?> uml = myEnv.getUMLReflection(); |
| for (EParameter param : uml.getParameters(operation)) { |
| nested.add(uml.getName(param), args[i]); |
| } |
| } |
| |
| EvaluationVisitor visitor = factory.createEvaluationVisitor(myEnv, nested, getExtentMap()); |
| if(visitor instanceof QvtOperationalEvaluationVisitorImpl) { |
| // ensure shared instance of oclAnnotationSupport to avoid repeated OCL parsing |
| ((QvtOperationalEvaluationVisitorImpl)visitor).oclAnnotationSupport = getOCLAnnotationSupport(); |
| } |
| |
| return visitor.visitExpression(actualOperBody); |
| } |
| } |
| } |
| } |
| } |
| |
| return super.call(operation, body, target, args); |
| } |
| |
| @SuppressWarnings("unchecked") //$NON-NLS-1$ |
| @Override |
| protected Object navigate(EStructuralFeature property, OCLExpression<EClassifier> derivation, Object target) { |
| Environment myEnv = getEnvironment(); |
| EnvironmentFactory factory = myEnv.getFactory(); |
| // create a nested evaluation environment for this property call |
| EvaluationEnvironment nested = factory.createEvaluationEnvironment(getEvaluationEnvironment()); |
| // bind "self" |
| nested.add(Environment.SELF_VARIABLE_NAME, target); |
| |
| EvaluationVisitor visitor = factory.createEvaluationVisitor(myEnv, nested, getExtentMap()); |
| if(visitor instanceof QvtOperationalEvaluationVisitorImpl) { |
| // ensure shared instance of oclAnnotationSupport to avoid repeated OCL parsing |
| ((QvtOperationalEvaluationVisitorImpl)visitor).oclAnnotationSupport = getOCLAnnotationSupport(); |
| } |
| |
| return visitor.visitExpression(derivation); |
| } |
| |
| @Override |
| protected OCLExpression<EClassifier> getPropertyBody(EStructuralFeature property) { |
| if(OCLAnnotationSupport.isDynamicClassFeature(property)) { |
| return getOCLAnnotationSupport().getDerivedProperty(property); |
| } |
| |
| return super.getPropertyBody(property); |
| } |
| |
| @Override |
| protected OCLExpression<EClassifier> getOperationBody(EOperation operation) { |
| if(operation != null && OCLAnnotationSupport.isDynamicClassOperation(operation)) { |
| return getOCLAnnotationSupport().getBody(operation); |
| } |
| |
| return super.getOperationBody(operation); |
| } |
| |
| private OCLAnnotationSupport getOCLAnnotationSupport() { |
| if(oclAnnotationSupport == null) { |
| oclAnnotationSupport = new OCLAnnotationSupport(); |
| |
| oclAnnotationSupport.setErrorHandler(new OCLAnnotationSupport.ParseErrorHandler() { |
| org.eclipse.ocl.ecore.OCLExpression invalidBodyExpr = EcoreFactory.eINSTANCE.createInvalidLiteralExp(); |
| |
| public org.eclipse.ocl.ecore.OCLExpression handleError(ParserException parserException, EModelElement contextElement) { |
| QvtPlugin.log(QvtPlugin.createErrorStatus("Failed to parse OCL annotation :" + //$NON-NLS-1$ |
| getUMLReflection().getQualifiedName(contextElement) , parserException)); |
| |
| return invalidBodyExpr; |
| } |
| }); |
| } |
| return oclAnnotationSupport; |
| } |
| |
| protected QvtOperationalEnv getOperationalEnv() { |
| return (QvtOperationalEnv) getEnvironment(); |
| } |
| |
| public QvtOperationalEvaluationEnv getOperationalEvaluationEnv() { |
| return (QvtOperationalEvaluationEnv) getEvaluationEnvironment(); |
| } |
| |
| |
| /** |
| * Adds the given variable value into evaluation environment. |
| * |
| * @param varName |
| * the name of the variable |
| * @param value |
| * the value of the variable |
| * @param declaredType |
| * the type of the variable (optional) or <code>null</code> |
| */ |
| protected void addToEnv(String varName, Object value, EClassifier declaredType) { |
| getOperationalEvaluationEnv().add(varName, value); |
| } |
| |
| /** |
| * Replaces the given variable value in evaluation environment. |
| * |
| * @param varName |
| * the name of the variable to replace |
| * @param value |
| * the new value of the variable |
| * @param declaredType |
| * the type of the variable (optional) or <code>null</code> |
| */ |
| protected void replaceInEnv(String varName, Object value, EClassifier declaredType) { |
| getOperationalEvaluationEnv().replace(varName, value); |
| } |
| |
| private Object getRuntimeValue(final String name) { |
| return getEvaluationEnvironment().getValueOf(name); |
| } |
| |
| private Object getOutOwner(final ObjectExp objectExp) { |
| Object owner = getRuntimeValue(objectExp.getName()); |
| if (owner != null) { |
| if (objectExp.getType() instanceof CollectionType == false) { |
| if (!oclIsKindOf(owner, objectExp.getType())) { |
| throw new RuntimeException(MessageFormat.format( |
| EvaluationMessages.ExtendedOclEvaluatorVisitorImpl_InvalidObjectExpType, new Object[] { |
| objectExp.getName(), owner, objectExp.getType() })); |
| } |
| } |
| } else { |
| owner = createInstance(objectExp.getType(), objectExp.getExtent()); |
| if(objectExp.getName() != null) { |
| getOperationalEvaluationEnv().replace(objectExp.getName(), owner); |
| } |
| } |
| |
| return owner; |
| } |
| |
| /** |
| * Creates tuple value representing the result of the given operation. |
| * |
| * @param operation |
| * the operation currently executed by this environment |
| * |
| * @return the tuple value collecting all result parameters, never <code>null</code> |
| */ |
| private Tuple<EOperation, EStructuralFeature> createTupleResult(ImperativeOperation operation) { |
| boolean isMapping = operation.eClass() == ExpressionsPackage.eINSTANCE.getMappingOperation(); |
| QvtOperationalEvaluationEnv evalEnv = getOperationalEvaluationEnv(); |
| EList<VarParameter> resultParams = operation.getResult(); |
| |
| HashMap<EStructuralFeature, Object> values = new HashMap<EStructuralFeature, Object>(2); |
| @SuppressWarnings("unchecked") |
| TupleType<EClassifier, EStructuralFeature> tupleType = (TupleType<EClassifier, EStructuralFeature>)operation.getEType(); |
| |
| for (EStructuralFeature tupleProp : tupleType.oclProperties()) { |
| Object propVal = evalEnv.getValueOf(tupleProp.getName()); |
| if(propVal == null) { |
| ModelParameter extent = null; |
| for (VarParameter resultParam : resultParams) { |
| if(tupleProp.getName().equals(resultParam.getName())) { |
| MappingParameter mappingParameter = (MappingParameter) resultParam; |
| extent = mappingParameter.getExtent(); |
| break; |
| } |
| } |
| |
| if(isMapping) { |
| propVal = createInstance(tupleProp.getEType(), extent); |
| } |
| } |
| values.put(tupleProp, propVal); |
| evalEnv.replace(tupleProp.getName(), propVal, tupleProp.getEType()); |
| } |
| |
| return evalEnv.createTuple(operation.getEType(), values); |
| } |
| |
| private static CollectionKind getCollectionKind(Collection<?> collection) { |
| if (collection instanceof ArrayList) { |
| return CollectionKind.SEQUENCE_LITERAL; |
| } |
| if (collection instanceof LinkedHashSet) { |
| return CollectionKind.ORDERED_SET_LITERAL; |
| } |
| // Remark: check Set after Ordered set |
| if (collection instanceof HashSet) { |
| return CollectionKind.SET_LITERAL; |
| } |
| if (collection instanceof Bag) { |
| return CollectionKind.BAG_LITERAL; |
| } |
| return null; |
| } |
| |
| private List<Object> makeArgs(OperationCallExp<EClassifier, EOperation> operationCallExp) { |
| List<Object> argValues = new ArrayList<Object>(); |
| for (OCLExpression<EClassifier> arg : operationCallExp.getArgument()) { |
| Object value = arg.accept(getVisitor()); |
| argValues.add(value); |
| } |
| |
| return argValues; |
| } |
| |
| private List<Object> makeEntryOperationArgs(ImperativeOperation entryPoint, OperationalTransformation module) { |
| List<Object> args = new ArrayList<Object>(entryPoint.getEParameters().size()); |
| |
| int paramIndex = 0; |
| for (EParameter param : entryPoint.getEParameters()) { |
| int matchedIndex = paramIndex; |
| |
| MappingParameter mappingParam = (MappingParameter) param; |
| if (mappingParam.getKind() == DirectionKind.OUT) { |
| args.add(null); |
| continue; |
| } |
| |
| if (mappingParam.getExtent() != null) { |
| int modelParamIndex = 0; |
| for (ModelParameter modelParam : module.getModelParameter()) { |
| if (modelParam == mappingParam.getExtent()) { |
| matchedIndex = modelParamIndex; |
| break; |
| } |
| modelParamIndex++; |
| } |
| } |
| |
| if (matchedIndex < getOperationalEvaluationEnv().getOperationArgs().size()) { |
| Object envArg = getOperationalEvaluationEnv().getOperationArgs().get(matchedIndex); |
| ModelInstance argModel = (ModelInstance) envArg; |
| ModelParameterExtent argExtent = argModel.getExtent(); |
| List<EObject> initialObjects = argExtent.getInitialObjects(); |
| if(!initialObjects.isEmpty()) { |
| args.add(initialObjects.get(0)); |
| } |
| } |
| else { |
| throw new IllegalArgumentException("entry operation arguments mismatch: no argument for " + mappingParam + " parameter"); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| |
| paramIndex++; |
| } |
| return args; |
| } |
| |
| private EStructuralFeature getRenamedProperty(EStructuralFeature property) { |
| EAnnotation annotation = property.getEAnnotation(Environment.OCL_NAMESPACE_URI); |
| if (annotation != null) { |
| for (EObject nextAnn : annotation.getContents()) { |
| if (false == nextAnn instanceof Constraint) { |
| continue; |
| } |
| Constraint cnt = (Constraint) nextAnn; |
| if (QvtOperationalEnv.RENAMED_PROPERTY_STEREOTYPE.equals(cnt.getStereotype()) |
| && !cnt.getConstrainedElements().isEmpty() |
| && cnt.getConstrainedElements().get(0) instanceof EStructuralFeature) { |
| return (EStructuralFeature) cnt.getConstrainedElements().get(0); |
| } |
| } |
| } |
| return property; |
| } |
| |
| /** |
| * Wraps the environment's creatInstance() and transforms failures to QVT exception |
| */ |
| protected Object createInstance(EClassifier type, ModelParameter extent) throws QvtRuntimeException { |
| Object newInstance = null; |
| try { |
| if(type instanceof CollectionType) { |
| @SuppressWarnings("unchecked") |
| CollectionType<EClassifier, EOperation> collectionType = (CollectionType<EClassifier, EOperation>)type; |
| if(collectionType.getKind() == CollectionKind.COLLECTION_LITERAL) { |
| newInstance = CollectionUtil.createNewSequence(); |
| } else { |
| newInstance = CollectionUtil.createNewCollection(collectionType.getKind()); |
| } |
| } else { |
| newInstance = getOperationalEvaluationEnv().createInstance(type, extent); |
| if (IntermediateClassFactory.isIntermediateClass(type)) { |
| EPackage superPackage = type.getEPackage().getESuperPackage(); |
| assert superPackage instanceof Module; |
| Module intermPackage = (Module) superPackage; |
| IntermediateClassFactory.getFactory(intermPackage).doInstancePropertyInit(newInstance, getQVTVisitor()); |
| } |
| } |
| |
| } catch (IllegalArgumentException e) { |
| throwQVTException(new QvtRuntimeException(e)); |
| } |
| |
| return newInstance; |
| } |
| |
| private EObject setCurrentEnvInstructionPointer(EObject node) { |
| InternalEvaluationEnv internEnv = getOperationalEvaluationEnv().getAdapter(InternalEvaluationEnv.class); |
| if(node != null) { |
| return internEnv.setCurrentIP(node); |
| } |
| return internEnv.getCurrentIP(); |
| } |
| |
| private InternalEvaluator createInterruptibleVisitor() { |
| final EvaluationMonitor monitor = (EvaluationMonitor)getContext().getMonitor(); |
| |
| class InterruptVisitor extends QvtGenericEvaluationVisitor.Any implements InternalEvaluator { |
| public InterruptVisitor() { |
| super(QvtOperationalEvaluationVisitorImpl.this); |
| } |
| |
| public QvtOperationalEvaluationEnv getOperationalEvaluationEnv() { |
| return QvtOperationalEvaluationVisitorImpl.this.getOperationalEvaluationEnv(); |
| } |
| |
| public void setOperationalEvaluationEnv(QvtOperationalEvaluationEnv evalEnv) { |
| QvtOperationalEvaluationVisitorImpl.this.setOperationalEvaluationEnv(evalEnv); |
| } |
| |
| public IContext getContext() { |
| return QvtOperationalEvaluationVisitorImpl.this.getContext(); |
| } |
| |
| public ModuleInstance callTransformationImplicitConstructor(OperationalTransformation transformation, List<ModelInstance> args) { |
| return QvtOperationalEvaluationVisitorImpl.this.callTransformationImplicitConstructor(transformation, args); |
| } |
| |
| public OperationCallResult runMainEntry(OperationalTransformation transformation, List<Object> args) { |
| return QvtOperationalEvaluationVisitorImpl.this.runMainEntry(transformation, args); |
| } |
| |
| @Override |
| protected void genericVisitAny(Object object) { |
| if(monitor != null && monitor.isCanceled()) { |
| if(object instanceof ASTNode) { |
| throwQVTException(new QvtInterruptedExecutionException()); |
| } else { |
| throwQVTException(new QvtInterruptedExecutionException()); |
| } |
| } |
| } |
| }; |
| |
| return new InterruptVisitor(); |
| } |
| |
| public OperationCallResult runMainEntry(OperationalTransformation transformation, List<Object> args) { |
| ImperativeOperation entryOperation = QvtOperationalParserUtil.getMainOperation(transformation); |
| OperationCallResult result = executeImperativeOperation(entryOperation, null, args, false); |
| processDeferredTasks(); |
| return result; |
| } |
| |
| /** |
| * Performs cast on the result of {@link #getVisitor()}; |
| * @see #getVisitor() |
| */ |
| protected QvtOperationalEvaluationVisitor getQVTVisitor() { |
| return (QvtOperationalEvaluationVisitor)getVisitor(); |
| } |
| |
| private Object createFromString(final EClassifier type, final String stringValue) { |
| if(stringValue == null) { |
| return null; |
| } |
| |
| if (!QvtOperationalUtil.isCreateFromStringSupported(type)) { |
| return getOclInvalid(); |
| } |
| |
| // QVT primitive type |
| // FIXME - should rather used primitive type singletons from the Standard library |
| try { |
| if (type instanceof org.eclipse.ocl.ecore.PrimitiveType && PrimitiveType.INTEGER_NAME.equals(((org.eclipse.ocl.ecore.PrimitiveType) type).getName())) { |
| return new Integer(stringValue); |
| } |
| if (type instanceof org.eclipse.ocl.ecore.PrimitiveType && PrimitiveType.REAL_NAME.equals(((org.eclipse.ocl.ecore.PrimitiveType) type).getName())) { |
| return new Double(stringValue); |
| } |
| } catch (NumberFormatException e) { |
| return getOclInvalid(); |
| } |
| |
| if (type instanceof org.eclipse.ocl.ecore.PrimitiveType && PrimitiveType.STRING_NAME.equals(((org.eclipse.ocl.ecore.PrimitiveType) type).getName())) { |
| return new String(stringValue); |
| } |
| |
| if (type instanceof org.eclipse.ocl.ecore.PrimitiveType && PrimitiveType.BOOLEAN_NAME.equals(((org.eclipse.ocl.ecore.PrimitiveType) type).getName())) { |
| return Boolean.valueOf(stringValue); |
| } |
| // Enumeration |
| if (type instanceof EDataType) { |
| if(type.getEPackage() != null && type.getEPackage().getEFactoryInstance() != null) { |
| Object value = type.getEPackage().getEFactoryInstance().createFromString((EDataType) type, stringValue); |
| if (value != null) { |
| return value; |
| } |
| } |
| } |
| |
| return getOclInvalid(); |
| } |
| |
| /** |
| * TODO - Avoid using this exception return expression to interrupt execution flow for |
| * immediate return from an operation. |
| * Though, if the last statement in a body is return expression, no exception is thrown, we |
| * should try to avoid this cost to be paid in general |
| */ |
| private static class ReturnExpEvent extends RuntimeException { |
| private static final long serialVersionUID = 2971434369853642555L; |
| private final OperationCallResult fResult; |
| |
| ReturnExpEvent(Object returnValue, QvtOperationalEvaluationEnv evalEnv) { |
| fResult = new OperationCallResult(returnValue, evalEnv); |
| } |
| |
| public OperationCallResult getResult() { |
| return fResult; |
| } |
| } |
| } |