blob: 2cdca6a3ac9b74e7d92e7ecbe72108283b74c895 [file] [log] [blame]
/*****************************************************************************
* Copyright (c) 2010 Atos Origin.
*
*
* 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:
* Alexia Allanic (Atos Origin) alexia.allanic@atosorigin.com - Initial API and implementation
* Papa Malick Wade (Atos Origin) papa-malick.wade@atosorigin.com - extension the format of diagrams
*****************************************************************************/
package org.eclipse.gendoc.bundle.acceleo.gmf.impl;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Stack;
import org.eclipse.core.databinding.observable.Realm;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.emf.common.util.BasicDiagnostic;
import org.eclipse.emf.common.util.Diagnostic;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.transaction.TransactionalEditingDomain;
import org.eclipse.emf.transaction.TransactionalEditingDomain.Factory;
import org.eclipse.emf.transaction.util.TransactionUtil;
import org.eclipse.emf.workspace.util.WorkspaceSynchronizer;
import org.eclipse.gef.ConnectionEditPart;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.GraphicalEditPart;
import org.eclipse.gendoc.documents.FileRunnable;
import org.eclipse.gendoc.documents.IImageManipulationService;
import org.eclipse.gendoc.documents.IImageManipulationServiceFactory;
import org.eclipse.gendoc.services.GendocServices;
import org.eclipse.gendoc.services.IGendocDiagnostician;
import org.eclipse.gendoc.services.ILogger;
import org.eclipse.gendoc.tags.handlers.Activator;
import org.eclipse.gmf.runtime.common.core.util.Trace;
import org.eclipse.gmf.runtime.diagram.core.preferences.PreferencesHint;
import org.eclipse.gmf.runtime.diagram.core.util.ViewUtil;
import org.eclipse.gmf.runtime.diagram.ui.editparts.DiagramEditPart;
import org.eclipse.gmf.runtime.diagram.ui.image.ImageFileFormat;
import org.eclipse.gmf.runtime.diagram.ui.parts.DiagramEditor;
import org.eclipse.gmf.runtime.diagram.ui.render.clipboard.DiagramGenerator;
import org.eclipse.gmf.runtime.diagram.ui.render.internal.DiagramUIRenderPlugin;
import org.eclipse.gmf.runtime.diagram.ui.render.util.CopyToImageUtil;
import org.eclipse.gmf.runtime.diagram.ui.util.DiagramEditorUtil;
import org.eclipse.gmf.runtime.notation.Diagram;
import org.eclipse.gmf.runtime.notation.View;
import org.eclipse.jface.databinding.swt.SWTObservables;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
public class GMFDiagramRunnable implements FileRunnable {
private Diagram diagram;
private enum FileFormat {
PNG, JPEG, GIF, BMP, JPG, SVG, EMF;
public FileFormat getExtension(){
IImageManipulationServiceFactory imageManipulationServiceFactory = GendocServices.getDefault().getService(IImageManipulationServiceFactory.class);
IImageManipulationService imageManipulationService = imageManipulationServiceFactory.getService(name().toLowerCase()) ;
return transformToFormat(imageManipulationService.renameExtension(name()));
}
public String getFullExtension() {
return "." + getExtension().name().toLowerCase();
}
}
/** The extension of diagram. */
private FileFormat extension;
private final List<EObject> visibleElements;
public GMFDiagramRunnable(Diagram diagram, String extension) {
this(diagram, extension, null);
}
public GMFDiagramRunnable(Diagram diagram, String extension,
List<EObject> visibleElements) {
this.diagram = diagram;
this.extension = transformToFormat(extension);
if (visibleElements != null) {
this.visibleElements = visibleElements;
} else {
this.visibleElements = Collections.emptyList();
}
}
private static FileFormat transformToFormat(String ext) {
FileFormat format;
try {
format = FileFormat.valueOf(ext.toUpperCase());
return format;
} catch (IllegalArgumentException e) {
IGendocDiagnostician diagnostician = GendocServices.getDefault()
.getService(IGendocDiagnostician.class);
String message = "The format " + ext + " is not supported";
diagnostician.addDiagnostic(new BasicDiagnostic(Diagnostic.ERROR,
Activator.PLUGIN_ID, 0, message, null));
return FileFormat.valueOf(ext);
}
}
/**
* Instantiates a new diagram runnable.
*
* @param diagram
* the diagram
*/
public void run(final String resourceId, final String outputResourceFolder) {
if (Realm.getDefault() == null) {
Realm.runWithDefault(SWTObservables.getRealm(Display.getDefault()), new Runnable() {
public void run() {
doRun(resourceId, outputResourceFolder);
}
});
}
else {
doRun(resourceId, outputResourceFolder);
}
}
protected void doRun(String resourceId, String outputResourceFolder) {
if (extension != null) {
MultiElementsCopytoImageUtils c = new MultiElementsCopytoImageUtils();
new File(outputResourceFolder).mkdirs();
IPath path = new Path(outputResourceFolder + "/" + resourceId
+ extension.getFullExtension());
try {
// this part of code is necessary for Transactional Editing
// Domain by GMF
// if this section is removed a NPE occurs in copyToImage
// Function (no Editing Domain)
{
Resource eResource = diagram.eResource();
if (eResource != null) {
ResourceSet resourceSet = eResource.getResourceSet();
if (TransactionUtil.getEditingDomain(resourceSet) == null) {
Factory factory = TransactionalEditingDomain.Factory.INSTANCE;
factory.createEditingDomain(resourceSet);
}
}
}
if (visibleElements == null || visibleElements.isEmpty()) {
c.copyToImage(diagram, path, getImageFileFormat(extension.getExtension()),
new NullProgressMonitor(),
PreferencesHint.USE_DEFAULTS);
} else {
c.copyToImage(diagram, path, visibleElements,
getImageFileFormat(extension.getExtension()),
new NullProgressMonitor(),
PreferencesHint.USE_DEFAULTS);
}
IImageManipulationServiceFactory imageManipulationServiceFactory = GendocServices.getDefault().getService(IImageManipulationServiceFactory.class);
IImageManipulationService imageManipulationService = imageManipulationServiceFactory.getService(extension.name().toLowerCase()) ;
imageManipulationService.transform(path);
} catch (CoreException e) {
IGendocDiagnostician diag = GendocServices.getDefault().getService(IGendocDiagnostician.class);
if (diag != null)
{
diag.addDiagnostic(IStatus.WARNING,"no image can be generated for Diagram : " + diagram.toString(), new Object[]{diagram} );
}
e.printStackTrace();
} catch (Exception ex) {
IGendocDiagnostician diag = GendocServices.getDefault().getService(IGendocDiagnostician.class);
if (diag != null)
{
diag.addDiagnostic(IStatus.WARNING,"no image can be generated for Diagram : " + diagram.toString(), new Object[]{diagram} );
}
ex.printStackTrace();
}
}
}
private ImageFileFormat getImageFileFormat(FileFormat format) {
return ImageFileFormat.resolveImageFormat(format.name());
}
public String getFileExtension() {
return extension.toString().toLowerCase();
}
/**
* A subclass to manage list of elements and diagram in parameter
*
* @author tfaure
*
*/
public class MultiElementsCopytoImageUtils extends CopyToImageUtil {
public List copyToImage(Diagram diagram, IPath destination,
ImageFileFormat format, NullProgressMonitor monitor,
PreferencesHint preferencesHint)
throws CoreException {
Trace.trace(DiagramUIRenderPlugin.getInstance(),
"Copy diagram to Image " + destination + " as " + format); //$NON-NLS-1$ //$NON-NLS-2$
List partInfo = Collections.EMPTY_LIST;
DiagramEditor openedDiagramEditor = findOpenedDiagramEditor(diagram);
if (openedDiagramEditor != null) {
DiagramGenerator generator = copyToImage(openedDiagramEditor.getDiagramEditPart(),
destination, format, monitor);
partInfo = generator.getDiagramPartInfo(openedDiagramEditor.getDiagramEditPart());
} else {
Shell shell = new Shell();
try {
DiagramEditPart diagramEditPart = createDiagramEditPart(diagram,
shell, preferencesHint);
Assert.isNotNull(diagramEditPart);
DiagramGenerator generator = copyToImage(diagramEditPart,
destination, format, monitor);
partInfo = generator.getDiagramPartInfo(diagramEditPart);
} finally {
shell.dispose();
}
}
return partInfo;
}
private DiagramEditor findOpenedDiagramEditor(Diagram diagram) {
DiagramEditor result = DiagramEditorUtil.findOpenedDiagramEditorForID(ViewUtil.getIdStr(diagram));
if (result != null){
IPath iPathDiagEditor =getIPath(result.getDiagram());
IPath iPathDiag = getIPath(diagram) ;
if (iPathDiagEditor == null || iPathDiag == null || !iPathDiag.equals(iPathDiagEditor)){
((ILogger) GendocServices.getDefault().getService(ILogger.class)).log("Two diagrams in separate files " + iPathDiagEditor + " and " + iPathDiag + " have the same identifier", Status.WARNING);
return null ;
}
}
return result ;
}
private IPath getIPath(Diagram diagram) {
if (diagram != null){
Resource resource = diagram.eResource();
if (resource != null){
IFile file = WorkspaceSynchronizer.getUnderlyingFile(resource);
if (file != null){
return file.getFullPath();
}
}
}
return null;
}
public List copyToImage(Diagram diagram, IPath destination,
List<EObject> visibleElements, ImageFileFormat format,
NullProgressMonitor monitor, PreferencesHint preferencesHint)
throws CoreException {
Shell shell = null ;
try {
Trace.trace(DiagramUIRenderPlugin.getInstance(),
"Copy diagram to Image " + destination + " as " + format); //$NON-NLS-1$ //$NON-NLS-2$
List partInfo = Collections.EMPTY_LIST;
DiagramEditor openedDiagramEditor = findOpenedDiagramEditor(diagram);
DiagramEditPart diagramEditPart = null ;
if (openedDiagramEditor != null) {
diagramEditPart = openedDiagramEditor.getDiagramEditPart();
} else {
shell = new Shell();
diagramEditPart = createDiagramEditPart(
diagram, shell, preferencesHint);
}
Assert.isNotNull(diagramEditPart);
copyToImage(diagramEditPart,
getEditParts(visibleElements, diagramEditPart),
destination, format, monitor);
return partInfo;
} finally {
if (shell != null && !shell.isDisposed())
{
shell.dispose();
}
}
}
}
public List<?> getEditParts(List<EObject> visibleElements,
DiagramEditPart diagramEditPart) {
return getEditParts(visibleElements, diagramEditPart, true);
}
public List<?> getEditParts(List<EObject> visibleElements,
DiagramEditPart diagramEditPart, boolean includeConnections) {
List<GraphicalEditPart> result = new LinkedList<GraphicalEditPart>();
for (EObject e : visibleElements) {
Object model = diagramEditPart.getModel();
if (model instanceof Diagram) {
Diagram diagram = (Diagram) model;
for (TreeIterator<EObject> i = EcoreUtil.getAllProperContents(
diagram, true); i.hasNext();) {
EObject current = i.next();
if (current instanceof View) {
View view = (View) current;
if (equals(e, view.getElement())) {
Object part = diagramEditPart.getViewer()
.getEditPartRegistry().get(view);
if (part instanceof GraphicalEditPart) {
result.add((GraphicalEditPart) part);
}
}
}
}
}
}
if (includeConnections)
{
// the process is made twice but copying the code
// in this case is better than overriding to avoid maintenance problem
ArrayList<GraphicalEditPart> tmp = new ArrayList<GraphicalEditPart>(result);
for(GraphicalEditPart g : tmp)
{
result.addAll(findConnectionsToPaint(g, result));
}
}
return result;
}
protected boolean equals(EObject e, EObject fromView) {
boolean result = false ;
if (fromView == e)
{
result = true ;
}
else
{
// if the diagram editor is the opened one the eobjects are not the same
if (e.eResource() != null && fromView.eResource() != null)
{
Resource eResource = e.eResource();
Resource viewResoure = fromView.eResource();
if (eResource.getURI() != null && eResource.getURI().equals(viewResoure.getURI()))
{
result = (eResource.getURIFragment(e) != null && eResource.getURIFragment(e).equals(viewResoure.getURIFragment(fromView)));
}
}
}
return result ;
}
/**
* Collects all connections contained within the given edit part
* Code copy from {@link CopyToImageUtil}
* @param editPart
* the container editpart
* @return connections within it
*/
protected Collection<ConnectionEditPart> findConnectionsToPaint(
GraphicalEditPart editPart, List<GraphicalEditPart> relatedEditParts) {
/*
* Set of node editparts contained within the given editpart
*/
HashSet<GraphicalEditPart> editParts = new HashSet<GraphicalEditPart>();
/*
* All connection editparts that have a source contained within the
* given editpart
*/
HashSet<ConnectionEditPart> connectionEPs = new HashSet<ConnectionEditPart>();
/*
* Connections contained within the given editpart (or just the
* connections to paint
*/
HashSet<ConnectionEditPart> connectionsToPaint = new HashSet<ConnectionEditPart>();
/*
* Populate the set of node editparts
*/
getNestedEditParts(editPart, editParts);
/*
* Populate the set of connections whose source is within the given
* editpart
*/
for (GraphicalEditPart gep : editParts) {
connectionEPs.addAll(getAllConnectionsFrom(gep));
}
/*
* Populate the set of connections whose source is the given editpart
*/
connectionEPs.addAll(getAllConnectionsFrom(editPart));
/*
* Create a set of connections constained within the given editpart
*/
while (!connectionEPs.isEmpty()) {
/*
* Take the first connection and check whethe there is a path
* through that connection that leads to the target contained within
* the given editpart
*/
Stack<ConnectionEditPart> connectionsPath = new Stack<ConnectionEditPart>();
ConnectionEditPart conn = connectionEPs.iterator().next();
connectionEPs.remove(conn);
connectionsPath.add(conn);
/*
* Initialize the target for the current path
*/
EditPart target = conn.getTarget();
while (connectionEPs.contains(target)) {
/*
* If the target end is a connection, check if it's one of the
* connection's whose target is a connection and within the
* given editpart. Append it to the path if it is. Otherwise
* check if the target is within the actual connections or nodes
* contained within the given editpart
*/
ConnectionEditPart targetConn = (ConnectionEditPart) target;
connectionEPs.remove(targetConn);
connectionsPath.add(targetConn);
/*
* Update the target for the new path
*/
target = targetConn.getTarget();
}
/*
* The path is built, check if it's target is a node or a connection
* contained within the given editpart
*/
if (editParts.contains(target)
|| connectionsToPaint.contains(target)
|| relatedEditParts.contains(target)) {
connectionsToPaint.addAll(connectionsPath);
}
}
return connectionsToPaint;
}
/**
* This method is used to obtain the list of child edit parts for shape
* compartments.
*
* @param childEditPart
* base edit part to get the list of children editparts
* @param editParts
* list of nested shape edit parts
*/
protected void getNestedEditParts(GraphicalEditPart baseEditPart,
Collection<GraphicalEditPart> editParts) {
for (Object child : baseEditPart.getChildren()) {
if (child instanceof GraphicalEditPart) {
GraphicalEditPart childEP = (GraphicalEditPart) child;
editParts.add(childEP);
getNestedEditParts(childEP, editParts);
}
}
}
/**
* Returns all connections orginating from a given editpart. All means that
* connections originating from connections that have a source given
* editpart will be included
*
* @param ep
* the editpart
* @return all source connections
*/
protected List<ConnectionEditPart> getAllConnectionsFrom(
GraphicalEditPart ep) {
LinkedList<ConnectionEditPart> connections = new LinkedList<ConnectionEditPart>();
for (Object sourceConnObj : ep.getSourceConnections()) {
if (sourceConnObj instanceof ConnectionEditPart) {
ConnectionEditPart sourceConn = (ConnectionEditPart) sourceConnObj;
connections.add(sourceConn);
connections.addAll(getAllConnectionsFrom(sourceConn));
}
}
return connections;
}
}