blob: f23ccc8290f1b7c8b1c43979e7141890ef8bdd3b [file] [log] [blame]
/*****************************************************************************
* Copyright (c) 2019 CEA LIST and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Vincent Lorenzo (CEA LIST) vincent.lorenzo@cea.fr - Initial API and implementation
* Vincent Lorenzo (CEA LIST) - bug 549183
*****************************************************************************/
package org.eclipse.papyrus.model2doc.integration.nattable.template2structure.internal.mapping;
import java.io.File;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.xmi.XMIResource;
import org.eclipse.nebula.widgets.nattable.NatTable;
import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry;
import org.eclipse.nebula.widgets.nattable.style.DisplayMode;
import org.eclipse.osgi.util.NLS;
import org.eclipse.papyrus.infra.core.sashwindows.di.service.IPageManager;
import org.eclipse.papyrus.infra.core.services.ServiceException;
import org.eclipse.papyrus.infra.core.services.ServicesRegistry;
import org.eclipse.papyrus.infra.emf.utils.ServiceUtilsForEObject;
import org.eclipse.papyrus.infra.nattable.export.image.ImageFormat;
import org.eclipse.papyrus.infra.nattable.manager.table.INattableModelManager;
import org.eclipse.papyrus.infra.nattable.manager.table.ITreeNattableModelManager;
import org.eclipse.papyrus.infra.nattable.manager.table.NattableModelManager;
import org.eclipse.papyrus.infra.nattable.model.nattable.Table;
import org.eclipse.papyrus.infra.nattable.model.nattable.nattableaxis.ITreeItemAxis;
import org.eclipse.papyrus.infra.nattable.model.nattable.nattableaxisconfiguration.TreeFillingConfiguration;
import org.eclipse.papyrus.infra.nattable.parsers.CSVParser;
import org.eclipse.papyrus.infra.nattable.parsers.CellIterator;
import org.eclipse.papyrus.infra.nattable.parsers.RowIterator;
import org.eclipse.papyrus.infra.nattable.style.configattribute.PapyrusExportConfigAttributes;
import org.eclipse.papyrus.infra.nattable.tree.CollapseAndExpandActionsEnum;
import org.eclipse.papyrus.infra.nattable.utils.AxisUtils;
import org.eclipse.papyrus.infra.nattable.utils.CSVPasteHelper;
import org.eclipse.papyrus.infra.nattable.utils.TableClipboardUtils;
import org.eclipse.papyrus.infra.ui.editor.IMultiDiagramEditor;
import org.eclipse.papyrus.model2doc.core.builtintypes.BasicRow;
import org.eclipse.papyrus.model2doc.core.builtintypes.BuiltInTypesFactory;
import org.eclipse.papyrus.model2doc.core.builtintypes.CellLocation;
import org.eclipse.papyrus.model2doc.core.builtintypes.TextCell;
import org.eclipse.papyrus.model2doc.core.generatorconfiguration.IDocumentStructureGeneratorConfiguration;
import org.eclipse.papyrus.model2doc.core.generatorconfiguration.operations.GeneratorConfigurationOperations;
import org.eclipse.papyrus.model2doc.emf.documentstructure.BodyPart;
import org.eclipse.papyrus.model2doc.emf.documentstructure.DocumentStructureFactory;
import org.eclipse.papyrus.model2doc.emf.documentstructure.ExtendedBasicTable;
import org.eclipse.papyrus.model2doc.emf.documentstructure.ExtendedTextCell;
import org.eclipse.papyrus.model2doc.emf.documentstructure.Image;
import org.eclipse.papyrus.model2doc.emf.documentstructure.Title;
import org.eclipse.papyrus.model2doc.emf.documentstructuretemplate.DocumentTemplate;
import org.eclipse.papyrus.model2doc.emf.documentstructuretemplate.utils.DocumentStructureTemplateUtils;
import org.eclipse.papyrus.model2doc.emf.template2structure.mapping.AbstractTemplateToStructureMapper;
import org.eclipse.papyrus.model2doc.emf.template2structure.mapping.IMappingService;
import org.eclipse.papyrus.model2doc.integration.nattable.documentstructuretemplate.PapyrusNattableDocumentStructureTemplatePackage;
import org.eclipse.papyrus.model2doc.integration.nattable.documentstructuretemplate.PapyrusTableView;
import org.eclipse.papyrus_model2doc.integration.nattable.template2structure.Activator;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IEditorPart;
/**
* This class create the DocumentStructure {@link Image} or Table from a {@link PapyrusTableView}
*/
public class PapyrusTableViewMapper extends AbstractTemplateToStructureMapper<PapyrusTableView> {
/**
* Constructor.
*
* @param inputEClass
* @param outputClass
*/
public PapyrusTableViewMapper() {
super(PapyrusNattableDocumentStructureTemplatePackage.eINSTANCE.getPapyrusTableView(), BodyPart.class);
}
/**
*
* @see org.eclipse.papyrus.model2doc.emf.template2structure.mapping.AbstractTemplateToStructureMapper#doMap(org.eclipse.papyrus.model2doc.emf.template2structure.mapping.IMappingService, org.eclipse.emf.ecore.EObject, org.eclipse.emf.ecore.EObject,
* java.lang.Class)
*
* @param <T>
* @param mappingService
* @param papyrusTableView
* @param semanticModelElement
* @param returnedClassType
* @return
*/
@Override
protected <T> List<T> doMap(final IMappingService mappingService, final PapyrusTableView papyrusTableView, final EObject semanticModelElement, final Class<T> returnedClassType) {
if (false == papyrusTableView.generateBranch(semanticModelElement)) {
return Collections.emptyList();
}
if (false == papyrusTableView.isGenerate()) {
// This element can't have children, so if isGenerate() returns false, we go out!
return Collections.emptyList();
}
final List<T> returnedValue = new ArrayList<>();
final Iterator<Table> tableIter = papyrusTableView.getMatchingTables(semanticModelElement).iterator();
Title title = null;
if (tableIter.hasNext() && papyrusTableView.isGenerateTitle()) {
title = DocumentStructureFactory.eINSTANCE.createTitle();
title.setTitle(papyrusTableView.buildPartTemplateTitle(semanticModelElement));
returnedValue.add(returnedClassType.cast(title));
}
while (tableIter.hasNext()) {
Table current = tableIter.next();
// the table mapping
// we log info, because it can take times
Activator.log.info(NLS.bind("Start the export of the table {0}.", current.getName())); //$NON-NLS-1$
final BodyPart mapResult = mapTable(papyrusTableView, current);
Activator.log.info(NLS.bind("End the export of the table {0}.", current.getName())); //$NON-NLS-1$
if (null == mapResult) {
Activator.log.warn(NLS.bind("We fail to import the table {0}.", current.getName())); //$NON-NLS-1$
}
if (null == title) {
returnedValue.add(returnedClassType.cast(mapResult));
} else {
title.getSubBodyParts().add(mapResult);
}
}
return returnedValue;
}
/**
*
* @param papyrusTableView
* the view to map
* @param table
* a table to import in the document
* @return
* a body part representing the table, or <code>null</code> in case of failure
*/
private BodyPart mapTable(final PapyrusTableView papyrusTableView, final Table table) {
// 1. get the page manager
final IPageManager pageManager = getIPageManager(table);
if (null == pageManager) {
return null;
}
// 2. open the table
boolean closePage = false;
if (pageManager.isOpen(table)) {
pageManager.selectPage(table);
} else {
pageManager.openPage(table);
closePage = true;
}
// 3. wait for the table opening
flushEventLoop();
// 4. get the table manager
final INattableModelManager manager = getINattableModelManager(table);
// 5. expand all the tables in case of TreeTable
if (manager instanceof ITreeNattableModelManager) {
((ITreeNattableModelManager) manager).doCollapseExpandAction(CollapseAndExpandActionsEnum.EXPAND_ALL, Collections.emptyList());
flushEventLoop();
}
// 6. call method to create the output BodyPart
final BodyPart bodyPart;
switch (papyrusTableView.getImportMethod()) {
case IMAGE:
bodyPart = mapTableAsImage(manager, papyrusTableView, table);
break;
case TABLE: // this is the default case
default:
bodyPart = mapTableAsTable(manager, papyrusTableView, table);
}
// 7. close the table if required
if (closePage) {
// TODO : uncomment me when the class cast exception due to Papyrus eclipseCopy package element will be fixed
// pageManager.closePage(table);
}
// 8. we reset the current page (which was the documenttemplate!)
// TODO : uncomment me when the class cast exception due to Papyrus eclipseCopy package element will be fixed
// pageManager.selectPage(DocumentStructureTemplateUtils.getDocumentTemplate(papyrusTableView));
// flushEventLoop();
return bodyPart;
}
/**
* This string is used to indicate we go down from 1 level in a tree table
*/
private static final String TREE_LEVEL_MARKER = " ";
/**
*
* @param papyrusTableView
* the {@link PapyrusTableView}
* @param pTable
* a {@link Table} found for the {@link PapyrusTableView}
* @return
* the table as Image
*/
private ExtendedBasicTable mapTableAsTable(final INattableModelManager manager, final PapyrusTableView papyrusTableView, final Table pTable) {
final boolean isTreeTable = manager instanceof ITreeNattableModelManager;
// 1. select all the table content
manager.selectAll();
// 2. copy the table contents to the clipboard
((NattableModelManager) manager).copyToClipboard();
// 3. get the cliboard contents
final String clipboardContent = TableClipboardUtils.getClipboardContentsAsString();
final StringReader reader = new StringReader(clipboardContent);
final CSVPasteHelper helper = new CSVPasteHelper();
final CSVParser parser = helper.createParser(reader, true);
// 4. create the label provider used to get header label
final NatTable nat = manager.getAdapter(NatTable.class);
final CustomNattableLabelProvider labelProvider = new CustomNattableLabelProvider(nat.getConfigRegistry());
// 5. create a basic table
final ExtendedBasicTable basicTable = DocumentStructureFactory.eINSTANCE.createExtendedBasicTable();
basicTable.setCaption(pTable.getName());
// 6. create the row of column header
final Iterator<Object> columnIter = manager.getColumnElementsList().iterator();
final BasicRow columnRowHeader = BuiltInTypesFactory.eINSTANCE.createBasicRow();
basicTable.getRows().add(columnRowHeader);
// 7. we create the corner cell
final ExtendedTextCell cellCorner = DocumentStructureFactory.eINSTANCE.createExtendedTextCell();
columnRowHeader.getCells().add(cellCorner);
cellCorner.setLocation(CellLocation.CORNER);
// 8. we fill the column header
while (columnIter.hasNext()) {
final Object current = columnIter.next();
final String res = labelProvider.getColumnHeaderLabel(current);
final TextCell cell = DocumentStructureFactory.eINSTANCE.createExtendedTextCell();
cell.setLocation(CellLocation.COLUMN_HEADER);
cell.setText(res);
columnRowHeader.getCells().add(cell);
}
// 8. now we will iterate on the clipboard contents, adding row header for each new line
final RowIterator rowIterator = parser.parse();
final Iterator<Object> rowIter = manager.getRowElementsList().iterator();
while (rowIterator.hasNext() && rowIter.hasNext()) {
// 8.1 create a new row
BasicRow row = BuiltInTypesFactory.eINSTANCE.createBasicRow();
basicTable.getRows().add(row);
// 8.2 create the row header cell
final Object element = rowIter.next();
final TextCell cell1 = DocumentStructureFactory.eINSTANCE.createExtendedTextCell();
cell1.setLocation(CellLocation.ROW_HEADER);
String cellContent = labelProvider.getRowHeaderLabel(element);
if (isTreeTable) {
int depth = 0;
if (element instanceof ITreeItemAxis) {// should always be true
Object representedElement = AxisUtils.getRepresentedElement(element);
if (representedElement instanceof TreeFillingConfiguration) {
depth = ((TreeFillingConfiguration) representedElement).getDepth() * 2;
} else {
final ITreeItemAxis parent = ((ITreeItemAxis) element).getParent();
if (null != parent) {
representedElement = AxisUtils.getRepresentedElement(parent);
// should always be true
if (representedElement instanceof TreeFillingConfiguration) {
depth = ((TreeFillingConfiguration) representedElement).getDepth() * 2 + 1;
}
}
}
}
// we add the marker of the tree level
final StringBuilder rowHeaderCellContent = new StringBuilder();
for (int i = 0; i < depth; i++) {
rowHeaderCellContent.append(TREE_LEVEL_MARKER);
}
cellContent = rowHeaderCellContent.append(cellContent).toString();
}
cell1.setText(cellContent);
row.getCells().add(cell1);
// 8.2 add the body cells for the row
final CellIterator cellIterator = rowIterator.next();
if (cellIterator != null) {
if (!cellIterator.hasNext()) {
// in case of tree table with only one column, the empty cells are not in the cellIterator...
// see bug 549183
final TextCell cell = DocumentStructureFactory.eINSTANCE.createExtendedTextCell();
row.getCells().add(cell);
} else {
while (cellIterator.hasNext()) {
final TextCell cell = DocumentStructureFactory.eINSTANCE.createExtendedTextCell();
cell.setText(cellIterator.next());
row.getCells().add(cell);
}
}
}
}
return basicTable;
}
/**
* @param manager
* the instance of the NatTable manager manaing the current Papyrus {@link Table}
* @param papyrusTableView
* the {@link PapyrusTableView}
* @param table
* a {@link Table} found for the {@link PapyrusTableView}
* @return
* the table as Image
*/
private Image mapTableAsImage(final INattableModelManager manager, final PapyrusTableView papyrusTableView, final Table table) {
// 1. calculate the image path output
final DocumentTemplate t = DocumentStructureTemplateUtils.getDocumentTemplate(papyrusTableView);
final IDocumentStructureGeneratorConfiguration conf = t.getDocumentStructureGeneratorConfiguration();
// we remove the whitespace in the name of the diagram
final StringBuilder imageNameBuilder = new StringBuilder(table.getName().replaceAll("\\s+", "")); //$NON-NLS-1$ //$NON-NLS-2$
if (table.eResource() instanceof XMIResource) {
final String XMI_ID = ((XMIResource) table.eResource()).getID(table);
if (null != XMI_ID && false == XMI_ID.isEmpty()) {
imageNameBuilder.append("_"); //$NON-NLS-1$
imageNameBuilder.append(XMI_ID);
}
}
String imagePath = GeneratorConfigurationOperations.getImageFileLocalPath(conf, imageNameBuilder.toString(), ImageFormat.PNG.getImageExtension());
imagePath = imagePath.replaceAll("file:/", ""); //$NON-NLS-1$ //$NON-NLS-2$
// 2. configure the image generation, changing some values in the ConfigRegistry of the NatTable instance
generateAllIntermediateFolders(imagePath);
final NatTable natTable = manager.getAdapter(NatTable.class);
if (null == natTable) {
Activator.log.warn("The NatTable widget can't be found"); //$NON-NLS-1$
}
final Object[] previousValues = configureTableForExport(natTable, false, false, imagePath, org.eclipse.papyrus.infra.nattable.export.image.ImageFormat.PNG);
// 3. do the export itself
manager.exportToImage();
// 4. wait for the end of imag creation process
flushEventLoop();
// 5. we create and return the image
final Image image = DocumentStructureFactory.eINSTANCE.createImage();
image.setCaption(table.getName());
image.setImagePath(imagePath);
// 6. we reset the previous values in the NatTable instance
configureTableForExport(natTable, (boolean) previousValues[0], (boolean) previousValues[1], (String) previousValues[2], (ImageFormat) previousValues[3]);
return image;
}
/**
*
* This method allows to configure the export in a NatTable instance. This method returns an array with the previous values.
* The order of the element in the array are the same than the order of the parameters of this method.
*
* @param natTable
* a {@link NatTable} instance
* @param useExportDialog
* value defining a dialog must be displayed to export the table (<code>true</code>, there is a dialog, <code>false</code> there is no dialog)
* @param openExportResult
* value defining if the exported image must be open after the export (<code>true</code>) or not (<code>false</code>)
* @param outputFile
* the name of the output file
* @param imageFormat
* the format of the image to create
* @return
* the list of the previous value for these parameters in the NatTable instance
*/
private Object[] configureTableForExport(final NatTable natTable, final boolean useExportDialog, boolean openExportResult, final String outputFile, final ImageFormat imageFormat) {
final IConfigRegistry configRegistry = natTable.getConfigRegistry();
boolean previousUseExportDialog = configRegistry.getConfigAttribute(PapyrusExportConfigAttributes.EXPORT_IMAGE_USE_PAPYRUS_EXPORT_TABLE_DIALOG, DisplayMode.NORMAL);
boolean previousOpenResult = configRegistry.getConfigAttribute(PapyrusExportConfigAttributes.OPEN_RESULT_AFTER_EXPORT, DisplayMode.NORMAL);
final String previousOutputfile = configRegistry.getConfigAttribute(PapyrusExportConfigAttributes.EXPORT_IMAGE_FILENAME, DisplayMode.NORMAL);
final ImageFormat previouImageFormat = configRegistry.getConfigAttribute(PapyrusExportConfigAttributes.EXPORT_IMAGE_FORMAT, DisplayMode.NORMAL);
configRegistry.registerConfigAttribute(PapyrusExportConfigAttributes.EXPORT_IMAGE_USE_PAPYRUS_EXPORT_TABLE_DIALOG, Boolean.valueOf(useExportDialog));
configRegistry.registerConfigAttribute(PapyrusExportConfigAttributes.OPEN_RESULT_AFTER_EXPORT, Boolean.valueOf(openExportResult));
configRegistry.registerConfigAttribute(PapyrusExportConfigAttributes.EXPORT_IMAGE_FILENAME, outputFile);
configRegistry.registerConfigAttribute(PapyrusExportConfigAttributes.EXPORT_IMAGE_FORMAT, imageFormat);
return new Object[] { previousUseExportDialog, previousOpenResult, previousOutputfile, previouImageFormat };
}
/**
*
* @param table
* a {@link Table}
* @return
* the page manager of the current Papyrus editor, or <code>null</code> if not found
*/
private IPageManager getIPageManager(final Table table) {
try {
return ServiceUtilsForEObject.getInstance().getIPageManager(table);
} catch (final ServiceException e) {
Activator.log.error("The page manager can't be found", e); //$NON-NLS-1$
}
return null;
}
/**
*
* @param table
* a {@link Table}
* @return
* the {@link INattableModelManager} associated to this table or <code>null</code> if not found
*/
private final INattableModelManager getINattableModelManager(final Table table) {
IMultiDiagramEditor multiDiagramEditor = getIMultiDiagramEditor(table);
final IEditorPart editor = multiDiagramEditor.getActiveEditor();
if (null == editor) {
Activator.log.warn(NLS.bind("The editor for the Table {0} has not been open.", table.getName())); //$NON-NLS-1$
}
final INattableModelManager manager = editor.getAdapter(INattableModelManager.class);
if (null == manager || manager.getTable() != table) {
Activator.log.warn(NLS.bind("The editor for the Table {0} can't be open.", table.getName())); //$NON-NLS-1$
}
return manager;
}
/**
*
* @param table
* a {@link Table}
* @return
* the {@link IMultiDiagramEditor} associated to this table, or <code>null</code> if not found
*/
private final IMultiDiagramEditor getIMultiDiagramEditor(final Table table) {
ServicesRegistry reg = null;
try {
reg = ServiceUtilsForEObject.getInstance().getServiceRegistry(table);
} catch (final ServiceException e) {
Activator.log.error("ServiceRegistry not found.", e); //$NON-NLS-1$
}
if (null == reg) {
return null;
}
IMultiDiagramEditor multiDiagramEditor = null;
try {
multiDiagramEditor = reg.getService(IMultiDiagramEditor.class);
} catch (ServiceException e) {
Activator.log.error("IMultiDiagramEditor not found.", e); //$NON-NLS-1$
}
return multiDiagramEditor;
}
/**
* This method creates all required intermediates folders
*
* @param pathRoot
* the full path of the image to create
*/
private final void generateAllIntermediateFolders(final String pathRoot) {
final Path imagePath = new Path(pathRoot.toString());
// we check all folders tree already exists, and we create them if not
if (imagePath.segmentCount() > 1) {
final IPath folderPath = imagePath.removeLastSegments(1);
final File folder = folderPath.toFile();
if (false == folder.exists()) {
folder.mkdirs();
}
}
}
/**
* Processes all events waiting in the Display's event loop and then returns.
*/
private static final void flushEventLoop() {
final Display display = Display.getDefault();
display.syncExec(new Runnable() {
@Override
public void run() {
try {
while (display.readAndDispatch()) {
// nothing
}
} catch (Exception ex) {
Activator.log.error("Exceptions occured during the opening of the Table", ex); //$NON-NLS-1$
}
}
});
}
}