blob: 8f7f12c82cb35bd8d4c2c2d601adf55ff0ce408e [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009, 2014 R.Dvorak 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:
* Radek Dvorak - initial API and implementation
* Christopher Gerking - bug 431082
*******************************************************************************/
package org.eclipse.m2m.internal.qvt.oml;
import static org.eclipse.m2m.internal.qvt.oml.emf.util.EmfUtilPlugin.isSuccess;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.emf.common.util.BasicDiagnostic;
import org.eclipse.emf.common.util.BasicMonitor;
import org.eclipse.emf.common.util.Diagnostic;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EEnumLiteral;
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.resource.ResourceSet;
import org.eclipse.m2m.internal.qvt.oml.ast.env.InternalEvaluationEnv;
import org.eclipse.m2m.internal.qvt.oml.ast.env.ModelExtentContents;
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.QvtOperationalFileEnv;
import org.eclipse.m2m.internal.qvt.oml.ast.env.QvtOperationalStdLibrary;
import org.eclipse.m2m.internal.qvt.oml.common.MdaException;
import org.eclipse.m2m.internal.qvt.oml.compiler.CompiledUnit;
import org.eclipse.m2m.internal.qvt.oml.compiler.CompilerUtils;
import org.eclipse.m2m.internal.qvt.oml.compiler.QVTOCompiler;
import org.eclipse.m2m.internal.qvt.oml.compiler.UnitProxy;
import org.eclipse.m2m.internal.qvt.oml.compiler.UnitResolverFactory;
import org.eclipse.m2m.internal.qvt.oml.emf.util.EmfUtil;
import org.eclipse.m2m.internal.qvt.oml.evaluator.EvaluationMessages;
import org.eclipse.m2m.internal.qvt.oml.evaluator.InternalEvaluator;
import org.eclipse.m2m.internal.qvt.oml.evaluator.ModelInstance;
import org.eclipse.m2m.internal.qvt.oml.evaluator.ModelParameterHelper;
import org.eclipse.m2m.internal.qvt.oml.evaluator.QvtException;
import org.eclipse.m2m.internal.qvt.oml.evaluator.QvtInterruptedExecutionException;
import org.eclipse.m2m.internal.qvt.oml.evaluator.QvtRuntimeException;
import org.eclipse.m2m.internal.qvt.oml.evaluator.QvtStackOverFlowError;
import org.eclipse.m2m.internal.qvt.oml.expressions.DirectionKind;
import org.eclipse.m2m.internal.qvt.oml.expressions.ImperativeOperation;
import org.eclipse.m2m.internal.qvt.oml.expressions.ModelParameter;
import org.eclipse.m2m.internal.qvt.oml.expressions.Module;
import org.eclipse.m2m.internal.qvt.oml.expressions.OperationalTransformation;
import org.eclipse.m2m.internal.qvt.oml.library.Context;
import org.eclipse.m2m.internal.qvt.oml.trace.Trace;
import org.eclipse.m2m.qvt.oml.ExecutionContext;
import org.eclipse.m2m.qvt.oml.ExecutionDiagnostic;
import org.eclipse.m2m.qvt.oml.ModelExtent;
import org.eclipse.m2m.qvt.oml.util.IContext;
import org.eclipse.m2m.qvt.oml.util.ISessionData;
import org.eclipse.m2m.qvt.oml.util.Log;
import org.eclipse.ocl.EvaluationVisitor;
import org.eclipse.ocl.ecore.CallOperationAction;
import org.eclipse.ocl.ecore.Constraint;
import org.eclipse.ocl.ecore.SendSignalAction;
/**
* Internal transformation executor
*
* @since 3.0
*/
public class InternalTransformationExecutor {
private URI fURI;
private EPackage.Registry fPackageRegistry;
private CompiledUnit fCompiledUnit;
private ResourceSet fCompilationRs;
private ExecutionDiagnostic fLoadDiagnostic;
private OperationalTransformation fTransformation;
private QvtOperationalEnvFactory fEnvFactory;
/**
* Constructs the executor for the given transformation URI.
* <p>
* No attempt to resolve and load the transformation is done at this step
*
* @param uri
* the URI of an existing transformation
*/
public InternalTransformationExecutor(URI uri) {
if (uri == null) {
throw new IllegalArgumentException("null transformation URI"); //$NON-NLS-1$
}
fURI = uri;
}
public URI getURI() {
return fURI;
}
public ResourceSet getResourceSet() {
return fCompilationRs;
}
public InternalTransformationExecutor(URI uri, EPackage.Registry registry) {
this(uri);
if (registry == null) {
throw new IllegalArgumentException("null package registry"); //$NON-NLS-1$
}
fPackageRegistry = registry;
}
/**
* Attempts to load the transformation referred by this executor and checks
* if it is valid for execution.
* <p>
* <b>Remark:</b></br> Only the first performs the actual transformation
* loading, subsequent calls to this method will return the existing
* diagnostic.
*
* @return the diagnostic indicating possible problems of the load action
*/
public Diagnostic loadTransformation(IProgressMonitor monitor) {
try {
if (fLoadDiagnostic == null) {
doLoad(monitor);
}
return fLoadDiagnostic;
}
finally {
monitor.done();
}
}
/**
* Retrieves compiled unit if the referencing URI gets successfully resolved
* <p>
* <b>Remark</b>: This method invocation causes the referenced transformation to
* load if not already done before by direct call to
* {@linkplain #loadTransformation()} or
* {@linkplain #execute(ExecutionContext, ModelExtent...)}
*
* @return compiled unit or <code>null</code> if it failed to be obtained
*/
public CompiledUnit getUnit() {
loadTransformation(new NullProgressMonitor());
return fCompiledUnit;
}
/**
* Executes the transformation referred by this executor using the given
* model parameters and execution context.
*
* @param executionContext
* the context object keeping the execution environment details
* @param modelParameters
* the actual model arguments to the transformation
*
* @return the diagnostic object indicating the execution result status,
* also keeping the details of possible problems
* @throws IllegalArgumentException
* if the context or any of the model parameters is
* <code>null</code>
*/
public ExecutionDiagnostic execute(ExecutionContext executionContext, ModelExtent... modelParameters) {
// Java API check for nulls etc.
if (executionContext == null) {
throw new IllegalArgumentException();
}
IProgressMonitor monitor = executionContext.getProgressMonitor();
try {
SubMonitor progress = SubMonitor.convert(monitor, "Execute " + getURI().toString(), 2); //$NON-NLS-1$
checkLegalModelParams(modelParameters);
// ensure transformation unit is loaded
loadTransformation(progress.newChild(1));
// check if we have successfully loaded the transformation unit
if (!isSuccess(fLoadDiagnostic)) {
return fLoadDiagnostic;
}
try {
return doExecute(modelParameters,
createInternalContext(executionContext, progress.newChild(1)));
} catch (QvtRuntimeException e) {
Log logger = executionContext.getLog();
logger.log(EvaluationMessages.TerminatingExecution);
return createExecutionFailure(e);
}
} finally {
if (monitor != null) {
monitor.done();
}
}
}
private ExecutionDiagnostic doExecute(ModelExtent[] args, IContext context) {
QvtOperationalEnvFactory factory = getEnvironmentFactory();
QvtOperationalEvaluationEnv evaluationEnv = factory
.createEvaluationEnvironment(context, null);
ExecutionDiagnostic modelParamsDiagnostic = initArguments(evaluationEnv, fTransformation, args);
if (!isSuccess(modelParamsDiagnostic)) {
return modelParamsDiagnostic;
}
QvtOperationalFileEnv rootEnv = factory.createEnvironment(fCompiledUnit.getURI());
EvaluationVisitor<EPackage, EClassifier, EOperation, EStructuralFeature, EEnumLiteral, EParameter, EObject, CallOperationAction, SendSignalAction, Constraint, EClass, EObject> evaluator = factory
.createEvaluationVisitor(rootEnv, evaluationEnv, null);
// perform the actual execution
assert evaluator instanceof InternalEvaluator : "expecting InternalEvaluator implementation"; //$NON-NLS-1$
InternalEvaluator rawEvaluator = (InternalEvaluator) evaluator;
Object evalResult = rawEvaluator.execute(fTransformation);
// unpack the internal extents into the passed model parameters
if (evalResult instanceof QvtEvaluationResult) {
int extentIndex = 0;
for (int i = 0; i < fTransformation.getModelParameter().size(); ++i) {
ModelParameter p = fTransformation.getModelParameter().get(i);
if (p.getKind() == DirectionKind.IN) {
continue;
}
ModelExtentContents extent = ((QvtEvaluationResult) evalResult).getModelExtents().get(extentIndex++);
args[i].setContents(extent.getAllRootElements());
}
}
else {
List<Object> resultArgs = evaluationEnv.getOperationArgs();
int i = 0;
for (Object nextResultArg : resultArgs) {
ModelInstance modelInstance = (ModelInstance) nextResultArg;
ModelParameterExtent extent = modelInstance.getExtent();
List<EObject> allRootElements = extent.getContents().getAllRootElements();
try {
args[i++].setContents(allRootElements);
} catch (UnsupportedOperationException e) {
return new ExecutionDiagnosticImpl(Diagnostic.ERROR, ExecutionDiagnostic.MODEL_PARAMETER_MISMATCH,
NLS.bind(Messages.ReadOnlyExtentModificationError, i - 1));
}
}
}
// do some handy processing with traces
Trace traces = evaluationEnv.getAdapter(InternalEvaluationEnv.class).getTraces();
handleExecutionTraces(traces);
return ExecutionDiagnostic.OK_INSTANCE;
}
protected void handleExecutionTraces(Trace traces) {
// nothing interesting here
}
private void doLoad(IProgressMonitor monitor) {
fLoadDiagnostic = ExecutionDiagnostic.OK_INSTANCE;
UnitProxy unit = UnitResolverFactory.Registry.INSTANCE.getUnit(fURI);
if (unit == null) {
fLoadDiagnostic = new ExecutionDiagnosticImpl(Diagnostic.ERROR,
ExecutionDiagnostic.TRANSFORMATION_LOAD_FAILED, NLS.bind(
Messages.UnitNotFoundError, fURI));
return;
}
QVTOCompiler compiler = createCompiler();
try {
fCompiledUnit = compiler.compile(unit, null, BasicMonitor.toMonitor(monitor));
fCompilationRs = compiler.getResourceSet();
// fCompilerKernel = compiler.getKernel();
fLoadDiagnostic = createCompilationDiagnostic(fCompiledUnit);
} catch (MdaException e) {
fLoadDiagnostic = new ExecutionDiagnosticImpl(Diagnostic.ERROR,
ExecutionDiagnostic.TRANSFORMATION_LOAD_FAILED, NLS.bind(
Messages.FailedToCompileUnitError, fURI));
fLoadDiagnostic.merge(BasicDiagnostic.toDiagnostic(e));
}
if (fCompiledUnit != null
&& isSuccess(fLoadDiagnostic)) {
fTransformation = getTransformation();
if (fTransformation == null) {
fLoadDiagnostic = new ExecutionDiagnosticImpl(Diagnostic.ERROR,
ExecutionDiagnostic.TRANSFORMATION_LOAD_FAILED, NLS
.bind(Messages.NotTransformationInUnitError,
fURI));
return;
}
ExecutionDiagnostic validForExecution = checkIsExecutable(fTransformation);
if (!isSuccess(validForExecution)) {
fLoadDiagnostic = validForExecution;
}
}
}
private ExecutionDiagnostic initArguments(
QvtOperationalEvaluationEnv evalEnv,
OperationalTransformation transformationModel, ModelExtent[] args) {
EList<ModelParameter> modelParameters = transformationModel
.getModelParameter();
if (modelParameters.size() != args.length) {
return new ExecutionDiagnosticImpl(Diagnostic.ERROR,
ExecutionDiagnostic.MODEL_PARAMETER_MISMATCH, NLS.bind(
Messages.InvalidModelParameterCountError,
args.length, modelParameters.size()));
}
ExecutionDiagnostic result = ExecutionDiagnostic.OK_INSTANCE;
List<ModelParameterExtent> extents = new ArrayList<ModelParameterExtent>(
args.length);
int argCount = 0;
for (ModelParameter modelParam : modelParameters) {
ModelParameterExtent nextExtent;
ModelExtent nextArg = args[argCount++];
if (modelParam.getKind() != org.eclipse.m2m.internal.qvt.oml.expressions.DirectionKind.OUT) {
nextExtent = new ModelParameterExtent(nextArg.getContents(), getResourceSet(), modelParam);
} else {
nextExtent = new ModelParameterExtent(getResourceSet());
}
evalEnv.addModelExtent(nextExtent);
extents.add(nextExtent);
}
List<ModelInstance> modelArgs = ModelParameterHelper
.createModelArguments(transformationModel, extents);
evalEnv.getOperationArgs().addAll(modelArgs);
return result;
}
private static ExecutionDiagnostic checkIsExecutable(
OperationalTransformation transformation) {
if (transformation.isIsBlackbox()) {
return ExecutionDiagnostic.OK_INSTANCE;
}
EList<EOperation> operations = transformation.getEOperations();
for (EOperation oper : operations) {
if (oper instanceof ImperativeOperation
&& QvtOperationalEnv.MAIN.equals(oper.getName())) {
return ExecutionDiagnostic.OK_INSTANCE;
}
}
return new ExecutionDiagnosticImpl(Diagnostic.ERROR,
ExecutionDiagnostic.VALIDATION, NLS.bind(
Messages.NoTransformationEntryPointError,
transformation.getName()));
}
public OperationalTransformation getTransformation() {
// TODO - cached the transformation selected as main
if(fCompiledUnit == null) {
return null;
}
List<Module> allModules = fCompiledUnit.getModules();
for (Module module : allModules) {
if (module instanceof OperationalTransformation) {
return (OperationalTransformation) module;
}
}
return null;
}
public void setEnvironmentFactory(QvtOperationalEnvFactory factory) {
fEnvFactory = factory;
}
protected QvtOperationalEnvFactory getEnvironmentFactory() {
return fEnvFactory != null ? fEnvFactory : new QvtOperationalEnvFactory();
}
public void cleanup() {
setEnvironmentFactory(null);
if (fCompilationRs != null) {
EmfUtil.cleanupResourceSet(fCompilationRs);
}
}
private static ExecutionDiagnostic createExecutionFailure(
QvtRuntimeException qvtRuntimeException) {
int code = 0;
int severity = Diagnostic.ERROR;
String message = qvtRuntimeException.getLocalizedMessage();
Object[] data = null;
if (qvtRuntimeException instanceof QvtException) {
code = ((QvtException) qvtRuntimeException).getExceptionType() == QvtOperationalStdLibrary.INSTANCE.getAssertionFailedClass() ?
ExecutionDiagnostic.FATAL_ASSERTION : ExecutionDiagnostic.EXCEPTION_THROWN;
} else if (qvtRuntimeException instanceof QvtInterruptedExecutionException) {
code = ExecutionDiagnostic.USER_INTERRUPTED;
severity = Diagnostic.CANCEL;
} else {
code = ExecutionDiagnostic.EXCEPTION_THROWN;
if (qvtRuntimeException instanceof QvtStackOverFlowError == false) {
Throwable cause = qvtRuntimeException.getCause();
data = new Object[] { cause != null ? cause : qvtRuntimeException };
} else {
message = Messages.StackTraceOverFlowError;
}
}
if (message == null) {
message = NLS.bind(Messages.QVTRuntimeExceptionCaught,
qvtRuntimeException.getClass().getName());
}
ExecutionDiagnosticImpl diagnostic = new ExecutionDiagnosticImpl(severity,
code, message, data);
diagnostic.setStackTrace(qvtRuntimeException.getQvtStackTrace());
return diagnostic;
}
private void checkLegalModelParams(ModelExtent[] extents)
throws IllegalArgumentException {
if (extents == null) {
throw new IllegalArgumentException("Null model parameters"); //$NON-NLS-1$
}
for (int i = 0; i < extents.length; i++) {
if (extents[i] == null) {
throw new IllegalArgumentException(
"Null model parameter[" + i + "]"); //$NON-NLS-1$ //$NON-NLS-2$
}
}
}
private static ExecutionDiagnostic createCompilationDiagnostic(
CompiledUnit compiledUnit) {
List<QvtMessage> errors = compiledUnit.getErrors();
if (errors.isEmpty()) {
return ExecutionDiagnostic.OK_INSTANCE;
}
URI uri = compiledUnit.getURI();
ExecutionDiagnosticImpl mainDiagnostic = new ExecutionDiagnosticImpl(
Diagnostic.ERROR, ExecutionDiagnostic.VALIDATION, NLS.bind(
Messages.CompilationErrorsFoundInUnit, uri.toString()));
for (QvtMessage message : errors) {
// FIXME - we should include warnings as well
mainDiagnostic.add(CompilerUtils.createProblemDiagnostic(uri, message));
}
return mainDiagnostic;
}
private static IContext createInternalContext(ExecutionContext executionContext, IProgressMonitor monitor) {
Context ctx = new Context();
ctx.setLog(executionContext.getLog());
ctx.setProgressMonitor(monitor);
for (String key : executionContext.getConfigPropertyNames()) {
Object value = executionContext.getConfigProperty(key);
ctx.setConfigProperty(key, value);
}
for (ISessionData.Entry<Object> key : executionContext.getSessionDataEntries()) {
ctx.getSessionData().setValue(key, executionContext.getSessionData().getValue(key));
}
return ctx;
}
@Override
public String toString() {
return "QVTO-Executor: " + fURI; //$NON-NLS-1$
}
protected QVTOCompiler createCompiler() {
if(fPackageRegistry == null) {
return CompilerUtils.createCompiler();
}
return QVTOCompiler.createCompiler(fPackageRegistry);
}
}