| /******************************************************************************* |
| * Copyright (c) 2007, 2015 Borland Software Corporation and others |
| * |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Borland Software Corporation - initial API and implementation |
| * Christopher Gerking - bugs 302594, 309762, 310991, 325192, 377882, 388325, |
| * 392080, 392153, 394498, 397215, 397218, 269744, |
| * 415660, 415315, 414642, 427237, 428618, 425069 |
| * Alex Paperno - bugs 294127, 416584, 419299, 267917, 420970, 424584 |
| * Christine Gerpheide - bugs 432969 |
| *******************************************************************************/ |
| 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.Collection; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.ListIterator; |
| |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.emf.common.notify.Adapter; |
| import org.eclipse.emf.common.util.EList; |
| import org.eclipse.emf.ecore.EClass; |
| import org.eclipse.emf.ecore.EClassifier; |
| import org.eclipse.emf.ecore.EEnumLiteral; |
| import org.eclipse.emf.ecore.EFactory; |
| 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.emf.ecore.util.EcoreUtil; |
| import org.eclipse.m2m.internal.qvt.oml.NLS; |
| import org.eclipse.m2m.internal.qvt.oml.QvtPlugin; |
| import org.eclipse.m2m.internal.qvt.oml.ast.binding.ASTBindingHelper; |
| import org.eclipse.m2m.internal.qvt.oml.ast.binding.IModuleSourceInfo; |
| 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.env.QvtOperationalModuleEnv; |
| import org.eclipse.m2m.internal.qvt.oml.ast.env.QvtOperationalStdLibrary; |
| import org.eclipse.m2m.internal.qvt.oml.ast.parser.ConstructorOperationAdapter; |
| import org.eclipse.m2m.internal.qvt.oml.ast.parser.IntermediateClassFactory; |
| import org.eclipse.m2m.internal.qvt.oml.ast.parser.IntermediateClassFactory.ExceptionClassInstance; |
| 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.blackbox.BlackboxRegistry; |
| import org.eclipse.m2m.internal.qvt.oml.emf.util.EmfUtil; |
| 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.Constructor; |
| import org.eclipse.m2m.internal.qvt.oml.expressions.ConstructorBody; |
| import org.eclipse.m2m.internal.qvt.oml.expressions.ContextualProperty; |
| 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.Helper; |
| import org.eclipse.m2m.internal.qvt.oml.expressions.ImperativeCallExp; |
| import org.eclipse.m2m.internal.qvt.oml.expressions.ImperativeOperation; |
| import org.eclipse.m2m.internal.qvt.oml.expressions.Library; |
| 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.ResolveExp; |
| import org.eclipse.m2m.internal.qvt.oml.expressions.ResolveInExp; |
| import org.eclipse.m2m.internal.qvt.oml.expressions.VarParameter; |
| 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.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.ConversionUtils; |
| import org.eclipse.m2m.internal.qvt.oml.stdlib.DictionaryImpl; |
| import org.eclipse.m2m.internal.qvt.oml.stdlib.MutableListImpl; |
| import org.eclipse.m2m.internal.qvt.oml.stdlib.model.ExceptionInstance; |
| import org.eclipse.m2m.internal.qvt.oml.trace.Trace; |
| import org.eclipse.m2m.internal.qvt.oml.trace.TraceRecord; |
| import org.eclipse.m2m.qvt.oml.blackbox.java.JavaModelExtent; |
| import org.eclipse.m2m.qvt.oml.blackbox.java.JavaModelInstance; |
| import org.eclipse.m2m.qvt.oml.blackbox.java.JavaModelType; |
| import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.AltExp; |
| import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.AssertExp; |
| import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.AssignExp; |
| import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.BlockExp; |
| import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.BreakExp; |
| import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.CatchExp; |
| import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.ComputeExp; |
| import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.ContinueExp; |
| import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.DictLiteralExp; |
| import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.DictLiteralPart; |
| import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.ForExp; |
| import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.ImperativeIterateExp; |
| import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.ImperativeLoopExp; |
| import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.InstantiationExp; |
| import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.ListType; |
| import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.LogExp; |
| import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.OrderedTupleLiteralExp; |
| import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.OrderedTupleLiteralPart; |
| import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.RaiseExp; |
| import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.ReturnExp; |
| import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.SeverityKind; |
| import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.SwitchExp; |
| import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.TryExp; |
| import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.UnlinkExp; |
| import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.UnpackExp; |
| import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.VariableInitExp; |
| import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.WhileExp; |
| import org.eclipse.m2m.qvt.oml.util.Dictionary; |
| import org.eclipse.m2m.qvt.oml.util.IContext; |
| 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.EvaluationVisitorImpl; |
| 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.CollectionItem; |
| import org.eclipse.ocl.expressions.CollectionLiteralExp; |
| import org.eclipse.ocl.expressions.CollectionLiteralPart; |
| import org.eclipse.ocl.expressions.CollectionRange; |
| 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.types.BagType; |
| import org.eclipse.ocl.types.CollectionType; |
| import org.eclipse.ocl.types.SetType; |
| import org.eclipse.ocl.types.TupleType; |
| import org.eclipse.ocl.types.VoidType; |
| import org.eclipse.ocl.util.CollectionUtil; |
| import org.eclipse.ocl.util.Tuple; |
| import org.eclipse.ocl.utilities.PredefinedType; |
| import org.eclipse.ocl.utilities.UMLReflection; |
| |
| |
| 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 boolean myIsSuppressLoggin = false; |
| |
| 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 InternalEvaluator createNestedEvaluationVisitor(QvtOperationalEvaluationVisitorImpl parent, QvtOperationalEvaluationEnv nestedEvalEnv) { |
| InternalEvaluator evalVisitor = (InternalEvaluator) getOperationalEnv().getFactory().createEvaluationVisitor( |
| parent.getOperationalEnv(), nestedEvalEnv, nestedEvalEnv.createExtentMap(null)); |
| if (evalVisitor instanceof QvtOperationalEvaluationVisitorImpl) { |
| // ensure shared instance of oclAnnotationSupport to avoid repeated OCL parsing |
| ((QvtOperationalEvaluationVisitorImpl) evalVisitor).oclAnnotationSupport = getOCLAnnotationSupport(); |
| } |
| return evalVisitor; |
| } |
| |
| public Object visitDictLiteralExp(DictLiteralExp dictLiteralExp) { |
| Dictionary<Object, Object> result = new DictionaryImpl<Object, Object>(); |
| for (DictLiteralPart part : dictLiteralExp.getPart()) { |
| Object key = visitExpression(part.getKey()); |
| Object value = visitExpression(part.getValue()); |
| if(key != getInvalid() && value != getInvalid()) |
| 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 condVal = visitExpression(condition); |
| if (isUndefined(condVal)) { |
| return getInvalid(); |
| } |
| |
| Boolean condValBool = (Boolean) condVal; |
| if (condValBool.booleanValue()) { |
| return visitExpression(ie.getThenExpression()); |
| } |
| if (ie.getElseExpression() != null) { |
| return visitExpression(ie.getElseExpression()); |
| } |
| return null; |
| } |
| |
| @SuppressWarnings("unchecked") |
| 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 = lvalueExp.getReferredProperty(); |
| internEnv |
| .setLastAssignmentLvalueEval(new EObjectEStructuralFeaturePair((EObject) ownerObj, referredProperty)); |
| } |
| } |
| } |
| |
| Object exprValue = null; |
| for (OCLExpression<EClassifier> exp : assignExp.getValue()) { |
| exprValue = visitExpression(exp); |
| } |
| |
| 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<?, ?>) { |
| 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(); |
| |
| Object newValue = env.assign(variableType, oldValue, exprValue, assignExp.isIsReset()); |
| |
| replaceInEnv(varName, newValue, variableType); |
| } |
| } else if (lValue instanceof PropertyCallExp<?, ?>) { |
| Object ownerObj = getAssignExpLValueOwner(lValue); |
| if (ownerObj instanceof EObject) { |
| EObject oldIP = setCurrentEnvInstructionPointer(assignExp); |
| env.callSetter((EObject) ownerObj, |
| ((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 = visitExpression(sourceExp); |
| |
| // obtain correct owner for features of modules, which are possibly defined in an extended module that is not an explicit supertype (fixed by bug 302594/310991) |
| if (owner instanceof ModuleInstance) { |
| |
| ModuleInstance moduleInstance = (ModuleInstance) owner; |
| |
| Object property = ((PropertyCallExp<?,?>) lValue).getReferredProperty(); |
| |
| EClassifier containingClassifier = getUMLReflection().getOwningClassifier(property); |
| if (containingClassifier instanceof Module) { |
| owner = moduleInstance.getThisInstanceOf((Module) containingClassifier); |
| } |
| |
| } |
| |
| 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 (!context.getConfigProperties().containsKey(configProperty.getName())) { |
| //leave non-specified configuration property as undefined |
| //value = EvaluationUtil.createInitialValue(propertyType, getEnvironment().getOCLStandardLibrary(), getEvaluationEnvironment()); |
| } |
| else if(rawValue instanceof String && propertyType != getEnvironment().getOCLStandardLibrary().getString()) { |
| try { |
| value = ConversionUtils.createFromString(propertyType, (String) rawValue); |
| } |
| catch (IllegalArgumentException e) { |
| value = getInvalid(); |
| } |
| |
| } |
| |
| if(value == getInvalid()) { |
| // 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); |
| |
| if (helper.isIsBlackbox()) { |
| Object result = doVisitBlackboxOperation(helper); |
| return new OperationCallResult(result, getOperationalEvaluationEnv()); |
| } |
| |
| return new OperationCallResult(visitOperationBody(helper.getBody()), getOperationalEvaluationEnv()); |
| } |
| |
| public Object visitImperativeOperation(ImperativeOperation imperativeOperation) { |
| |
| 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(); |
| |
| if (param.getKind() != DirectionKind.OUT) { |
| 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; |
| } |
| |
| private Object doVisitBlackboxOperation(ImperativeOperation operation) { |
| |
| assert operation.isIsBlackbox() : "Blackbox operation expected"; //$NON-NLS-1$ |
| |
| QvtOperationalModuleEnv moduleEnv = ASTBindingHelper.resolveModuleEnvironment(operation.eContainer()); |
| |
| Collection<CallHandler> handlers = BlackboxRegistry.INSTANCE.getBlackboxCallHandler(operation, moduleEnv); |
| if (handlers.isEmpty()) { |
| throwQVTException(new QvtRuntimeException(NLS.bind(EvaluationMessages.NoBlackboxOperationFound, |
| QvtOperationalParserUtil.safeGetMappingQualifiedName(getOperationalEnv(), operation)))); |
| } |
| if (handlers.size() > 1) { |
| throwQVTException(new QvtRuntimeException(NLS.bind(EvaluationMessages.AmbiguousBlackboxOperationFound, |
| QvtOperationalParserUtil.safeGetMappingQualifiedName(getOperationalEnv(), operation)))); |
| } |
| |
| QvtOperationalEvaluationEnv evalEnv = getOperationalEvaluationEnv(); |
| return handlers.iterator().next().invoke(evalEnv.getThisOfType(QvtOperationalParserUtil.getOwningModule(operation)), evalEnv.getOperationSelf(), evalEnv.getOperationArgs().toArray(), evalEnv); |
| } |
| |
| public Object visitLibrary(Library library) { |
| return null; |
| } |
| |
| public Object visitLocalProperty(EStructuralFeature property) { |
| OCLExpression<EClassifier> initExp = QvtOperationalParserUtil.getInitExpression(property); |
| if(initExp != null) { |
| return visitExpression(initExp); |
| } |
| |
| return null; |
| } |
| |
| public Object visitContextualProperty(ContextualProperty contextualProperty) { |
| if(contextualProperty.getInitExpression() != null) { |
| return visitExpression(contextualProperty.getInitExpression()); |
| } |
| return null; |
| } |
| |
| private boolean isWhenPreconditionSatisfied(MappingOperation mappingOperation) { |
| for (OCLExpression<EClassifier> nextCond : mappingOperation.getWhen()) { |
| if(!Boolean.TRUE.equals(visitExpression(nextCond))) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| private boolean isWherePreconditionSatisfied(MappingOperation mappingOperation) { |
| if (false == mappingOperation.getWhere() instanceof BlockExp) { |
| return true; |
| } |
| for (OCLExpression<EClassifier> nextCond : ((BlockExp) mappingOperation.getWhere()).getBody()) { |
| if(!Boolean.TRUE.equals(visitExpression(nextCond))) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| private void setupInitialResultValue(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) { |
| Object initialValue = EvaluationUtil.createInitialValue(resultParam.getEType(), getStandardLibrary(), evalEnv); |
| replaceInEnv(resultParam.getName(), initialValue, resultParam.getEType()); |
| } |
| } |
| |
| if(operation.getResult().size() > 1) { |
| if(evalEnv.getValueOf(Environment.RESULT_VARIABLE_NAME) == null) { |
| Object initialValue = EvaluationUtil.createInitialValue(operation.getEType(), getStandardLibrary(), evalEnv); |
| replaceInEnv(Environment.RESULT_VARIABLE_NAME, initialValue, operation.getEType()); |
| } |
| } |
| } |
| |
| public Object visitMappingBody(MappingBody mappingBody) { |
| QvtOperationalEvaluationEnv evalEnv = getOperationalEvaluationEnv(); |
| MappingOperation currentMappingCalled = (MappingOperation) mappingBody.getOperation(); |
| |
| setupInitialResultValue(mappingBody); |
| |
| for (OCLExpression<EClassifier> initExp : mappingBody.getInitSection()) { |
| visitExpression(initExp); |
| } |
| |
| if(!mappingBody.getInitSection().isEmpty()) { |
| // setup a meaningful IP after init section to avoid pointing to the last expression of init |
| // section, so in case of error a proper error location can be computed |
| setCurrentEnvInstructionPointer(currentMappingCalled); |
| } |
| |
| Object result = createOrGetResult(currentMappingCalled); |
| createOutParams(currentMappingCalled); |
| setOutParamsValues(currentMappingCalled, myEvalEnv.getOperationArgs()); |
| TraceUtil.addTraceRecord(getOperationalEvaluationEnv(), currentMappingCalled); |
| |
| // call inherited mappings |
| if(!currentMappingCalled.getInherited().isEmpty()) { |
| for (MappingOperation extendedMapping : currentMappingCalled.getInherited()) { |
| |
| // consider overriding mapping |
| ImperativeOperation overridingOper = EvaluationUtil.getOverridingOperation(getOperationalEvaluationEnv(), extendedMapping); |
| if (overridingOper instanceof MappingOperation) { |
| extendedMapping = (MappingOperation) overridingOper; |
| } |
| |
| executeImperativeOperation(extendedMapping, evalEnv.getOperationSelf(), evalEnv.getOperationArgs(), true); |
| } |
| } |
| |
| visitOperationBody(mappingBody); |
| |
| // TODO investigate possibility to modify result |
| for (OCLExpression<EClassifier> endExp : mappingBody.getEndSection()) { |
| visitExpression(endExp); |
| } |
| |
| // call merged mappings |
| callMergedMappings(currentMappingCalled, evalEnv); |
| |
| return result; |
| } |
| |
| private void callMergedMappings(MappingOperation mappingOperation, QvtOperationalEvaluationEnv evalEnv) { |
| for (MappingOperation extendedMapping : mappingOperation.getMerged()) { |
| // consider overriding mapping |
| ImperativeOperation overridingOper = EvaluationUtil.getOverridingOperation(getOperationalEvaluationEnv(), extendedMapping); |
| if (overridingOper instanceof MappingOperation) { |
| extendedMapping = (MappingOperation) overridingOper; |
| } |
| |
| executeImperativeOperation(extendedMapping, evalEnv.getOperationSelf(), evalEnv.getOperationArgs(), true); |
| } |
| } |
| |
| 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 reflect 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 = visitExpression(operationCallExp.getSource()); |
| if (((ImperativeOperation) referredOperation).getContext() != null) { |
| source = EvaluationUtil.doImplicitListCoercion( |
| ((ImperativeOperation) referredOperation).getContext().getEType(), source); |
| } |
| List<Object> args = makeArgs(operationCallExp); |
| // does not make sense continue at all, call on invalid results in invalid |
| if(source == getInvalid()) { |
| return source; |
| } |
| |
| 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) { |
| |
| if (QvtOperationalParserUtil.isContextual(method) && source == null) { |
| if (QvtOperationalParserUtil.getContextualType(method) != getStandardLibrary().getOclVoid()) { |
| return getInvalid(); |
| } |
| } |
| |
| if(operationCallExp instanceof ImperativeCallExp) { |
| ImperativeCallExp imperativeCall = (ImperativeCallExp) operationCallExp; |
| if(imperativeCall.isIsVirtual()) { |
| ImperativeOperation overridingOper = EvaluationUtil.getOverridingOperation(getOperationalEvaluationEnv(), method); |
| if(overridingOper != null) { |
| method = overridingOper; |
| } |
| } |
| } |
| |
| OperationCallResult opResult = executeImperativeOperation(method, source, args, false); |
| if (operationCallExp instanceof MappingCallExp && opResult instanceof MappingCallResult |
| && method instanceof MappingOperation) { |
| if (((MappingCallExp) operationCallExp).isIsStrict() && ((MappingCallResult) opResult).isPreconditionFailed()) { |
| throwQVTException(new QvtAssertionFailed(NLS.bind(EvaluationMessages.MappingPreconditionFailed, |
| method.getName()))); |
| } |
| retrieveOutArgs(operationCallExp, ((MappingCallResult) opResult).myEvalEnv.getOperationArgs(), |
| (MappingOperation) method); |
| } |
| |
| return EvaluationUtil.doImplicitListCoercion(referredOperation.getEType(), opResult.myResult); |
| } |
| } |
| |
| Object result = null; |
| try { |
| result = super.visitOperationCallExp(operationCallExp); |
| } |
| catch (QvtRuntimeException e) { |
| throw e; |
| } |
| catch (RuntimeException ex) { |
| //Logger.getLogger().log(Logger.WARNING, "QvtEvaluator: failed to evaluate oclOperationCall", ex);//$NON-NLS-1$ |
| result = getInvalid(); |
| } |
| |
| 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) { |
| InternalEvaluationEnv internEnv = getOperationalEvaluationEnv().getAdapter(InternalEvaluationEnv.class); |
| EOperation oper = vTable.lookupActualOperation(sourceEClass, getEnvironment(), internEnv); |
| if(oper != null) { |
| actualOperation = oper; |
| } |
| } |
| } |
| return actualOperation; |
| } |
| |
| @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); |
| } |
| |
| MappingCallResult result = executeMappingBody(mappingOperation, evalEnv); |
| if (!isWherePreconditionSatisfied(mappingOperation)) { |
| throwQVTException(new QvtAssertionFailed(NLS.bind(EvaluationMessages.MappingPostconditionFailed, |
| mappingOperation.getName()))); |
| } |
| return result; |
| } |
| |
| protected MappingCallResult executeMappingBody(MappingOperation mappingOperation, QvtOperationalEvaluationEnv evalEnv) { |
| // 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); |
| } |
| |
| if(!mappingOperation.getDisjunct().isEmpty()) { |
| return dispatchDisjunctMapping(mappingOperation); |
| } |
| |
| if (mappingOperation.isIsBlackbox()) { |
| Object result = doVisitBlackboxOperation(mappingOperation); |
| if (isUndefined(result)) { |
| throwQVTException(new QvtRuntimeException(NLS.bind(EvaluationMessages.BlackboxMappingFailedToAssignResult, |
| QvtOperationalParserUtil.safeGetMappingQualifiedName(getOperationalEnv(), mappingOperation)))); |
| } |
| |
| replaceInEnv(Environment.RESULT_VARIABLE_NAME, result, mappingOperation.getEType()); |
| TraceUtil.addTraceRecord(getOperationalEvaluationEnv(), mappingOperation); |
| |
| // call merged mappings |
| callMergedMappings(mappingOperation, evalEnv); |
| |
| return new MappingCallResult(result, evalEnv, MappingCallResult.BODY_EXECUTED); |
| } |
| |
| return new MappingCallResult(((OperationBodyImpl) mappingOperation.getBody()).accept(getVisitor()), evalEnv, |
| MappingCallResult.BODY_EXECUTED); |
| } |
| |
| public Object execute(OperationalTransformation transformation) throws QvtRuntimeException { |
| boolean isSuppressLoggin = myIsSuppressLoggin; |
| try { |
| myIsSuppressLoggin = true; |
| return transformation.accept(getVisitor()); |
| } finally { |
| myIsSuppressLoggin = isSuppressLoggin; |
| } |
| } |
| |
| 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) { |
| if (!myIsSuppressLoggin) { |
| StringWriter strWriter = new StringWriter(); |
| PrintWriter printWriter = new PrintWriter(strWriter); |
| e.printQvtStackTrace(printWriter); |
| |
| Log logger = getContext().getLog(); |
| logger.log(EvaluationMessages.TerminatingExecution); |
| logger.log(strWriter.getBuffer().toString()); |
| } |
| throw e; |
| } finally { |
| IntermediatePropertyModelAdapter.cleanup(module); |
| getOperationalEvaluationEnv().cleanup(); |
| } |
| } |
| |
| /** |
| * 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; |
| } |
| |
| @SuppressWarnings("unchecked") |
| public Object visitInstantiationExp(InstantiationExp objectExp) { |
| // should instantiate the module transformation |
| EClass _class = objectExp.getInstantiatedClass(); |
| if (_class instanceof OperationalTransformation) { |
| |
| EList<org.eclipse.ocl.ecore.OCLExpression> formalArguments = objectExp.getArgument(); |
| List<ModelInstance> actualArguments = new ArrayList<ModelInstance>(formalArguments.size()); |
| |
| for (OCLExpression<EClassifier> nextArg : formalArguments) { |
| Object argVal = visitExpression(nextArg); |
| if(argVal instanceof ModelInstance == false) { |
| throwQVTException(new QvtRuntimeException(EvaluationMessages.QvtOperationalEvaluationVisitorImpl_UndefModelParamInTransf)); |
| } |
| 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. |
| // bug 416584: Now we need to access configuration properties of non-entry modules |
| Context nestedContext = EvaluationUtil.createAggregatedContext(currentEnv); |
| |
| QvtOperationalEnvFactory envFactory = getOperationalEnv().getFactory(); |
| QvtOperationalEvaluationEnv nestedEvalEnv = envFactory.createEvaluationEnvironment(nestedContext, null); |
| |
| //bug 392153: reuse existing trace for nested environment |
| Trace trace = currentEnv.getAdapter(InternalEvaluationEnv.class).getTraces(); |
| nestedEvalEnv.getAdapter(InternalEvaluationEnv.class).setTraces(trace); |
| |
| // send arguments into the entry operation |
| nestedEvalEnv.getOperationArgs().addAll(actualArguments); |
| // Use per transformation instance visitor |
| InternalEvaluator nestedVisitor = createNestedEvaluationVisitor(this, nestedEvalEnv); |
| try { |
| setOperationalEvaluationEnv(nestedEvalEnv); |
| |
| ModuleInstance moduleInstance = nestedVisitor.callTransformationImplicitConstructor(targetTransf, actualArguments); |
| |
| return moduleInstance; |
| } finally { |
| setOperationalEvaluationEnv(currentEnv); |
| } |
| } |
| else { |
| Object owner = createInstance(objectExp.getType(), (ModelParameter) objectExp.getExtent()); |
| |
| EList<org.eclipse.ocl.ecore.OCLExpression> formalArguments = objectExp.getArgument(); |
| List<Object> actualArguments = new ArrayList<Object>(formalArguments.size()); |
| |
| for (OCLExpression<EClassifier> nextArg : formalArguments) { |
| Object argVal = visitExpression(nextArg); |
| actualArguments.add(argVal); |
| } |
| |
| Adapter adapter = EcoreUtil.getAdapter(objectExp.eAdapters(), ConstructorOperationAdapter.class); |
| if (adapter != null) { |
| Constructor constructorOp = ((ConstructorOperationAdapter) adapter).getReferredConstructor(); |
| |
| for (int i = 0, in = constructorOp.getEParameters().size(); i < in; ++i) { |
| actualArguments.set(i, EvaluationUtil.doImplicitListCoercion( |
| constructorOp.getEParameters().get(i).getEType(), actualArguments.get(i))); |
| } |
| |
| executeImperativeOperation(constructorOp, owner, actualArguments, false); |
| } |
| else { |
| if (objectExp.getArgument().isEmpty()) { |
| // it's OK since default constructor is called |
| } |
| else if (objectExp.getEType() instanceof CollectionType<?,?> && owner instanceof Collection<?>) { |
| ((Collection<Object>) owner).addAll(actualArguments); |
| } |
| else { |
| throwQVTException(new QvtRuntimeException("Undefined constructor is called")); //$NON-NLS-1$ |
| } |
| } |
| |
| return owner; |
| } |
| } |
| |
| 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); |
| |
| CallHandler handler = createTransformationHandler(); |
| InternalTransformation internTransf = instance.getAdapter(InternalTransformation.class); |
| internTransf.setTransformationHandler(handler); |
| |
| // we are initialized set back the pointer to the module |
| setCurrentEnvInstructionPointer(transformation); |
| return instance; |
| } |
| |
| private CallHandler createTransformationHandler() { |
| return new CallHandler() { |
| public OperationCallResult invoke(ModuleInstance moduleInstance, Object source, Object[] args, QvtOperationalEvaluationEnv evalEnv) { |
| assert (args.length == 0) : "transformation instance must be invoked with zero arguments"; |
| |
| TransformationInstance transformationInstance = (TransformationInstance) source; |
| |
| try { |
| evaluateModelParameterConditions(transformationInstance, evalEnv); |
| |
| OperationalTransformation transformation = transformationInstance.getTransformation(); |
| |
| if (transformation.isIsBlackbox()) { |
| QvtOperationalModuleEnv moduleEnv = ASTBindingHelper.resolveModuleEnvironment(transformationInstance.getModule()); |
| |
| Collection<CallHandler> handlers = BlackboxRegistry.INSTANCE.getBlackboxCallHandler( |
| transformation, moduleEnv); |
| if (handlers.isEmpty()) { |
| throwQVTException(new QvtRuntimeException(NLS.bind(EvaluationMessages.NoBlackboxOperationFound, |
| QvtOperationalParserUtil.safeGetQualifiedName(getOperationalEnv(), transformation)))); |
| } |
| if (handlers.size() > 1) { |
| throwQVTException(new QvtRuntimeException(NLS.bind(EvaluationMessages.AmbiguousBlackboxOperationFound, |
| QvtOperationalParserUtil.safeGetQualifiedName(getOperationalEnv(), transformation)))); |
| } |
| |
| List<Object> actualArgs = makeBlackboxTransformationArgs(transformationInstance, evalEnv); |
| Object result = handlers.iterator().next().invoke(transformationInstance, source, actualArgs.toArray(), evalEnv); |
| |
| return new OperationCallResult(result, evalEnv); |
| } |
| else { |
| return runMainEntry(transformation, makeEntryOperationArgs(transformation)); |
| } |
| } finally { |
| transformationInstance.getAdapter(InternalTransformation.class).dispose(); |
| } |
| } |
| }; |
| } |
| |
| // FIXME - review the strange case of having various return types |
| private Object doVisitTransformation(OperationalTransformation transformation) { |
| |
| QvtOperationalEvaluationEnv evaluationEnv = getOperationalEvaluationEnv(); |
| List<ModelInstance> modelArgs = EvaluationUtil.getTransfromationModelArguments(evaluationEnv, transformation); |
| |
| TransformationInstance moduleInstance = callTransformationImplicitConstructor(transformation, modelArgs); |
| |
| InternalTransformation internTransf = moduleInstance.getAdapter(InternalTransformation.class); |
| |
| CallHandler handler = internTransf.getTransformationHandler(); |
| |
| // call main entry operation or blackbox implementation |
| OperationCallResult callResult = (OperationCallResult) handler.invoke( |
| null, |
| moduleInstance, |
| Collections.emptyList().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( |
| Collections.singletonList((EObject) callResult.myResult), null, null); |
| evaluationEnv.addModelExtent(modelParameter); |
| evalResult.getModelExtents().add(modelParameter.getContents()); |
| } else { |
| return callResult.myResult; |
| } |
| } |
| |
| return evalResult; |
| } |
| |
| 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); |
| |
| if(objectExp.getBody() != null) { |
| EList<org.eclipse.ocl.ecore.OCLExpression> contents = objectExp.getBody().getContent(); |
| for (OCLExpression<EClassifier> exp : contents) { |
| visitExpression(exp); |
| } |
| } |
| |
| // owner may have changed in body content, so retrieve it again (fixed by bug 388325) |
| owner = getOutOwner(objectExp); |
| |
| 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 = visitExpression(valueExp); |
| } |
| // Wrap the result of the expression in an OperationCallResult object. |
| // This type breaks control flow, so the value arrives directly at its |
| // calling operation, where it is unwrapped again and returned. |
| return new OperationCallResult(value, getOperationalEvaluationEnv()); |
| } |
| |
| public Object visitOperationBody(OperationBody operationBody) { |
| Object result = null; |
| for (OCLExpression<EClassifier> exp : operationBody.getContent()) { |
| result = visitExpression(exp); |
| |
| // If control flow was broken (by means of a return statement, |
| // stop executing the next lines and return this result. |
| if(result instanceof BreakingResult) { |
| if(result instanceof OperationCallResult) { |
| result = ((OperationCallResult)result).myResult; |
| } |
| else { |
| result = null; |
| } |
| break; |
| } |
| } |
| |
| ImperativeOperation operation = operationBody.getOperation(); |
| |
| if(operation.getEType() == getStandardLibrary().getOclVoid()) { |
| result = null; |
| } |
| |
| if(operation.getResult().size() > 1) { |
| return createTupleResult(operation); |
| } |
| return result; |
| } |
| |
| public Object visitVarParameter(VarParameter varParameter) { |
| return null; |
| } |
| |
| public Object visitVariableInitExp(VariableInitExp variableInitExp) { |
| org.eclipse.ocl.ecore.Variable referredVariable = variableInitExp.getReferredVariable(); |
| OCLExpression<EClassifier> initExpression = referredVariable.getInitExpression(); |
| Object value = null; |
| if(initExpression != null) { |
| value = visitExpression(initExpression); |
| // check that collection is initialized to empty collection in case of 'null' |
| } else { |
| value = EvaluationUtil.createInitialValue(referredVariable.getType(), getEnvironment().getOCLStandardLibrary(), |
| getEvaluationEnvironment()); |
| } |
| |
| replaceInEnv(referredVariable.getName(), |
| EvaluationUtil.doImplicitListCoercion(variableInitExp.getType(), value), variableInitExp.getType()); |
| |
| return variableInitExp.isWithResult() ? value : null; |
| } |
| |
| public Object visitBlockExp(BlockExp blockExp) { |
| return visitBlockExpImpl(blockExp.getBody(), blockExp.eContainer() instanceof ImperativeOperation); |
| } |
| |
| private Object visitBlockExpImpl(EList<org.eclipse.ocl.ecore.OCLExpression> expList, boolean isInImperativeOper) { |
| List<String> scopeVars = null; |
| |
| Object result = null; |
| |
| for (OCLExpression<EClassifier> exp : expList) { |
| if((exp instanceof VariableInitExp) && !isInImperativeOper) { |
| if(scopeVars == null) { |
| scopeVars = new LinkedList<String>(); |
| } |
| VariableInitExp varInitExp = (VariableInitExp) exp; |
| scopeVars.add(varInitExp.getName()); |
| } |
| |
| result = visitExpression(exp); |
| if(result instanceof BreakingResult) { |
| break; |
| } |
| // Return null, unless control flow is broken (by break, continue or return). |
| // When control flow is broken, propagate this upward in the AST. |
| result = null; |
| } |
| |
| if(scopeVars != null) { |
| EvaluationEnvironment<EClassifier, EOperation, EStructuralFeature, EClass, EObject> evalEnv = getEvaluationEnvironment(); |
| for (String varName : scopeVars) { |
| evalEnv.remove(varName); |
| } |
| } |
| |
| return result; |
| } |
| |
| public Object visitComputeExp(ComputeExp computeExp) { |
| Variable<EClassifier, EParameter> returnedElement = computeExp.getReturnedElement(); |
| Object initExpressionValue = null; |
| OCLExpression<EClassifier> initExpression = returnedElement.getInitExpression(); |
| if (initExpression != null) { |
| initExpressionValue = visitExpression(initExpression); |
| } |
| replaceInEnv(returnedElement.getName(), initExpressionValue, returnedElement.getType()); |
| |
| Object result = visitExpression(computeExp.getBody()); |
| |
| Object returnedValue = getEvaluationEnvironment().remove(returnedElement.getName()); |
| |
| if(result instanceof BreakingResult) { |
| // Control flow was broken (break or continue). |
| // Instead of returning the value, propagate this. |
| return result; |
| } |
| |
| return returnedValue; |
| } |
| |
| public Object visitWhileExp(WhileExp whileExp) { |
| Object result = null; |
| while (true) { |
| Object condition = visitExpression(whileExp.getCondition()); |
| if (Boolean.TRUE.equals(condition)) { |
| result = visitExpression(whileExp.getBody()); |
| |
| if(result instanceof BreakingResult) { |
| // Control flow is being broken (break, continue, or return). |
| |
| if(result instanceof BreakResult) { |
| // Result must be null, unless it comes from a return statement. |
| result = null; |
| break; |
| } |
| if(result instanceof ContinueResult) { |
| // Instead of breaking out of the loop, continue with the next iteration. |
| result = null; |
| continue; |
| } |
| break; |
| } |
| } else { |
| break; |
| } |
| } |
| |
| return result; |
| } |
| |
| private static class SwitchAltExpResult { |
| public Object myCondition; |
| public Object myResult; |
| } |
| |
| public Object visitAltExp(AltExp switchAltExp) { |
| SwitchAltExpResult result = new SwitchAltExpResult(); |
| result.myCondition = visitExpression(switchAltExp.getCondition()); |
| if (Boolean.TRUE.equals(result.myCondition)) { |
| result.myResult = visitExpression(switchAltExp.getBody()); |
| } |
| return result; |
| } |
| |
| public Object visitSwitchExp(SwitchExp switchExp) { |
| for (AltExp altExp : switchExp.getAlternativePart()) { |
| Object altResult = visitExpression(altExp); |
| if (altResult instanceof SwitchAltExpResult) { |
| if (isUndefined(((SwitchAltExpResult) altResult).myCondition)) { |
| return getInvalid(); |
| } |
| 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 visitExpression(elsePart); |
| } |
| 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(), |
| (QvtOperationalEvaluationVisitor) getVisitor(), 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(), |
| (QvtOperationalEvaluationVisitor) getVisitor(), 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 String doVisitLogExp(LogExp logExp, Log logger, String messagePrefix) { |
| if(logExp.getCondition() != null && !Boolean.TRUE.equals(visitExpression(logExp.getCondition()))) { |
| return null; |
| } |
| Object invalid = 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 = visitExpression(args.get(2)); |
| level = NumberConversions.strictConvertNumber(levelObj, Integer.class); |
| } |
| |
| Object message = visitExpression(args.get(0)); |
| if(message == null) { |
| message = "<null>"; //$NON-NLS-1$ |
| } else if(message == invalid) { |
| message = invalidRepr; |
| } |
| |
| Object logEntry = message; |
| if(messagePrefix != null) { |
| logEntry = messagePrefix + " : " + message; //$NON-NLS-1$ |
| } |
| |
| Object element = null; |
| Object formatedElement = null; |
| if(args.size() > 1) { |
| element = visitExpression(args.get(1)); |
| if(element == invalid) { |
| formatedElement = invalidRepr; |
| } else { |
| formatedElement = EvaluationUtil.formatLoggedElement(element); |
| } |
| } |
| |
| if(level == null) { |
| if(element == null) { |
| logger.log(String.valueOf(logEntry)); |
| } else { |
| logger.log(String.valueOf(logEntry), formatedElement); |
| } |
| } else { |
| if(element == null) { |
| logger.log(level, String.valueOf(logEntry)); |
| } else { |
| logger.log(level, String.valueOf(logEntry), formatedElement); |
| } |
| } |
| |
| return message.toString(); |
| } |
| |
| public Object visitAssertExp(AssertExp assertExp) { |
| Object assertionValue = visitExpression(assertExp.getAssertion()); |
| |
| if(!Boolean.TRUE.equals(assertionValue)) { |
| InternalEvaluationEnv internEvalEnv = getOperationalEvaluationEnv().getAdapter(InternalEvaluationEnv.class); |
| Module currentModule = internEvalEnv.getCurrentModule().getModule(); |
| IModuleSourceInfo moduleSource = ASTBindingHelper.getModuleSourceBinding(currentModule); |
| |
| String source; |
| if(moduleSource != null) { |
| source = moduleSource.getSourceURI().lastSegment(); |
| } else { |
| source = EvaluationMessages.UknownSourceLabel; |
| } |
| |
| StringBuilder locationBuf = new StringBuilder(source); |
| if(assertExp.getStartPosition() >= 0 && moduleSource != null) { |
| int lineNum = moduleSource.getLineNumberProvider().getLineNumber(assertExp.getStartPosition()); |
| locationBuf.append(':').append(lineNum); |
| } |
| |
| String message = NLS.bind(EvaluationMessages.AssertFailedMessage, assertExp.getSeverity(), locationBuf.toString()); |
| Log logger = getContext().getLog(); |
| |
| String logMessage = null; |
| if(assertExp.getLog() != null) { |
| logMessage = doVisitLogExp(assertExp.getLog(), logger, message); |
| } else { |
| logger.log(message); |
| } |
| |
| if(SeverityKind.FATAL.equals(assertExp.getSeverity())) { |
| String msg = (logMessage == null ? EvaluationMessages.FatalAssertionFailed : logMessage); |
| throwQVTException(new QvtAssertionFailed(msg)); |
| } |
| |
| } |
| |
| return null; |
| } |
| |
| public Object visitImperativeLoopExp(ImperativeLoopExp imperativeLoopExp) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| public Object visitForExp(ForExp forExp) { |
| Object sourceValue = visitExpression(forExp.getSource()); |
| |
| 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 = visitExpression(imperativeIterateExp.getSource()); |
| |
| // value of iteration expression is undefined if the source is |
| // null or OclInvalid |
| if (isUndefined(sourceValue)) { |
| return getInvalid(); |
| } |
| |
| // 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 = EvaluationUtil.createNewCollectionOfSameKind((Collection<?>) sourceValue); |
| } 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 = NLS.bind(EvaluationMessages.IteratorNotImpl, imperativeIterateExp.getName()); |
| throw new UnsupportedOperationException(message); |
| } |
| |
| public Object visitConstructor(Constructor constructor) { |
| visitImperativeOperation(constructor); |
| |
| QvtOperationalEvaluationEnv env = getOperationalEvaluationEnv(); |
| |
| if (constructor.isIsBlackbox()) { |
| Object result = doVisitBlackboxOperation(constructor); |
| return new OperationCallResult(result, env); |
| } |
| |
| env.add(Environment.RESULT_VARIABLE_NAME, env.remove(Environment.SELF_VARIABLE_NAME)); |
| return new OperationCallResult(visitOperationBody(constructor.getBody()), env); |
| } |
| |
| public Object visitConstructorBody(ConstructorBody constructorBody) { |
| return visitOperationBody(constructorBody); |
| } |
| |
| public Object visitBreakExp(BreakExp astNode) { |
| return BREAK; |
| } |
| |
| public Object visitCatchtExp(CatchExp astNode) { |
| // TODO Auto-generated method stub |
| return null; |
| } |
| |
| public Object visitContinueExp(ContinueExp astNode) { |
| return CONTINUE; |
| } |
| |
| public Object visitDictLiteralPart(DictLiteralPart astNode) { |
| // TODO Auto-generated method stub |
| return null; |
| } |
| |
| public Object visitOrderedTupleLiteralExp(OrderedTupleLiteralExp astNode) { |
| // TODO Auto-generated method stub |
| return null; |
| } |
| |
| public Object visitOrderedTupleLiteralPart(OrderedTupleLiteralPart astNode) { |
| // TODO Auto-generated method stub |
| return null; |
| } |
| |
| public Object visitRaiseExp(RaiseExp raiseExp) { |
| Object value = null; |
| if(raiseExp.getArgument() != null) { |
| value = visitExpression(raiseExp.getArgument()); |
| } |
| String argument = (value instanceof String) ? (String)value : null; |
| QvtException exception = new QvtException(argument, (EClass)raiseExp.getException()); |
| exception.setStackQvtTrace(new QvtStackTraceBuilder(getOperationalEvaluationEnv()).buildStackTrace()); |
| throw exception; |
| } |
| |
| public Object visitTryExp(TryExp tryExp) { |
| try { |
| return visitBlockExpImpl(tryExp.getTryBody(), tryExp.eContainer() instanceof ImperativeOperation); |
| } |
| catch (QvtException exception) { |
| boolean processed = false; |
| |
| OUTERMOST: for (CatchExp catchExp : tryExp.getExceptClause()) { |
| for (EClassifier excType : catchExp.getException()) { |
| if (EmfUtil.isAssignableFrom(excType, exception.getExceptionType())) { |
| processed = processCatch(catchExp, exception); |
| break OUTERMOST; |
| } |
| } |
| if (catchExp.getException().isEmpty()) { // catch all |
| processed = processCatch(catchExp, exception); |
| break OUTERMOST; |
| } |
| } |
| |
| if (!processed) { |
| throw exception; |
| } |
| } |
| |
| return null; |
| } |
| |
| private boolean processCatch(CatchExp catchExp, QvtException exception) { |
| QvtOperationalEvaluationEnv evalEnv = getOperationalEvaluationEnv(); |
| String varName = null; |
| |
| org.eclipse.ocl.ecore.Variable catchVariable = ASTBindingHelper.getCatchVariable(catchExp); |
| if (catchVariable != null) { |
| ExceptionInstance excObject = null; |
| if (exception.getExceptionType().getEPackage() == QvtOperationalStdLibrary.INSTANCE.getStdLibModule()) { |
| excObject = QvtOperationalStdLibrary.INSTANCE.getStdlibFactory().createException(exception.getExceptionType(), |
| exception.getMessage(), exception.getQvtStackTrace()); |
| } |
| else { |
| ExceptionClassInstance exceptionImpl = (ExceptionClassInstance) createInstance(exception.getExceptionType(), null); |
| exceptionImpl.setArgument(exception.getMessage()); |
| exceptionImpl.setStackElements(exception.getQvtStackTrace()); |
| excObject = exceptionImpl; |
| } |
| |
| varName = catchVariable.getName(); |
| evalEnv.add(varName, excObject); |
| } |
| |
| visitBlockExpImpl(catchExp.getBody(), catchExp.eContainer() instanceof ImperativeOperation); |
| |
| if (varName != null) { |
| evalEnv.remove(varName); |
| } |
| return true; |
| } |
| |
| public Object visitUnlinkExp(UnlinkExp astNode) { |
| // TODO Auto-generated method stub |
| return null; |
| } |
| |
| public Object visitUnpackExp(UnpackExp astNode) { |
| // TODO Auto-generated method stub |
| return null; |
| } |
| |
| 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); |
| // push stack before a chance for exception, to have push/pop in balance |
| pushedStack(nestedEvalEnv); |
| // eventually cause STO exception |
| EvaluationUtil.checkCurrentStackDepth(currentEvalEnv); |
| |
| // do initialization of model params here to ensure existing out extent for objects created during initialization of imported modules (fixed by bug 392080) |
| doInitModelParams(moduleInstance, modelParameters); |
| |
| for (ModuleImport moduleImport : type.getModuleImport()) { |
| Module importedModule = moduleImport.getImportedModule(); |
| ModuleInstance importedInstance = moduleInstance.getThisInstanceOf(importedModule); |
| if(importedInstance != null) { |
| ModuleInstance.Internal importedInternal = importedInstance.getAdapter(ModuleInstance.Internal.class); |
| if(!importedInternal.isInitialized()) { |
| initModule(importedInstance, modelParameters); |
| } |
| } |
| } |
| |
| doInitModule(moduleInstance); |
| |
| moduleInstance.getAdapter(ModuleInstance.Internal.class).setInitialized(); |
| |
| } finally { |
| setOperationalEvaluationEnv(getOperationalEvaluationEnv().getParent()); |
| poppedStack(); |
| } |
| } |
| |
| private void doInitModelParams(ModuleInstance moduleInstance, ModelParameterHelper modelParameters) { |
| |
| if(modelParameters != null && moduleInstance.getModule().eClass() == ExpressionsPackage.eINSTANCE.getOperationalTransformation()) { |
| modelParameters.initModelParameters((TransformationInstance) moduleInstance); |
| } |
| |
| } |
| |
| private void doInitModule(ModuleInstance moduleInstance) { |
| Module module = moduleInstance.getModule(); |
| |
| QvtOperationalEvaluationEnv env = getOperationalEvaluationEnv(); |
| |
| 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 = visitExpression(initExp); |
| } |
| else { |
| propValue = EvaluationUtil.createInitialValue(feature.getEType(), getEnvironment().getOCLStandardLibrary(), getEvaluationEnvironment()); |
| } |
| 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 = null; |
| |
| for (VarParameter varParam : mappingOperation.getResult()) { |
| result = getRuntimeValue(varParam.getName()); |
| |
| if (isUndefined(result)) { |
| |
| if (false == varParam.getEType() instanceof VoidType<?>) { |
| result = createInstance(varParam.getEType(), ((MappingParameter) varParam).getExtent()); |
| replaceInEnv(varParam.getName(), result, varParam.getEType()); |
| } |
| } |
| } |
| |
| if (mappingOperation.getResult().size() > 1) { |
| result = createTupleResult(mappingOperation); |
| replaceInEnv(Environment.RESULT_VARIABLE_NAME, result, mappingOperation.getEType()); |
| } |
| else { |
| result = getRuntimeValue(Environment.RESULT_VARIABLE_NAME); |
| } |
| |
| return result; |
| } |
| |
| private void createOutParams(MappingOperation mappingOperation) { |
| for (EParameter nextParam : mappingOperation.getEParameters()) { |
| VarParameter param = (VarParameter) nextParam; |
| Object paramValue = getRuntimeValue(param.getName()); |
| if (isUndefined(paramValue) && param.getKind() == DirectionKind.OUT) { |
| paramValue = createInstance(param.getType(), ((MappingParameter) param).getExtent()); |
| replaceInEnv(param.getName(), paramValue, param.getType()); |
| } |
| } |
| } |
| |
| private void createOutParamsDisjunct(MappingOperation operation, List<Object> argValues) { |
| Iterator<EParameter> itParams = operation.getEParameters().iterator(); |
| Iterator<Object> itValue = argValues.iterator(); |
| while (itParams.hasNext()) { |
| MappingParameter param = (MappingParameter) itParams.next(); |
| Object argValue = itValue.next(); |
| if (param.getKind() == DirectionKind.OUT) { |
| replaceInEnv(param.getName(), argValue, param.getType()); |
| } |
| } |
| } |
| |
| private void setOutParamsValues(MappingOperation operation, List<Object> argValues) { |
| Iterator<EParameter> itParams = operation.getEParameters().iterator(); |
| ListIterator<Object> itArgument = argValues.listIterator(); |
| while (itArgument.hasNext()) { |
| MappingParameter mappingParam = (MappingParameter) itParams.next(); |
| itArgument.next(); |
| if (mappingParam.getKind() == DirectionKind.OUT) { |
| itArgument.set(getRuntimeValue(mappingParam.getName())); |
| } |
| } |
| } |
| |
| /** |
| * Tag interface which represents an evaluation result which, |
| * when encountered, breaks control flow. |
| * |
| * Examples of this are break, continue, and return. |
| */ |
| public static interface BreakingResult { |
| |
| } |
| |
| /** |
| * Type of result which represents the situation in which a break statement is encountered. |
| */ |
| public static class BreakResult implements BreakingResult { |
| BreakResult() { } |
| } |
| |
| protected final static BreakResult BREAK = new BreakResult(); |
| |
| /** |
| * Type of result which represents the situation in which a continue statement is encountered. |
| */ |
| public static class ContinueResult implements BreakingResult { |
| ContinueResult() { } |
| } |
| |
| protected final static ContinueResult CONTINUE = new ContinueResult(); |
| |
| /** |
| * The result of an operation call. |
| * Represents the situation where a return statement was encountered. |
| */ |
| public static class OperationCallResult implements BreakingResult { |
| 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_DISJUNCT_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) { |
| QvtOperationalEvaluationEnv oldEvalEnv = getOperationalEvaluationEnv(); |
| |
| boolean isMapping = method instanceof MappingOperation; |
| // 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 (source != getInvalid()) { |
| 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 (StackOverflowError e) { |
| throwQVTException(new QvtStackOverFlowError(e)); |
| } |
| catch (QvtRuntimeException e) { |
| throw e; |
| } |
| catch (RuntimeException e) { |
| String errorMessage = EvaluationMessages.QvtOperationalEvaluationVisitorImpl_unexpectedRuntimeExc; |
| QvtPlugin.error(errorMessage, e); |
| |
| throwQVTException(new QvtRuntimeException(errorMessage, 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 dispatchDisjunctMapping(MappingOperation method) { |
| QvtOperationalEvaluationEnv evalEnv = getOperationalEvaluationEnv(); |
| Object source = evalEnv.getOperationSelf(); |
| List<Object> args = evalEnv.getOperationArgs(); |
| |
| for (MappingOperation nextDisjunct : method.getDisjunct()) { |
| |
| // consider overriding mapping (fixed by bug 309762) |
| ImperativeOperation overridingOper = EvaluationUtil.getOverridingOperation(getOperationalEvaluationEnv(), nextDisjunct); |
| if (overridingOper instanceof MappingOperation) { |
| nextDisjunct = (MappingOperation) overridingOper; |
| } |
| |
| EClassifier ctxType = QvtOperationalParserUtil.getContextualType(nextDisjunct); |
| if(ctxType != null) { |
| if(!evalEnv.isKindOf(source, nextDisjunct.getContext().getEType())) { |
| continue; |
| } |
| } |
| |
| if(!dispatchDisjunctMappingArgumentsMatch(nextDisjunct)) { |
| 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 |
| |
| // add trace record for disjuncting mapping (fixed by bug 377882) |
| replaceInEnv(Environment.RESULT_VARIABLE_NAME, result.myResult, method.getEType()); |
| //retrieveOutArgs((OperationCallExp<EClassifier, EOperation>)source, result.myEvalEnv.getOperationArgs(), nextDisjunct); |
| createOutParamsDisjunct(method, result.myEvalEnv.getOperationArgs()); |
| setOutParamsValues(method, args); |
| |
| TraceUtil.addTraceRecord(evalEnv, method); |
| |
| return result; |
| } |
| } |
| |
| return new MappingCallResult(null, myEvalEnv, MappingCallResult.NO_DISJUNCT_SELECTED); |
| } |
| |
| private boolean dispatchDisjunctMappingArgumentsMatch(MappingOperation disjunct) { |
| |
| List<EParameter> params = disjunct.getEParameters(); |
| QvtOperationalEvaluationEnv evalEnv = getOperationalEvaluationEnv(); |
| List<Object> args = evalEnv.getOperationArgs(); |
| |
| if(params.size() != args.size()) { |
| return false; |
| } |
| |
| for (int i = 0; i < args.size(); i++) { |
| Object nextArg = args.get(i); |
| EClassifier nextParamType = params.get(i).getEType(); |
| if(nextArg != null && !evalEnv.isKindOf(nextArg, nextParamType)) { |
| return false; |
| } |
| } |
| |
| return true; |
| |
| } |
| |
| protected final void throwQVTException(QvtRuntimeException exception) throws QvtRuntimeException { |
| getOperationalEvaluationEnv().getAdapter(InternalEvaluationEnv.class) |
| .throwQVTException(exception); |
| } |
| |
| @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 overridden for a sub-class |
| EOperation actualOperation = getOCLAnnotationSupport().resolveDynamic(operation, eTarget); |
| if(actualOperation != null && actualOperation != operation) { |
| OCLExpression<EClassifier> actualOperBody = getOperationBody(actualOperation); |
| |
| if(actualOperBody != null) { |
| Environment<EPackage, EClassifier, EOperation, EStructuralFeature, EEnumLiteral, EParameter, EObject, CallOperationAction, SendSignalAction, Constraint, EClass, EObject> myEnv = getEnvironment(); |
| EnvironmentFactory<EPackage, EClassifier, EOperation, EStructuralFeature, EEnumLiteral, EParameter, EObject, CallOperationAction, SendSignalAction, Constraint, EClass, EObject> factory = myEnv |
| .getFactory(); |
| // create a nested evaluation environment for this |
| // operation call |
| EvaluationEnvironment<EClassifier, EOperation, EStructuralFeature, EClass, EObject> 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<EPackage, EClassifier, EOperation, EStructuralFeature, EEnumLiteral, EParameter, EObject, CallOperationAction, SendSignalAction, Constraint, EClass, EObject> 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); |
| } |
| |
| @Override |
| public Object visitCollectionLiteralExp(CollectionLiteralExp<EClassifier> cl) { |
| if (cl.getType() instanceof ListType) { |
| Collection<Object> result = new MutableListImpl<Object>(); |
| for (CollectionLiteralPart<EClassifier> part : cl.getPart()) { |
| if (part instanceof CollectionItem<?>) { |
| // CollectionItem part |
| CollectionItem<EClassifier> item = (CollectionItem<EClassifier>) part; |
| OCLExpression<EClassifier> itemExp = item.getItem(); |
| Object itemVal = visitExpression(itemExp); |
| if (itemVal == getInvalid()) { |
| return getInvalid(); // can't have an invalid element in a collection |
| } |
| result.add(itemVal); |
| } else { |
| // Collection range |
| CollectionRange<EClassifier> range = (CollectionRange<EClassifier>) part; |
| OCLExpression<EClassifier> first = range.getFirst(); |
| OCLExpression<EClassifier> last = range.getLast(); |
| |
| // evaluate first value |
| Object firstVal = visitExpression(first); |
| Object lastVal = visitExpression(last); |
| if (firstVal == getInvalid() || lastVal == getInvalid()) { |
| return getInvalid(); // can't have an invalid element in a collection |
| } |
| if (firstVal instanceof Integer && lastVal instanceof Integer) { |
| // TODO: enhance IntegerRangeList to support multiple ranges |
| // add values between first and last inclusive |
| int firstInt = ((Integer) firstVal).intValue(); |
| int lastInt = ((Integer) lastVal).intValue(); |
| for (int i = firstInt; i <= lastInt; i++) { |
| result.add(new Integer(i)); |
| } |
| } |
| } // end of collection range |
| } // end of parts iterator |
| return result; |
| } |
| return super.visitCollectionLiteralExp(cl); |
| } |
| |
| @Override |
| protected Object navigate(EStructuralFeature property, OCLExpression<EClassifier> derivation, Object target) { |
| Environment<EPackage, EClassifier, EOperation, EStructuralFeature, EEnumLiteral, EParameter, EObject, CallOperationAction, SendSignalAction, Constraint, EClass, EObject> myEnv = getEnvironment(); |
| EnvironmentFactory<EPackage, EClassifier, EOperation, EStructuralFeature, EEnumLiteral, EParameter, EObject, CallOperationAction, SendSignalAction, Constraint, EClass, EObject> factory = myEnv |
| .getFactory(); |
| // create a nested evaluation environment for this property call |
| EvaluationEnvironment<EClassifier, EOperation, EStructuralFeature, EClass, EObject> nested = factory |
| .createEvaluationEnvironment(getEvaluationEnvironment()); |
| // bind "self" |
| nested.add(Environment.SELF_VARIABLE_NAME, target); |
| |
| EvaluationVisitor<EPackage, EClassifier, EOperation, EStructuralFeature, EEnumLiteral, EParameter, EObject, CallOperationAction, SendSignalAction, Constraint, EClass, EObject> 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) { |
| return null; |
| } |
| |
| if(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.error("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(), (ModelParameter) 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 && isMapping) { |
| ModelParameter extent = null; |
| for (VarParameter resultParam : resultParams) { |
| if(tupleProp.getName().equals(resultParam.getName())) { |
| MappingParameter mappingParameter = (MappingParameter) resultParam; |
| extent = mappingParameter.getExtent(); |
| break; |
| } |
| } |
| |
| propVal = createInstance(tupleProp.getEType(), extent); |
| } |
| values.put(tupleProp, propVal); |
| evalEnv.replace(tupleProp.getName(), propVal, tupleProp.getEType()); |
| } |
| |
| return evalEnv.createTuple(operation.getEType(), values); |
| } |
| |
| private List<Object> makeArgs(OperationCallExp<EClassifier, EOperation> operationCallExp) { |
| EOperation referredOperation = operationCallExp.getReferredOperation(); |
| Iterator<EParameter> iterParam = referredOperation.getEParameters().iterator(); |
| List<Object> argValues = new ArrayList<Object>(); |
| for (OCLExpression<EClassifier> arg : operationCallExp.getArgument()) { |
| Object value = visitExpression(arg); |
| EParameter param = iterParam.next(); |
| argValues.add(EvaluationUtil.doImplicitListCoercion(param.getEType(), value)); |
| } |
| |
| return argValues; |
| } |
| |
| private void retrieveOutArgs(OperationCallExp<EClassifier, EOperation> operationCallExp, List<Object> argValues, MappingOperation operation) { |
| Iterator<OCLExpression<EClassifier>> itArgument = operationCallExp.getArgument().iterator(); |
| Iterator<EParameter> itParams = operation.getEParameters().iterator(); |
| Iterator<Object> itValue = argValues.iterator(); |
| while (itArgument.hasNext()) { |
| OCLExpression<EClassifier> arg = itArgument.next(); |
| MappingParameter mappingParam = (MappingParameter) itParams.next(); |
| Object argValue = itValue.next(); |
| |
| if (mappingParam.getKind() != DirectionKind.OUT) { |
| continue; |
| } |
| |
| if (arg instanceof VariableExp<?, ?>) { |
| @SuppressWarnings("unchecked") |
| VariableExp<EClassifier, EParameter> varExp = (VariableExp<EClassifier, EParameter>) arg; |
| Variable<EClassifier, EParameter> referredVariable = varExp.getReferredVariable(); |
| if (referredVariable != null) { |
| String varName = referredVariable.getName(); |
| EClassifier variableType = arg.getType(); |
| replaceInEnv(varName, argValue, variableType); |
| } |
| } else if (arg instanceof PropertyCallExp<?, ?>) { |
| Object ownerObj = getAssignExpLValueOwner(arg); |
| if (ownerObj instanceof EObject) { |
| @SuppressWarnings("unchecked") |
| PropertyCallExp<EClassifier, EStructuralFeature> propCallExp = ((PropertyCallExp<EClassifier, EStructuralFeature>) arg); |
| QvtOperationalEvaluationEnv env = getOperationalEvaluationEnv(); |
| EObject oldIP = setCurrentEnvInstructionPointer(operationCallExp); |
| env.callSetter((EObject) ownerObj, propCallExp.getReferredProperty(), argValue, isUndefined(argValue), true); |
| setCurrentEnvInstructionPointer(oldIP); |
| } |
| } else { |
| throw new UnsupportedOperationException("Unsupported LValue type: " + ((arg == null) ? null : arg.getType())); //$NON-NLS-1$ |
| } |
| } |
| } |
| |
| private List<Object> makeEntryOperationArgs(OperationalTransformation module) { |
| |
| assert !module.isIsBlackbox() : "Non-blackbox module expected"; |
| |
| ImperativeOperation entryPoint = QvtOperationalParserUtil.getMainOperation(module); |
| if (entryPoint == null) { |
| throw new IllegalArgumentException(NLS.bind(EvaluationMessages.ExtendedOclEvaluatorVisitorImpl_ModuleNotExecutable, module.getName())); |
| } |
| |
| 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 List<Object> makeBlackboxTransformationArgs(TransformationInstance transformation, QvtOperationalEvaluationEnv evalEnv) { |
| |
| assert transformation.getTransformation().isIsBlackbox() : "Blackbox transformation expected"; |
| |
| List<Object> actualArgs = new ArrayList<Object>(); |
| |
| for (ModelParameter param : EvaluationUtil.getBlackboxSignature(transformation.getTransformation())) { |
| ModelInstance modelInst = transformation.getModel(param); |
| Object arg = createJavaModelInstance(modelInst, evalEnv); |
| actualArgs.add(arg); |
| } |
| |
| for (EStructuralFeature p : transformation.getTransformation().getConfigProperty()) { |
| Object val = evalEnv.navigateProperty(p, Collections.emptyList(), transformation); |
| ((Context) evalEnv.getContext()).setConfigProperty(p.getName(), val); |
| } |
| |
| return actualArgs; |
| } |
| |
| private JavaModelInstance createJavaModelInstance(final ModelInstance modelInst, final QvtOperationalEvaluationEnv evalEnv) { |
| return new JavaModelInstance() { |
| |
| final JavaModelType modelType = new JavaModelType() { |
| |
| public String getName() { |
| return modelInst.getModelType().getName(); |
| } |
| |
| public List<EPackage> getMetamodels() { |
| return modelInst.getModelType().getMetamodel(); |
| } |
| }; |
| |
| final JavaModelExtent modelExtent = new JavaModelExtent() { |
| |
| public void removeObject(EObject obj) { |
| modelInst.getExtent().removeElement(obj); |
| } |
| |
| public List<EObject> getRootObjects() { |
| return modelInst.getExtent().getRootObjects(); |
| } |
| |
| public List<Object> getAllObjects() { |
| return modelInst.getExtent().getAllObjects(); |
| } |
| |
| public void addObject(EObject obj) { |
| modelInst.getExtent().addObject(obj); |
| } |
| }; |
| |
| public JavaModelType getType() { |
| return modelType; |
| } |
| |
| public JavaModelExtent getExtent() { |
| return modelExtent; |
| } |
| }; |
| } |
| |
| |
| // 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; |
| newInstance = EvaluationUtil.createNewCollection(collectionType); |
| } else { |
| newInstance = getOperationalEvaluationEnv().createInstance(type, extent); |
| |
| EFactory eFactory = type.getEPackage().getEFactoryInstance(); |
| if(eFactory instanceof IntermediateClassFactory) { |
| IntermediateClassFactory intermFactory = (IntermediateClassFactory) eFactory; |
| intermFactory.doInstancePropertyInit(newInstance, this); |
| } |
| } |
| |
| } catch (IllegalArgumentException e) { |
| throwQVTException(new QvtRuntimeException(e)); |
| } |
| |
| return newInstance; |
| } |
| |
| protected EObject setCurrentEnvInstructionPointer(EObject node) { |
| InternalEvaluationEnv internEnv = getOperationalEvaluationEnv().getAdapter(InternalEvaluationEnv.class); |
| if(node != null) { |
| return internEnv.setCurrentIP(node); |
| } |
| return internEnv.getCurrentIP(); |
| } |
| |
| protected InternalEvaluator createInterruptibleVisitor() { |
| final IProgressMonitor monitor = getContext().getProgressMonitor(); |
| |
| class InterruptVisitor extends QvtGenericVisitorDecorator.Any { |
| |
| public InterruptVisitor() { |
| super(QvtOperationalEvaluationVisitorImpl.this); |
| } |
| |
| @Override |
| protected Object genericVisitAny(Object object) { |
| if(monitor != null && monitor.isCanceled()) { |
| throwQVTException(new QvtInterruptedExecutionException()); |
| } |
| |
| // set the current instruction pointer |
| if(object instanceof EObject) { |
| InternalEvaluationEnv evalEnv = getOperationalEvaluationEnv().getAdapter(InternalEvaluationEnv.class); |
| evalEnv.setCurrentIP((EObject)object); |
| } |
| |
| return null; |
| } |
| }; |
| |
| 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; |
| } |
| |
| private void evaluateModelParameterConditions(TransformationInstance transformationInstance, QvtOperationalEvaluationEnv evalEnv) { |
| for (ModelParameter parameter : transformationInstance.getTransformation().getModelParameter()) { |
| if (parameter.getEType() instanceof ModelType && parameter.getKind() != DirectionKind.OUT) { |
| ModelType parameterType = (ModelType) parameter.getEType(); |
| ModelInstance modelInstance = transformationInstance.getModel(parameter); |
| for (OCLExpression<EClassifier> whereExpression : parameterType.getAdditionalCondition()) { |
| myEvalEnv.add(Environment.SELF_VARIABLE_NAME, modelInstance); |
| try { |
| boolean isConditionMet = Boolean.TRUE.equals(visitExpression(whereExpression)); |
| if(!isConditionMet) { |
| throwQVTException(new QvtAssertionFailed(NLS.bind(EvaluationMessages.ModelTypeConstraintFailed, |
| parameter.getName(), transformationInstance.getTransformation().getName()))); |
| } |
| } |
| finally { |
| myEvalEnv.remove(Environment.SELF_VARIABLE_NAME); |
| } |
| } |
| } |
| } |
| } |
| |
| } |