blob: e69643569e3e1beaa8e393cee389a2a830407df8 [file] [log] [blame]
/*
* 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;
}
}