blob: 616fbd3e935a597a55c88fd6e53d3a88b04412eb [file] [log] [blame]
/**
* <copyright>
*
* Copyright (c) 2007, 2008 Obeo.
* 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
*
* </copyright>
*
* Contributors:
* Quentin Glineur - initial API and implementation
*
* $Id: ATLVMCompiler.java,v 1.22 2009/03/05 18:06:12 qglineur Exp $
*/
package org.eclipse.qvt.declarative.relations.atlvm;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.InvalidPropertiesFormatException;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.m2m.atl.drivers.emf4atl.ASMEMFModel;
import org.eclipse.m2m.atl.drivers.emf4atl.EMFModelLoader;
import org.eclipse.m2m.atl.engine.vm.ASM;
import org.eclipse.m2m.atl.engine.vm.ASMExecEnv;
import org.eclipse.m2m.atl.engine.vm.ASMInterpreter;
import org.eclipse.m2m.atl.engine.vm.ASMXMLReader;
import org.eclipse.m2m.atl.engine.vm.Debugger;
import org.eclipse.m2m.atl.engine.vm.SimpleDebugger;
import org.eclipse.m2m.atl.engine.vm.nativelib.ASMModel;
import org.eclipse.m2m.atl.engine.vm.nativelib.ASMModule;
import org.eclipse.qvt.declarative.atlvm.problems.problems.Problem;
import org.eclipse.qvt.declarative.atlvm.problems.problems.ProblemsPackage;
import org.eclipse.qvt.declarative.common.framework.service.Operation;
import org.eclipse.qvt.declarative.compilation.CompilationProvider;
import org.eclipse.qvt.declarative.compilation.CompileOperation;
import org.eclipse.qvt.declarative.compilation.DeclarativeQVTCompilationException;
import org.eclipse.qvt.declarative.compilation.QVTRelationsCompilationException;
import org.eclipse.qvt.declarative.ecore.QVTBase.TypedModel;
import org.eclipse.qvt.declarative.ecore.QVTRelation.QVTRelationPackage;
import org.eclipse.qvt.declarative.ecore.QVTRelation.RelationalTransformation;
import org.eclipse.qvt.declarative.relations.atlvm.relationsToTraceClass.RelationsToTraceClassPackage;
import org.eclipse.qvt.declarative.relations.atlvm.utils.ASMUtils;
import org.osgi.framework.Bundle;
/**
* A client implementation to provide a compilation of QVT Relations in ATLVM
* byte code
*
* @author Quentin Glineur
*
*/
public class ATLVMCompiler implements CompilationProvider {
private static final String COMPILER_ASM_LOCATION = "resources/QVTR.asm"; //$NON-NLS-1$
private static final String TRACE_CLASS_ASM_LOCATION = "resources/RelationsToTraceClass.asm"; //$NON-NLS-1$
private static final ASM RELATION_TO_TRACE_CLASS;
private static final String DEFAULT_DEBUGGER_PROPERTIES_LOCATION = "debugger.properties.xml"; //$NON-NLS-1$
protected static final String EXECUTABLE_SUFFIX = "asm"; //$NON-NLS-1$
public static final String OUT_FILE_PARAMETER_NAME = "WriteTo"; //$NON-NLS-1$
public static final String DIRECTION_PARAMETER_NAME = "direction"; //$NON-NLS-1$
public static final String TRANSFORMATION_MODEL_NAME = "IN"; //$NON-NLS-1$
private static final String PROBLEM_MODEL_FILE_EXTENSION = "pbm.xmi"; //$NON-NLS-1$
private static final String PROBLEM_MODEL_NAME = "OUT";
private static final ASM COMPILER_ASM;
private static final String RELATION_METAMODEL_ALIAS = "QVTR";
/**
* The Problem metamodel. This is the way problems are reported during the
* compilation: instances of the Problem class are created inside a model.
* This model can be then interpreted to exhibit problems.
*/
private static final ASMModel PROBLEM_METAMODEL;
private static final Debugger DEFAULT_DEBUGGER;
private static final ASMModel QVTR_METAMODEL;
static {
// static initializations
COMPILER_ASM = loadQVTRCompiler();
PROBLEM_METAMODEL = loadProblemMetamodel();
DEFAULT_DEBUGGER = createDefaultDebugger();
QVTR_METAMODEL = loadQVTRMetamodel();
RELATION_TO_TRACE_CLASS = loadRelationToTraceClass();
}
/**
* Construct a new compiler producing ATLVM byte code from a QVT Relations
* source
*/
public ATLVMCompiler() {
}
private static ASM loadRelationToTraceClass() {
Bundle bundle = Activator.getDefault().getBundle();
ASM compilerASM = null;
URL compilerUrl = FileLocator.find(bundle, new Path(
TRACE_CLASS_ASM_LOCATION), Collections.EMPTY_MAP);
try {
compilerASM = new ASMXMLReader().read(new BufferedInputStream(
compilerUrl.openStream()));
} catch (IOException e) {
e.printStackTrace();
}
return compilerASM;
}
private static ASMModel loadQVTRMetamodel() {
ASMModel model = null;
try {
EMFModelLoader emfModelLoader = new EMFModelLoader();
model = emfModelLoader.loadModel("QVTR", emfModelLoader.getMOF(),
URI.createURI(QVTRelationPackage.eNS_URI));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return model;
}
private static ASMModel loadProblemMetamodel() {
ASMModel model = null;
try {
EMFModelLoader emfModelLoader = new EMFModelLoader();
model = emfModelLoader.loadModel(ProblemsPackage.eNAME,
emfModelLoader.getMOF(), URI
.createURI(ProblemsPackage.eNS_URI));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return model;
}
/**
* Load the compiled version (i.e ASM codes) of the QVTR compiler.
*/
private static final ASM loadQVTRCompiler() {
Bundle bundle = Activator.getDefault().getBundle();
ASM compilerASM = null;
URL compilerUrl = FileLocator.find(bundle, new Path(
COMPILER_ASM_LOCATION), Collections.EMPTY_MAP);
try {
compilerASM = new ASMXMLReader().read(new BufferedInputStream(
compilerUrl.openStream()));
} catch (IOException e) {
e.printStackTrace();
}
return compilerASM;
}
/**
* Create a default debugger with the parameters stored in the corresponding
* configuration file
*/
private static Debugger createDefaultDebugger() {
Properties debuggerProperties = new Properties();
URL debuggerPropertiesURL = ATLVMCompiler.class
.getResource(DEFAULT_DEBUGGER_PROPERTIES_LOCATION);
try {
debuggerProperties.loadFromXML(debuggerPropertiesURL.openStream());
} catch (InvalidPropertiesFormatException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
Debugger result = new SimpleDebugger(
/* step = */Boolean.toString(true).equals(
debuggerProperties.get("step")), //$NON-NLS-1$
/* stepops = */new ArrayList<String>(),
/* deepstepops = */new ArrayList<String>(),
/* nostepops = */new ArrayList<String>(),
/* deepnostepops = */new ArrayList<String>(),
/* showStackTrace = */true, Boolean.toString(true).equals(
debuggerProperties.get("showSummary")), //$NON-NLS-1$
Boolean.toString(true)
.equals(debuggerProperties.get("profile")), //$NON-NLS-1$
Boolean.toString(true).equals(
debuggerProperties.get("continueAfterError")) //$NON-NLS-1$
);
return result;
}
protected String getDefaultExecutablePath(
URI relativeAbstractSyntaxTreeURI, String direction) {
URI executableURI = relativeAbstractSyntaxTreeURI.trimFileExtension()
.appendFileExtension(direction).appendFileExtension(
EXECUTABLE_SUFFIX);
if (executableURI.isPlatformResource()) {
IPath workspacePath = ResourcesPlugin.getWorkspace().getRoot().getLocation();
IPath executablePath = workspacePath.append(new Path(executableURI.toPlatformString(true)));
return executablePath.toString();
}
if (executableURI.isFile()) {
return executableURI.toFileString();
}
return null;
}
protected ASMModel createProblemModelFor(Resource abstractSyntaxTree)
throws Exception {
URI abstractSyntaxTreeURI = abstractSyntaxTree.getURI();
String problemFileName = abstractSyntaxTreeURI.trimFileExtension()
.appendFileExtension(PROBLEM_MODEL_FILE_EXTENSION).toString();
return new EMFModelLoader().newModel(PROBLEM_MODEL_NAME,
problemFileName, PROBLEM_METAMODEL);
}
protected Properties createCompilationsProperties(
URI relativeAbstractSyntaxTreeURI,
final Map<String, String> parameters, String direction) {
Properties effectiveParameters = new Properties();
effectiveParameters.putAll(parameters);
if (!effectiveParameters.containsKey(OUT_FILE_PARAMETER_NAME)) {
String executablePath = getDefaultExecutablePath(
relativeAbstractSyntaxTreeURI, direction);
effectiveParameters.put(OUT_FILE_PARAMETER_NAME, executablePath);
}
return effectiveParameters;
}
protected URI[] getSplittedSourceURI(File binFolder, File sourceFile) {
URI sourceFileURI = URI.createFileURI(sourceFile.getAbsolutePath());
URI currentFolderURI = URI.createURI("./");
URI relativeURI = null;
URI folderURI = URI.createFileURI(binFolder.getAbsolutePath())
.appendSegment("");
relativeURI = sourceFileURI.replacePrefix(folderURI, currentFolderURI);
if (relativeURI != null) {
return new URI[] { folderURI, relativeURI };
}
return new URI[] { null, null };
}
private ASMModel loadModelForTraceClassTransformation(ASMExecEnv env,
Resource relationsTransformation) throws IOException {
URI transfoURI = relationsTransformation.getURI();
EMFModelLoader modelLoader = new EMFModelLoader();
ASMModel tracebilityMetamodel = modelLoader.loadModel("Traceability",
modelLoader.getMOF(), URI
.createURI(RelationsToTraceClassPackage.eINSTANCE
.getNsURI()));
env.addModel(tracebilityMetamodel);
ASMModel traceModel = modelLoader.newModel("traces", "traces.xmi",
tracebilityMetamodel);
((ASMEMFModel) traceModel).setCheckSameModel(false);
env.addModel(traceModel);
ASMModel relationsMM = modelLoader.loadModel("relationsMM", modelLoader
.getMOF(), URI.createURI(QVTRelationPackage.eINSTANCE
.getNsURI()));
env.addModel(relationsMM);
ASMModel relations = modelLoader.loadModel("relations", relationsMM,
transfoURI);
env.addModel(relations);
ASMModel coreMM = modelLoader.getMOF();
env.addModel("coreMM", modelLoader.getMOF());
String traceClassFileName = 'T' + transfoURI.trimFileExtension()
.lastSegment();
URI traceClassURI = transfoURI.trimSegments(1).appendSegment(
traceClassFileName).appendFileExtension("ecore");
ASMModel core = modelLoader.newModel("core", traceClassURI.toString(),
coreMM);
((ASMEMFModel) core).setCheckSameModel(false);
env.addModel(core);
return core;
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.qvt.declarative.compilation.CompilationProvider#compile(java
* .lang.Object, java.util.Map, org.eclipse.core.resources.IFolder,
* org.eclipse.core.resources.IFolder)
*/
public List<File> compile(Object abstractSyntaxTree,
Map<String, String> parameters)
throws DeclarativeQVTCompilationException {
if (!(abstractSyntaxTree instanceof Resource)) {
String message = "Abstract Syntax is not a Resource: "
+ abstractSyntaxTree.toString();
DeclarativeQVTCompilationException exception = new QVTRelationsCompilationException(
message, 0, 0, 0);
throw exception;
}
Resource abstractSyntaxTreeResource = (Resource) abstractSyntaxTree;
executeRelationsToTraceClass(abstractSyntaxTreeResource);
ASMModel qvtrTransformation = null;
try {
qvtrTransformation = new EMFModelLoader().loadModel(
TRANSFORMATION_MODEL_NAME, QVTR_METAMODEL,
abstractSyntaxTreeResource.getURI());
} catch (Exception e) {
String message = "Unable to load the AST in the ATLVM : "
+ e.getMessage();
QVTRelationsCompilationException exception = new QVTRelationsCompilationException(
message, 0, 0, 0);
throw exception;
}
List<String> directions = getDirections(abstractSyntaxTreeResource);
List<File> result = new ArrayList<File>(directions.size());
for (String directionDomainName : directions) {
ASM directionASM = ASMUtils
.createDirectionLibrary(directionDomainName);
ASMModel myProblems = null;
try {
myProblems = createProblemModelFor(abstractSyntaxTreeResource);
} catch (Exception e) {
String message = "Unable to create a problem model for the ATLVM : "
+ e.getMessage();
QVTRelationsCompilationException exception = new QVTRelationsCompilationException(
message, 0, 0, 0);
throw exception;
}
Properties effectiveParameters = createCompilationsProperties(
abstractSyntaxTreeResource.getURI(), parameters,
directionDomainName);
if (qvtrTransformation != null && myProblems != null) {
File resultFile = compile(qvtrTransformation, directionASM,
myProblems, DEFAULT_DEBUGGER, effectiveParameters);
result.add(resultFile);
handleProblems(myProblems);
}
}
return result;
}
protected List<String> getDirections(Resource resource) {
List<String> result = new ArrayList<String>();
RelationalTransformation relationalTransformation = getTransformation(resource);
if (relationalTransformation != null) {
for (TypedModel typedModel : relationalTransformation.getModelParameter()) {
result.add(typedModel.getName());
}
return result;
}
return null;
}
protected void handleProblems(ASMModel problems)
throws QVTRelationsCompilationException {
Set<?> problemSet = problems.getElementsByType("Problem");
for (Object object : problemSet) {
if (object instanceof Problem) {
Problem problem = (Problem) object;
String message = problem.getDescription();
int startLine = problem.getStartLine().intValue();
int startColumn = problem.getStartColumn().intValue();
int endColumn = problem.getEndColumn().intValue();
throw new QVTRelationsCompilationException(message, startLine,
startColumn, endColumn);
}
}
}
private RelationalTransformation getTransformation(Resource resource) {
List<EObject> contents = resource.getContents();
if (!contents.isEmpty()) {
EObject eObject = resource.getContents().get(0);
if (eObject instanceof RelationalTransformation) {
return (RelationalTransformation) eObject;
}
}
return null;
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.qvt.declarative.common.framework.service.Provider#provides
* (org.eclipse.qvt.declarative.common.framework.service.Operation)
*/
public boolean provides(Operation operation) {
if (operation instanceof CompileOperation) {
CompileOperation compileOperation = (CompileOperation) operation;
Object source = compileOperation.getSource();
if (source instanceof Resource) {
Resource resource = (Resource) source;
boolean contentIsValid = getTransformation(resource) != null;
return contentIsValid;
}
}
return false;
}
protected File compile(final ASMModel qvtrTransformation,
final ASM directionLibary, final ASMModel myProblems,
final Debugger debugger, final Properties compilationParameters)
throws QVTRelationsCompilationException {
ASMModule asmModule = new ASMModule(COMPILER_ASM);
/*
* Create an execution environment with the handled models
*/
ASMExecEnv env = new ASMExecEnv(asmModule, debugger, true);
env.addPermission("file.read"); //$NON-NLS-1$
env.addPermission("file.write"); //$NON-NLS-1$
env.addModel(RELATION_METAMODEL_ALIAS, qvtrTransformation
.getMetamodel());
env.addModel(qvtrTransformation.getName(), qvtrTransformation);
env.addModel(myProblems.getMetamodel().getName(), myProblems
.getMetamodel());
env.addModel(myProblems.getName(), myProblems);
env.registerOperations(directionLibary);
env.registerOperations(COMPILER_ASM);
/*
* Launch the interpretation of the compiler on the QVTR abstract syntax
* tree
*/
try {
new ASMInterpreter(COMPILER_ASM, asmModule, env,
compilationParameters);
} catch (Exception e) {
String message = e.getMessage();
QVTRelationsCompilationException exception = new QVTRelationsCompilationException(
message, 0, 0, 0);
throw exception;
}
File resultFile = new File(compilationParameters
.getProperty(OUT_FILE_PARAMETER_NAME));
return resultFile;
}
protected void executeRelationsToTraceClass(
final Resource relationsTransformation)
throws QVTRelationsCompilationException {
ASMModule asmModule = new ASMModule(RELATION_TO_TRACE_CLASS);
/*
* Create an execution environment with the handled models
*/
ASMExecEnv env = new ASMExecEnv(asmModule, DEFAULT_DEBUGGER, true);
env.addPermission("file.read"); //$NON-NLS-1$
env.addPermission("file.write"); //$NON-NLS-1$
try {
ASMModel result = loadModelForTraceClassTransformation(env,
relationsTransformation);
env.registerOperations(RELATION_TO_TRACE_CLASS);
Map<String, String> parameters = new HashMap<String, String>();
parameters.put("enforce", "true");
new ASMInterpreter(RELATION_TO_TRACE_CLASS, asmModule, env,
parameters);
URI transfoURI = relationsTransformation.getURI();
String traceClassFileName = 'T' + transfoURI.trimFileExtension()
.lastSegment();
URI traceClassURI = transfoURI.trimSegments(1).appendSegment(
traceClassFileName).appendFileExtension("ecore");
result.getModelLoader().save(result,
traceClassURI.toPlatformString(true));
} catch (Exception e) {
String message = "Problem creating the Trace Classes \n"
+ e.getMessage();
throw new QVTRelationsCompilationException(message, 0, 0, 0);
}
}
}