/* * Copyright (c) 2008, 2009 Borland Software Corporation * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Artem Tikhomirov (Borland) - initial API and implementation */ package org.eclipse.gmf.internal.xpand.ocl; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Set; import org.eclipse.emf.common.util.Diagnostic; import org.eclipse.emf.ecore.EClassifier; import org.eclipse.gmf.internal.xpand.StreamsHolder; import org.eclipse.gmf.internal.xpand.expression.ast.SyntaxElement; import org.eclipse.gmf.internal.xpand.model.AnalysationIssue; import org.eclipse.gmf.internal.xpand.model.EvaluationException; import org.eclipse.gmf.internal.xpand.model.ExecutionContext; import org.eclipse.gmf.internal.xpand.model.Scope; import org.eclipse.gmf.internal.xpand.qvtlibraries.XpandGlobalVars; import org.eclipse.gmf.internal.xpand.util.XpandStreamOperations; import org.eclipse.gmf.internal.xpand.xtend.ast.QvtResource; import org.eclipse.m2m.internal.qvt.oml.ast.env.QvtOperationalEvaluationEnv; import org.eclipse.m2m.internal.qvt.oml.evaluator.ModuleInstance; import org.eclipse.m2m.internal.qvt.oml.evaluator.QvtOperationalEvaluationVisitor; import org.eclipse.m2m.internal.qvt.oml.expressions.Module; import org.eclipse.ocl.cst.OCLExpressionCS; import org.eclipse.ocl.ecore.EcoreEnvironment; import org.eclipse.ocl.expressions.OCLExpression; import org.eclipse.ocl.parser.OCLProblemHandler; public class ExpressionHelper { private final OCLExpressionCS expressionCS; private OCLExpression<EClassifier> oclExpression; private EcoreEnvironment oclEnvironment; private Diagnostic oclExpressionDiagnostic; private SyntaxElement parentElement; public ExpressionHelper(OCLExpressionCS exprCS, SyntaxElement parentElement) { assert exprCS != null; this.expressionCS = exprCS; // TODO: determine start/end/line from CST element? this.parentElement = parentElement; } public OCLExpressionCS getCST() { return expressionCS; } public EClassifier analyze(ExecutionContext ctx, Set<AnalysationIssue> issues) { EcoreEnvironment env = getOCLEnvironment(ctx); OCLExpression<EClassifier> expression = getOCLExpression(env); handleOCLAnalyzationErrors(issues); return expression!= null ? expression.getType() : null; } /** * TODO: report error message with more concrete positions (currently whole * ImperativeOCL expression will be highlighted) */ private void handleOCLAnalyzationErrors(Set<AnalysationIssue> issues) { if (getOclExpressionDiagnostic() != null) { issues.add(new AnalysationIssue(AnalysationIssue.Type.INCOMPATIBLE_TYPES, getOclExpressionDiagnostic().getMessage(), this)); } } public Object evaluate(ExecutionContext ctx) { EcoreEnvironment env = getOCLEnvironment(ctx); OCLExpression<EClassifier> expression = getOCLExpression(env); if (getOclExpressionDiagnostic() != null) { throw new EvaluationException(getOclExpressionDiagnostic().getMessage(), this); } // TODO: use CustomOclValidationVisitor extracted from // QvtOperationalValidationVisitor once it is available. // // Validating AST only on evaluation time since this process can report // // some errors in indirectly references .qvto files which are not // // important while analyzing AST // ValidationVisitor<EPackage, EClassifier, EOperation, EStructuralFeature, EEnumLiteral, EParameter, EObject, CallOperationAction, SendSignalAction, Constraint, EClass, EObject> validator = new ValidationVisitor<EPackage, EClassifier, EOperation, EStructuralFeature, EEnumLiteral, EParameter, EObject, CallOperationAction, SendSignalAction, Constraint, EClass, EObject>( // env) { // }; // expression.accept(validator); // Diagnostic validationResults = getOCLDiagnostic(env); // if (validationResults != null) { // throw new EvaluationException(validationResults.getMessage(), this); // } QvtOperationalEvaluationVisitor visitor = ctx.createEvaluationVisitor(); defineGlobalVariables(ctx, visitor.getOperationalEvaluationEnv()); initializeStreamsHolder(ctx.getScope(), ctx.getScope().getOutput().getNamedStreams(), visitor.getOperationalEvaluationEnv()); Object val = visitor.visitExpression(expression); initializeStreamsHolder(ctx.getScope(), null, visitor.getOperationalEvaluationEnv()); clearGlobalVariables(ctx, visitor.getOperationalEvaluationEnv()); if (env.getOCLStandardLibrary().getOclInvalid() == val) { throw new EvaluationException("Can't evaluate expression: returned value is OclInvalid", this); } return val; } private EcoreEnvironment getOCLEnvironment(ExecutionContext ctx) { if (oclEnvironment == null) { oclEnvironment = ctx.getOCLEnvironment(); } return oclEnvironment; } private OCLExpression<EClassifier> getOCLExpression(EcoreEnvironment env) { if (oclExpression == null) { oclExpression = new EmbeddedQVTAnalyzer(env).analyzeExpression(expressionCS); oclExpressionDiagnostic = getOCLDiagnostic(env); } return oclExpression; } private Diagnostic getOCLDiagnostic(EcoreEnvironment env) { if (env.getProblemHandler() instanceof OCLProblemHandler) { OCLProblemHandler oclProblemHandler = (OCLProblemHandler) env.getProblemHandler(); Diagnostic diagnostic = oclProblemHandler.getDiagnostic(); if (diagnostic != null && diagnostic.getSeverity() == Diagnostic.ERROR) { return diagnostic; } oclProblemHandler.clearDiagnostic(); } return null; } /** * Should be called only after {@link #getOCLExpression(EcoreEnvironment)} * * @return Diagnostic or null if expression was analyzed successfully */ private Diagnostic getOclExpressionDiagnostic() { return oclExpressionDiagnostic; } private void clearGlobalVariables(ExecutionContext ctx, QvtOperationalEvaluationEnv evaluationEnv) { Collection<String> globalVarNames = ctx.getScope().getGlobalVarNames(); if (globalVarNames.isEmpty()) { return; } XpandGlobalVars globalVarsLibInstance = getGlobalVarsLibraryInstance(ctx.getScope(), evaluationEnv); if (globalVarsLibInstance != null) { globalVarsLibInstance.globalVariables = Collections.emptyMap(); } } private void defineGlobalVariables(ExecutionContext ctx, QvtOperationalEvaluationEnv evaluationEnv) { Scope scope = ctx.getScope(); Collection<String> globalVarNames = scope.getGlobalVarNames(); if (globalVarNames.isEmpty()) { return; } XpandGlobalVars globalVarsLibInstance = getGlobalVarsLibraryInstance(scope, evaluationEnv); if (globalVarsLibInstance != null) { Map<String, Object> globalVars = new HashMap<String, Object>(); for (String varName : globalVarNames) { globalVars.put(varName, scope.getGlobalVariable(varName).getValue()); } globalVarsLibInstance.globalVariables = globalVars; } } private XpandGlobalVars getGlobalVarsLibraryInstance(Scope scope, QvtOperationalEvaluationEnv evaluationEnv) { QvtResource globalVarsOperationResource = scope.findExtension("xpt::GlobalVarOperations"); if (globalVarsOperationResource != null) { for (Module module : globalVarsOperationResource.getModules()) { ModuleInstance moduleInstance = evaluationEnv.getThisOfType(module); if (moduleInstance != null) { XpandGlobalVars globalVarsLibInstance = moduleInstance.getAdapter(XpandGlobalVars.class); if (globalVarsLibInstance != null) { return globalVarsLibInstance; } } } } return null; } /** * Initializes QVT black-box java library with the given value of the streams holder. */ private void initializeStreamsHolder(Scope scope, StreamsHolder namedStreams, QvtOperationalEvaluationEnv evaluationEnv) { QvtResource streamOperationResource = scope.findExtension("xpt::StreamOperations"); if (streamOperationResource != null) { for (Module module : streamOperationResource.getModules()) { ModuleInstance moduleInstance = evaluationEnv.getThisOfType(module); if (moduleInstance != null) { XpandStreamOperations libInstance = moduleInstance.getAdapter(XpandStreamOperations.class); if (libInstance != null) { libInstance.streamsHolder = namedStreams; } } } } } public int getStart() { return expressionCS.getStartOffset(); } public int getEnd() { return expressionCS.getEndOffset(); } public String getFileName() { return parentElement.getFileName(); } public int getLine() { return parentElement.getLine(); } /** * @return cached oclExpression. This method should be called only after * analyze() or evaluate() method call. */ public OCLExpression<EClassifier> getOCLExpression() { return oclExpression; } }