/******************************************************************************* * Copyright (c) 2007 Anyware Technologies. 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: Jacques Lescot (Anyware Technologies) - initial API and * implementation * Thibault Landre (Atos Origin) - refactor to extract the exportAllDiagram from ExportAllDiagramsAction * Alexia Allanic (Atos Origin) - Add margin to not truncate images * Anass Radouani (AtoS) - add use GMF exporting tool and remove manual extraction * ******************************************************************************/ package org.eclipse.papyrus.infra.export; import java.io.File; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.eclipse.core.commands.ExecutionException; import org.eclipse.core.resources.IFile; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.IProgressMonitor; 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.core.runtime.SubProgressMonitor; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.emf.common.command.CommandStack; import org.eclipse.emf.common.notify.Notifier; import org.eclipse.emf.common.ui.dialogs.DiagnosticDialog; 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.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.emf.ecore.xmi.XMLResource; import org.eclipse.emf.edit.provider.ComposedAdapterFactory; import org.eclipse.emf.edit.provider.IItemLabelProvider; import org.eclipse.emf.edit.provider.ReflectiveItemProviderAdapterFactory; import org.eclipse.emf.transaction.RollbackException; import org.eclipse.emf.transaction.Transaction; import org.eclipse.emf.transaction.TransactionalCommandStack; import org.eclipse.emf.transaction.TransactionalEditingDomain; import org.eclipse.emf.transaction.TransactionalEditingDomain.Factory; import org.eclipse.emf.transaction.util.TransactionUtil; import org.eclipse.gmf.runtime.common.core.command.CommandResult; import org.eclipse.gmf.runtime.diagram.core.preferences.PreferencesHint; import org.eclipse.gmf.runtime.diagram.ui.image.ImageFileFormat; import org.eclipse.gmf.runtime.diagram.ui.render.util.CopyToImageUtil; import org.eclipse.gmf.runtime.emf.commands.core.command.AbstractTransactionalCommand; import org.eclipse.gmf.runtime.notation.Diagram; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.operation.IRunnableWithProgress; import org.eclipse.papyrus.commands.wrappers.GMFtoEMFCommandWrapper; import org.eclipse.papyrus.infra.export.internal.Activator; import org.eclipse.swt.SWTError; import org.eclipse.swt.widgets.Display; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.actions.WorkspaceModifyOperation; public class ExportAllDiagrams { private IFile file; private String extension; private String outputDirectoryPath; private IWorkbenchWindow workbenchWindow; private boolean displayRenamingInformation; private static boolean useDisplayRunnable = true; private boolean qualifiedName; private BasicDiagnostic diagnostic = new BasicDiagnostic(Diagnostic.OK, "", 0, Messages.ExportAllDiagrams_18, null); //$NON-NLS-1$ /** * Constructor * * @param file * the *.*di file where the diagrams are stored, can be null if * you use export method with diagrams in parameter * @param outputDirectoryPath * the directory in which the images will be saved * @param extension * the image extension * @param imageExporter * the image exporter used. The image exporter should be coherent * with the file extension */ public ExportAllDiagrams(IFile file, String outputDirectoryPath, String extension, boolean qualifiedName) { this.file = file; this.extension = extension; this.outputDirectoryPath = outputDirectoryPath; try { this.workbenchWindow = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); } catch (IllegalStateException e) { this.workbenchWindow = null; // is normal in batch mode } this.displayRenamingInformation = true; this.qualifiedName = qualifiedName; } /** * Run the export of all diagrams of a *.*di file into images in the given * format. */ public void exportDiagramsToImages() { final IRunnableWithProgress op = new WorkspaceModifyOperation() { @Override protected void execute(IProgressMonitor monitor) throws CoreException, InvocationTargetException, InterruptedException { if(monitor == null) { monitor = new NullProgressMonitor(); } final IProgressMonitor newMonitor = monitor; export(newMonitor); } }; Job job = new Job(Messages.ExportAllDiagrams_0) { @Override public IStatus run(IProgressMonitor monitor) { try { op.run(monitor); } catch (InvocationTargetException e) { } catch (InterruptedException e) { } return Status.OK_STATUS; } }; job.setUser(true); job.schedule(); } /** * Export all diagrams of the IFile * * @param newMonitor */ private void export(IProgressMonitor newMonitor) { // Then iterates on all the diagrams and export them one by one newMonitor.beginTask(Messages.ExportAllDiagrams_1, 10); newMonitor.subTask(Messages.ExportAllDiagrams_2); if(file != null) { final ResourceSetImpl resourceSet = new ResourceSetImpl(); resourceSet.getLoadOptions().put(XMLResource.OPTION_DEFER_IDREF_RESOLUTION, true); resourceSet.getLoadOptions().put(XMLResource.OPTION_DEFER_ATTACHMENT, true); resourceSet.getResource(URI.createPlatformResourceURI(file.getFullPath().toString(), true), true); // create transactional editing domain TransactionalEditingDomain editingDomain = TransactionUtil.getEditingDomain(resourceSet); if(editingDomain == null) { Factory factory = TransactionalEditingDomain.Factory.INSTANCE; editingDomain = factory.createEditingDomain(resourceSet); } AbstractTransactionalCommand com = new AbstractTransactionalCommand(editingDomain, "Resolve", Collections.emptyList()) { @Override protected CommandResult doExecuteWithResult(IProgressMonitor monitor, IAdaptable info) throws ExecutionException { EcoreUtil.resolveAll(resourceSet); return null; } }; // bypass all the transaction/validate/notification mechanisms, it is a lot faster and it has no impact // since we do not modify the model CommandStack commandStack = editingDomain.getCommandStack(); if(commandStack instanceof TransactionalCommandStack) { TransactionalCommandStack stack = (TransactionalCommandStack)commandStack; Map<Object, Object> options = new HashMap<Object, Object>(); options.put(Transaction.OPTION_NO_NOTIFICATIONS, Boolean.TRUE); options.put(Transaction.OPTION_NO_UNDO, Boolean.TRUE); options.put(Transaction.OPTION_UNPROTECTED, Boolean.TRUE); options.put(Transaction.OPTION_IS_UNDO_REDO_TRANSACTION, Boolean.FALSE); options.put(Transaction.OPTION_NO_TRIGGERS, Boolean.TRUE); options.put(Transaction.OPTION_VALIDATE_EDIT, Boolean.FALSE); options.put(Transaction.OPTION_VALIDATE_EDIT_CONTEXT, Boolean.FALSE); try { stack.execute(new GMFtoEMFCommandWrapper(com), options); } catch (InterruptedException e) { } catch (RollbackException e) { } } else { Activator.log("no transactional editing domain found", Status.WARNING); } List<Diagram> diagrams = new ArrayList<Diagram>(); if(newMonitor.isCanceled()) { return; } for(Iterator<Notifier> i = resourceSet.getAllContents(); i.hasNext();) { Notifier n = i.next(); if(n instanceof Diagram) { diagrams.add((Diagram)n); } } if(newMonitor.isCanceled()) { return; } newMonitor.worked(1); export(new SubProgressMonitor(newMonitor, 9), diagrams); } else { Activator.log(new Status(Status.ERROR, Activator.PLUGIN_ID, Messages.ExportAllDiagrams_3)); } } /** * export all the diagrams in image * * @param newMonitor * , the monitor * @param diagrams * , the emf element diagrams */ public void export(IProgressMonitor newMonitor, List<Diagram> diagrams) { boolean duplicates; newMonitor.beginTask(Messages.ExportAllDiagrams_4, 10); duplicates = createDiagramFiles(new SubProgressMonitor(newMonitor, 9), diagrams); unloadResources(new SubProgressMonitor(newMonitor, 1), diagrams); // Alert the user that file names have been changed to avoid duplicates if(duplicates && displayRenamingInformation) { final String message = Messages.ExportAllDiagrams_5; if(workbenchWindow != null && workbenchWindow.getShell() != null) { BasicDiagnostic newDiagnostic = new BasicDiagnostic(Diagnostic.WARNING, "", 0, message, null); //$NON-NLS-1$ diagnostic.add(newDiagnostic); } else { Activator.log(new Status(Status.INFO, Activator.PLUGIN_ID, message)); } } int severity = diagnostic.recomputeSeverity(); if(severity == Diagnostic.ERROR) { BasicDiagnostic oldDiagnostic = diagnostic; diagnostic = new BasicDiagnostic(Diagnostic.ERROR, "", 0, Messages.ExportAllDiagrams_22, null); //$NON-NLS-1$ diagnostic.addAll(oldDiagnostic); } else if(severity == Diagnostic.WARNING) { BasicDiagnostic oldDiagnostic = diagnostic; diagnostic = new BasicDiagnostic(Diagnostic.WARNING, "", 0, Messages.ExportAllDiagrams_24, null); //$NON-NLS-1$ diagnostic.addAll(oldDiagnostic); } else if(severity == Diagnostic.OK) { if(workbenchWindow != null && workbenchWindow.getShell() != null) { Display.getDefault().syncExec(new Runnable() { public void run() { MessageDialog.openInformation(Activator.getActiveWorkbenchShell(), Messages.ExportAllDiagrams_25, Messages.ExportAllDiagrams_26 + outputDirectoryPath); } }); } } Display.getDefault().syncExec(new Runnable() { public void run() { DiagnosticDialog.open(Activator.getActiveWorkbenchShell(), Messages.ExportAllDiagrams_27, "", diagnostic); //$NON-NLS-1$ } }); } /** * Browse all the diagrams and export them * * @param newMonitor * @param findAllDiagrams * @return */ private boolean createDiagramFiles(final IProgressMonitor newMonitor, List<Diagram> diagrams) { boolean duplicates = false; boolean nameCut = false; try { List<String> diagramNames = new ArrayList<String>(); try { newMonitor.beginTask(Messages.ExportAllDiagrams_7, diagrams.size()); for(final Diagram diagram : diagrams) { if(newMonitor.isCanceled()) { break; } String label = ""; //$NON-NLS-1$ if(qualifiedName) { ComposedAdapterFactory composedAdapterFactory = new ComposedAdapterFactory(ComposedAdapterFactory.Descriptor.Registry.INSTANCE); composedAdapterFactory.addAdapterFactory(new ReflectiveItemProviderAdapterFactory()); IItemLabelProvider itemLabelFactory = (IItemLabelProvider)composedAdapterFactory.adapt(diagram.getElement(), IItemLabelProvider.class); label = itemLabelFactory.getText(diagram.getElement()).replace(Messages.ExportAllDiagrams_16, "") + "_"; //$NON-NLS-1$//$NON-NLS-2$ } String uniqueFileName = encodeFileName(label + diagram.getName()); if(uniqueFileName.length() > 150) { nameCut = true; uniqueFileName = uniqueFileName.substring(0, 150); } if(diagramNames.contains(uniqueFileName)) { duplicates = true; uniqueFileName = getFirstAvailableName(uniqueFileName, diagramNames, 1); } if(nameCut) { BasicDiagnostic newDiagnostic = new BasicDiagnostic(Diagnostic.WARNING, "", 0, Messages.ExportAllDiagrams_10 + uniqueFileName, null); //$NON-NLS-1$ diagnostic.add(newDiagnostic); nameCut = false; } final String finalUniqueFileName = uniqueFileName; diagramNames.add(uniqueFileName); newMonitor.subTask(Messages.ExportAllDiagrams_8 + uniqueFileName); if(useDisplayRunnable) { Display.getDefault().syncExec(new Runnable() { public void run() { exportDiagram(finalUniqueFileName, diagram, newMonitor); } }); } else { exportDiagram(uniqueFileName, diagram, newMonitor); } newMonitor.worked(1); } } catch (SWTError e) { String message = Messages.ExportAllDiagrams_9; Activator.log(new Exception(message, e)); } } catch (Exception e) { e.printStackTrace(); } return duplicates; } private void exportDiagram(String uniqueFileName, Diagram diagram, IProgressMonitor newMonitor) { CopyToImageUtil copyImageUtil = new CopyToImageUtil(); try { copyImageUtil.copyToImage(diagram, new Path(outputDirectoryPath + File.separator + uniqueFileName + "." //$NON-NLS-1$ + ImageFileFormat.resolveImageFormat(extension)), ImageFileFormat.resolveImageFormat(extension), new SubProgressMonitor(newMonitor, 1), PreferencesHint.USE_DEFAULTS); } catch (Throwable e) { BasicDiagnostic newDiagnostic = new BasicDiagnostic(Diagnostic.ERROR, "", 0, String.format(Messages.ExportAllDiagrams_11, uniqueFileName, diagram.eResource().getURI().toString()), null); //$NON-NLS-1$ diagnostic.add(newDiagnostic); Activator.log(String.format(Messages.ExportAllDiagrams_11, uniqueFileName, diagram.eResource().getURI().toString()), IStatus.ERROR, e); } } public void unloadResources(IProgressMonitor newMonitor, List<Diagram> diagrams) { if(newMonitor == null) { newMonitor = new NullProgressMonitor(); } newMonitor.subTask(Messages.ExportAllDiagrams_12); if(diagrams != null && !diagrams.isEmpty()) { ResourceSet resourceSet2 = diagrams.get(0).eResource().getResourceSet(); newMonitor.beginTask(Messages.ExportAllDiagrams_13, resourceSet2.getResources().size()); for(int i = resourceSet2.getResources().size() - 1; i >= 0; i--) { try { Resource r = resourceSet2.getResources().get(i); if(r.isLoaded()) { r.unload(); } } catch (Exception e) { // not very clean but it sometimes occurs } newMonitor.worked(1); } } } /** * Escape all characters that may result in a wrong file name * * @param pathName * a file name to encode * @return The encoded file name */ private String encodeFileName(String pathName) { pathName = pathName.trim(); pathName = pathName.replaceAll(Messages.ExportAllDiagrams_14, Messages.ExportAllDiagrams_15); pathName = pathName.replaceAll("_-_", "-"); //$NON-NLS-1$ //$NON-NLS-2$ while(pathName.contains("__")) { //$NON-NLS-1$ pathName = pathName.replaceAll("__", "_"); //$NON-NLS-1$ //$NON-NLS-2$ } if(pathName.startsWith("_")) { //$NON-NLS-1$ pathName = pathName.replaceFirst("_", ""); //$NON-NLS-1$ //$NON-NLS-2$ } if(pathName.endsWith("_")) { //$NON-NLS-1$ pathName = pathName.substring(0, pathName.length() - 1); } return pathName; // return URLEncoder.encode(pathName, "UTF-8").replaceAll("[*]", "_"); } private String getFirstAvailableName(String commonBasis, List<String> existingNames, int cpt) { if(existingNames.contains(commonBasis + cpt)) { return getFirstAvailableName(commonBasis, existingNames, cpt + 1); } return commonBasis + cpt; } }