blob: e6e06324a3de82650564659fc78a487bdaef3160 [file] [log] [blame]
/*****************************************************************************
* Copyright (c) 2013, 2014 CEA LIST.
*
* 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:
* Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation
*****************************************************************************/
package org.eclipse.papyrus.interoperability.rpy.transformations;
import java.io.OutputStreamWriter;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.emf.common.util.BasicDiagnostic;
import org.eclipse.emf.common.util.Diagnostic;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.common.util.WrappedException;
import org.eclipse.emf.ecore.EAnnotation;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.ecore.util.ExtendedMetaData;
import org.eclipse.emf.ecore.xmi.XMIResource;
import org.eclipse.emf.ecore.xmi.XMLResource;
import org.eclipse.emf.ecore.xmi.impl.XMIResourceImpl;
import org.eclipse.gmf.runtime.emf.core.resources.GMFResource;
import org.eclipse.gmf.runtime.notation.Diagram;
import org.eclipse.m2m.qvt.oml.BasicModelExtent;
import org.eclipse.m2m.qvt.oml.ExecutionContext;
import org.eclipse.m2m.qvt.oml.ExecutionContextImpl;
import org.eclipse.m2m.qvt.oml.ExecutionDiagnostic;
import org.eclipse.m2m.qvt.oml.ModelExtent;
import org.eclipse.m2m.qvt.oml.TransformationExecutor;
import org.eclipse.m2m.qvt.oml.util.Trace;
import org.eclipse.m2m.qvt.oml.util.WriterLog;
import org.eclipse.papyrus.infra.emf.utils.EMFHelper;
import org.eclipse.papyrus.infra.tools.util.ListHelper;
import org.eclipse.papyrus.interoperability.common.concurrent.ExecutorsPool;
import org.eclipse.papyrus.interoperability.common.concurrent.ResourceAccessHelper;
import org.eclipse.papyrus.interoperability.rpy.Activator;
import org.eclipse.uml2.uml.resource.UMLResource;
import org.eclipse.uml2.uml.util.UMLUtil;
/**
* Executes a single RpyTool-to-Papyrus transformation
*
*
*/
public class ImportTransformations {
ResourceSetImpl resourceSet;
// SourceURI is the input
protected final URI sourceURI;
// targetURI is computed during the transformation
protected URI targetURI;
protected ModelExtent outUML, outNotation, outSashModel, primitivesCTypesModel, inPapyrusProfiles, inRpyModel,
inPapyrusLibraries, umlPrimitivesTypes, ansiCLibrary, sysML11Profile;
protected Trace executionTrace;
protected Job job;
protected Resource umlResource;
protected boolean complete = false;
protected List<Diagram> diagramsToDelete = new LinkedList<Diagram>();
protected static final ExecutorsPool executorsPool = new ExecutorsPool(2);
/**
* EPackages corresponding to source native profiles with specific support
* in the transformation
*/
protected static final Set<EPackage> sourceEPackages = new HashSet<EPackage>();
protected static final Set<String> supportedDiagramIds = new HashSet<String>();
public ImportTransformations(URI sourceURI) {
Assert.isNotNull(sourceURI);
this.sourceURI = sourceURI;
}
// MemoryLeak: Don't rely on BasicDiagnostic.toIStatus
// The source Diagnostic contains references to the QVTo ModelExtents,
// referencing the Model elements (used in #extractPapyrusProfiles())
// When using the standard conversion, these references are not discarded
protected static IStatus createStatusFromDiagnostic(Diagnostic diagnostic) {
return new Status(diagnostic.getSeverity(), diagnostic.getSource(), diagnostic.getMessage(),
diagnostic.getException());
}
/**
* Actually runs the transformation
*
* @param monitor
* @return The transformation IStatus
*
*
*/
public IStatus run() {
//
// INITIALIZATION / LOADING
//
initResourceSet();
List<ModelExtent> extents = getModelExtents();
String statusMessage = String.format("Import %s", getModelName());
MultiStatus generationStatus = new MultiStatus(Activator.PLUGIN_ID, IStatus.OK, statusMessage, null);
ExecutionContext context = createExecutionContext(generationStatus);
IStatus result; // Result of an individual transformation (Will be
// aggregated to the complete GenerationStatus)
// Diagrams
Collection<URI> transformations = getDiagramTransformationURIs();
// Semantic
// Collection<URI> transformations = getSemanticTransformationURI();
for (URI transformationURI : transformations) {
// TransformationExecutor.BlackboxRegistry.INSTANCE.registerModules(Rpy2PapyrusNotationBlackboxes.class);
TransformationExecutor executor = new TransformationExecutor(transformationURI);
ExecutionDiagnostic resultTransdo = executor.execute(context, extents.toArray(new ModelExtent[0]));
result = createStatusFromDiagnostic(resultTransdo);
System.out.print(resultTransdo.getMessage());
generationStatus.add(result);
}
if (generationStatus.getSeverity() <= Diagnostic.WARNING) {
URI notationModelURI = null;
URI sashModelURI = null;
targetURI = convertToPapyrus(sourceURI, "uml");
notationModelURI = convertToPapyrus(sourceURI, "notation");
sashModelURI = convertToPapyrus(sourceURI, "di");
umlResource = createUMLResource(resourceSet, sourceURI, targetURI);
// this resource contains the result of the qvto transfo
List<EObject> outUMLObjects = getInOutUMLModel().getContents();
umlResource.getContents().addAll(outUMLObjects);
GMFResource notationResource = new GMFResource(notationModelURI); // GMF
// Resource
// content
// type?
resourceSet.getResources().add(notationResource);
List<EObject> outNotationObjects = getInoutNotationModel().getContents();
notationResource.getContents().addAll(outNotationObjects);
XMIResource sashResource = new XMIResourceImpl(sashModelURI);
resourceSet.getResources().add(sashResource);
List<EObject> sashModelObjects = getOutSashModel().getContents();
sashResource.getContents().addAll(sashModelObjects);
configureResource(sashResource);
configureResource(notationResource);
configureResource((XMIResource) umlResource);
Collection<Resource> resourcesToSave = new HashSet<Resource>();
resourcesToSave.add(umlResource);
resourcesToSave.add(notationResource);
resourcesToSave.add(sashResource);
for (Resource resource : resourcesToSave) {
try {
cleanMetadataAnnotations(resource);
ResourceAccessHelper.INSTANCE.saveResource(resource, null);
} catch (Exception ex) {
Activator.log.error(ex);
generationStatus.add(
new Status(IStatus.ERROR, Activator.PLUGIN_ID, "An exception occurred during save", ex));
}
}
} else {
System.out.print("can not execute transfo");
}
unloadResourceSet(this.resourceSet);
this.resourceSet = null;
this.umlResource = null;
this.outNotation = this.outSashModel = this.outUML = this.primitivesCTypesModel = null;
return generationStatus;
}
protected void configureResource(XMIResource resource) {
Map<Object, Object> saveOptions = new HashMap<Object, Object>();
// default save options.
saveOptions.put(XMLResource.OPTION_DECLARE_XML, Boolean.TRUE);
saveOptions.put(XMLResource.OPTION_PROCESS_DANGLING_HREF, XMLResource.OPTION_PROCESS_DANGLING_HREF_DISCARD);
saveOptions.put(XMLResource.OPTION_SCHEMA_LOCATION, Boolean.TRUE);
saveOptions.put(XMIResource.OPTION_USE_XMI_TYPE, Boolean.TRUE);
saveOptions.put(XMLResource.OPTION_SAVE_TYPE_INFORMATION, Boolean.TRUE);
saveOptions.put(XMLResource.OPTION_SKIP_ESCAPE_URI, Boolean.FALSE);
saveOptions.put(XMLResource.OPTION_ENCODING, "UTF-8");
// see bug 397987: [Core][Save] The referenced plugin models are saved
// using relative path
saveOptions.put(XMLResource.OPTION_URI_HANDLER,
new org.eclipse.emf.ecore.xmi.impl.URIHandlerImpl.PlatformSchemeAware());
resource.setEncoding("UTF-8");
resource.getDefaultSaveOptions().putAll(saveOptions);
}
/**
* Executes the transformation (Asynchronous)
*
* @param urisToImport
*/
public URI getTargetURI() {
return targetURI;
}
/**
* Initializes the resource set, and resolve all dependencies
*/
protected void initResourceSet() {
resourceSet = new ResourceSetImpl();
synchronized (UMLUtil.class) {
UMLUtil.init(resourceSet);
}
resourceSet.getLoadOptions().put(XMLResource.OPTION_DEFER_ATTACHMENT, true);
resourceSet.getLoadOptions().put(XMLResource.OPTION_DEFER_IDREF_RESOLUTION, true);
resourceSet.getLoadOptions().put(XMLResource.OPTION_RECORD_UNKNOWN_FEATURE, Boolean.TRUE);
resourceSet.getLoadOptions().put(XMLResource.OPTION_USE_PACKAGE_NS_URI_AS_LOCATION, Boolean.FALSE);
try {
resourceSet.getResource(sourceURI, true);
} catch (Exception ex) {
Activator.log.error("An error occurred while loading " + getModelName(), ex);
}
}
// protected ModelExtent getInPapyrusProfiles() {
// if (inPapyrusProfiles == null) {
// loadInPapyrusProfiles();
// }
//
// return inPapyrusProfiles;
// }
// protected ModelExtent getInPapyrusLibraries() {
// if (inPapyrusLibraries == null) {
// loadInPapyrusLibraries();
// }
//
// return inPapyrusLibraries;
// }
protected Diagnostic loadInPapyrusProfiles() {
if (inPapyrusProfiles != null) {
return Diagnostic.OK_INSTANCE;
}
List<String> missingProfiles = new LinkedList<String>();
List<EObject> allContents = new LinkedList<EObject>();
// try {
// URI fcmProfile1 =
// URI.createURI("pathmap://FCM_PROFILES/FCM.profile.uml");
// Resource fcmProfile = resourceSet.getResource(fcmProfile1, true);
// checkResource(fcmProfile);
// allContents.addAll(fcmProfile.getContents());
// } catch (WrappedException ex) {
// missingProfiles.add("FCM Profile");
// }
inPapyrusProfiles = new BasicModelExtent(allContents);
String message;
int code;
if (missingProfiles.isEmpty()) {
message = "OK";
code = Diagnostic.OK;
} else {
message = "The following Papyrus profiles cannot be found: "
+ ListHelper.deepToString(missingProfiles, ", ");
code = Diagnostic.ERROR;
}
Diagnostic diagnostic = new BasicDiagnostic(code, Activator.PLUGIN_ID, code, message, null);
return diagnostic;
}
protected Diagnostic loadInPapyrusLibraries() {
if (inPapyrusLibraries != null) {
return Diagnostic.OK_INSTANCE;
}
List<String> missingLibraries = new LinkedList<String>();
List<EObject> allContents = new LinkedList<EObject>();
try {
URI ancyLib = URI.createURI("pathmap://PapyrusC_Cpp_LIBRARIES/AnsiCLibrary.uml");
Resource ancyLibRes = resourceSet.getResource(ancyLib, true);
checkResource(ancyLibRes);
allContents.addAll(ancyLibRes.getContents());
} catch (WrappedException ex) {
missingLibraries.add("Ancy C Library");
}
inPapyrusLibraries = new BasicModelExtent(allContents);
String message;
int code;
if (missingLibraries.isEmpty()) {
message = "OK";
code = Diagnostic.OK;
} else {
message = "The following Papyrus libraries cannot be found: "
+ ListHelper.deepToString(missingLibraries, ", ");
code = Diagnostic.ERROR;
}
Diagnostic diagnostic = new BasicDiagnostic(code, Activator.PLUGIN_ID, code, message, null);
return diagnostic;
}
protected void checkResource(Resource resource) {
Assert.isNotNull(resource);
Assert.isTrue(!resource.getContents().isEmpty(), "The resource " + resource.getURI() + " is empty");
for (EObject rootElement : resource.getContents()) {
Assert.isTrue(!rootElement.eIsProxy());
}
}
protected Resource createUMLResource(ResourceSet resourceSet, URI sourceResourceURI, URI targetResourceURI) {
Resource resource = resourceSet.createResource(targetResourceURI,
UMLResource.UML_5_0_0_CONTENT_TYPE_IDENTIFIER);
return resource;
}
protected URI convertToPapyrus(URI rhpURI, String extension) {
return rhpURI.trimFileExtension().appendFileExtension(extension);
}
// the transfo parameters :
protected List<ModelExtent> getModelExtents() {
List<ModelExtent> allExtents = new LinkedList<ModelExtent>();
allExtents.add(getInRpyModel());
allExtents.add(getInoutNotationModel());
allExtents.add(getInOutUMLModel());
allExtents.add(getPrimitivesCUMLModel());
allExtents.add(getSysML1_1Profile());
allExtents.add(getInUMLPrimitivesTypes());
return allExtents;
}
public ModelExtent getInUMLPrimitivesTypes() {
URI umlPrimitivesTypesURI = URI.createURI("pathmap://UML_LIBRARIES/UMLPrimitiveTypes.library.uml");
Resource umlPrimitivesTypesResource = resourceSet.getResource(umlPrimitivesTypesURI, true);
umlPrimitivesTypes = new BasicModelExtent(umlPrimitivesTypesResource.getContents());
return umlPrimitivesTypes;
}
// public ModelExtent getANSICLibrary() {
// URI umlPrimitivesTypesURI = URI.createURI("pathmap://PapyrusC_Cpp_LIBRARIES/AnsiCLibrary.uml");
// Resource umlPrimitivesTypesResource = resourceSet.getResource(umlPrimitivesTypesURI, true);
// ansiCLibrary = new BasicModelExtent(umlPrimitivesTypesResource.getContents());
// return ansiCLibrary;
// }
public ModelExtent getInRpyModel() {
if (inRpyModel == null) {
Resource resource = resourceSet.getResource(sourceURI, true);
inRpyModel = new BasicModelExtent(resource.getContents());
}
return inRpyModel;
}
public ModelExtent getSysML1_1Profile() {
URI sysMLProfile = URI.createURI("pathmap://SysML_PROFILES/SysML.profile.uml");
Resource sysProfile = resourceSet.getResource(sysMLProfile, true);
sysML11Profile = new BasicModelExtent(sysProfile.getContents());
return sysML11Profile;
}
public ModelExtent getPrimitivesCUMLModel() {
URI primitiveTypesURI = URI.createURI("pathmap://PapyrusC_Cpp_LIBRARIES/AnsiCLibrary.uml");
Resource primitiveTypes = resourceSet.getResource(primitiveTypesURI, true);
primitivesCTypesModel = new BasicModelExtent(primitiveTypes.getContents());
return primitivesCTypesModel;
}
public ModelExtent getInOutUMLModel() {
if (outUML == null)
outUML = new BasicModelExtent();
return outUML;
}
/*
* Notation model is initially empty, but will be filled successively by
* each transformation
*/
public ModelExtent getInoutNotationModel() {
if (outNotation == null) {
outNotation = new BasicModelExtent();
}
return outNotation;
}
protected ModelExtent getOutSashModel() {
if (outSashModel == null) {
outSashModel = new BasicModelExtent();
}
return outSashModel;
}
static {
supportedDiagramIds.addAll(Arrays.asList(new String[] { "Class", // Includes
// Profiles
"Statechart", }));
}
// TODO never call, called from the Rpy2PapyrusNotation transform to make the link between uml created elements and their representation
protected Collection<URI> getSemanticTransformationURI() {
return ListHelper.asList(new URI[] { getTransformationURI("Rpy2PapyrusSemanticElements"),
});
}
protected Collection<URI> getDiagramTransformationURIs() {
return ListHelper.asList(new URI[] { getTransformationURI("Rpy2PapyrusNotation"),
});
}
protected URI getTransformationURI(String transformationName) {
return URI.createPlatformPluginURI(
String.format("%s/transform/%s.qvto", Activator.PLUGIN_ID, transformationName), true); //$NON-NLS-1$
}
public String getModelName() {
return URI.decode(sourceURI.lastSegment());
}
protected ExecutionContext createExecutionContext(final MultiStatus generationStatus) {
ExecutionContextImpl context = new ExecutionContextImpl();
context.setConfigProperty("keepModeling", true); //$NON-NLS-1$
// TODO : exist in RSA, isn't it ?
// context.setConfigProperty(TransformationUI.MONITOR);
context.setLog(new WriterLog(new OutputStreamWriter(System.out)) {
@Override
public void log(String message) {
super.log(message);
}
@Override
public void log(String message, Object param) {
super.log(message, param);
}
@Override
public void log(int level, String message) {
super.log(level, message);
if (level >= 1) {
generationStatus.merge(new Status(level, Activator.PLUGIN_ID, message));
}
}
@Override
public void log(int level, String message, Object param) {
super.log(level, message, param);
if (level >= 1) {
generationStatus.merge(new Status(level, Activator.PLUGIN_ID, message + ", data:" + param));
}
}
});
return context;
}
protected void unloadResourceSet(ResourceSet resourceSet) {
EMFHelper.unload(resourceSet);
}
/**
* @param resource
*/
private void cleanMetadataAnnotations(Resource resource) {
// Bug 471684: UML2.x to UML2.5 creates (invalid) Ecore Metadata
// EAnnotations, which then cause OCL validation to fail
// Remove these EAnnotations from the model to avoid side effects
Iterator<EObject> rootElementsIterator = resource.getContents().iterator();
while (rootElementsIterator.hasNext()) {
EObject root = rootElementsIterator.next();
if (root instanceof EAnnotation) {
EAnnotation annotation = (EAnnotation) root;
if (ExtendedMetaData.ANNOTATION_URI.equals(annotation.getSource())) {
rootElementsIterator.remove();
}
}
}
}
}