blob: b541c7bb92e453036b451130fde8753941945759 [file] [log] [blame]
/*****************************************************************************
* Copyright (c) 2019-2021 CEA LIST.
*
* 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
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Yupanqui Munoz (CEA LIST) yupanqui.munozjulho@cea.fr - Initial API and implementation
* Vincent Lorenzo (CEA LIST) vincent.lorenzo@cea.fr - Initial API and implementation
* Vincent Lorenzo (CEA LIST) vincent.lorenzo@cea.fr - bug 559826
* Vincent Lorenzo (CEA LIST) vincent.lorenzo@cea.fr - bug 569817
* Pauline DEVILLE (CEA LIST) pauline.deville@cea.fr - Bug 570743
*
*****************************************************************************/
package org.eclipse.papyrus.model2doc.odt.internal.editor;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.papyrus.model2doc.core.generatorconfiguration.IDocumentGeneratorConfiguration;
import org.eclipse.papyrus.model2doc.odt.Activator;
import org.eclipse.papyrus.model2doc.odt.internal.message.Messages;
import org.eclipse.papyrus.model2doc.odt.internal.util.ExtensionConstants;
import org.eclipse.papyrus.model2doc.odt.internal.util.LibreOfficeInstallationPathUtil;
import org.eclipse.papyrus.model2doc.odt.internal.util.ODTFileIOUtil;
import org.eclipse.papyrus.model2doc.odt.service.ODTEditorService;
import org.eclipse.papyrus.model2doc.odt.service.ODTFileIOService;
import org.eclipse.papyrus.model2doc.odt.service.ODTFileIOServiceImpl;
import org.eclipse.swt.widgets.Display;
import com.sun.star.awt.XWindow;
import com.sun.star.beans.PropertyValue;
import com.sun.star.container.ElementExistException;
import com.sun.star.container.XNameContainer;
import com.sun.star.frame.XComponentLoader;
import com.sun.star.frame.XController;
import com.sun.star.frame.XDesktop;
import com.sun.star.frame.XDispatchHelper;
import com.sun.star.frame.XModel;
import com.sun.star.frame.XStorable;
import com.sun.star.io.IOException;
import com.sun.star.lang.IllegalArgumentException;
import com.sun.star.lang.WrappedTargetException;
import com.sun.star.lang.XComponent;
import com.sun.star.lang.XMultiComponentFactory;
import com.sun.star.lang.XMultiServiceFactory;
import com.sun.star.system.SimpleMailClientFlags;
import com.sun.star.system.XSimpleMailClient;
import com.sun.star.system.XSimpleMailClientSupplier;
import com.sun.star.system.XSimpleMailMessage;
import com.sun.star.text.XPageCursor;
import com.sun.star.text.XTextDocument;
import com.sun.star.text.XTextViewCursorSupplier;
import com.sun.star.uno.UnoRuntime;
import com.sun.star.uno.XComponentContext;
import com.sun.star.util.CloseVetoException;
import com.sun.star.util.XCloseable;
import ooo.connector.BootstrapSocketConnector;
/**
* ODT Editor is dedicated to management of odt files and interaction with LibreOffice Writer.
*
*/
public class ODTEditor {
public static final String LIBREOFFICE_FILE_PREFIX = "file:///"; //$NON-NLS-1$
private static final String STANDART_FILE_PREFIX = "file:/";//$NON-NLS-1$
public static final String FILE_SEPARATOR = "/"; //$NON-NLS-1$
private XComponentLoader officeLoader = null;
private XTextDocument xTextDocument = null;
private XDesktop xDesktop = null;
private IDocumentGeneratorConfiguration configuration;
private XMultiComponentFactory xMultiComponentFactory = null;
private XMultiServiceFactory xMultiServiceFactory = null;
private XComponentContext xComponentContext = null;
/**
* The dispatch helper (not yet used)
*/
private XDispatchHelper dispatchHelper = null;
private ODTFileIOService fileIOService = null;
public ODTEditor() {
fileIOService = new ODTFileIOServiceImpl();
// Take LibreOffice Loader
loadOffice();
}
/**
* Constructor.
*
* @param projectFolder
*
* @param generatorConfig
*/
@Deprecated
public ODTEditor(String projectFolder) {
this();
}
/**
* Constructor.
*
* @param generatorConfig
*/
public ODTEditor(final IDocumentGeneratorConfiguration generatorConfig) {
this.configuration = generatorConfig;
// Take LibreOffice Loader
loadOffice();
if (officeLoader != null) {
// Create text document
fileIOService = new ODTFileIOServiceImpl();
final String templateURL = generatorConfig.createTemplateFileInputAccessor().createInputFileURL().toString();
createTextDocument(templateURL);
xMultiServiceFactory = UnoRuntime.queryInterface(XMultiServiceFactory.class, xTextDocument);
try {
Object dispatchHelperObject = this.xMultiComponentFactory.createInstanceWithContext("com.sun.star.frame.DispatchHelper", xComponentContext); //$NON-NLS-1$
this.dispatchHelper = UnoRuntime.queryInterface(XDispatchHelper.class, dispatchHelperObject);
} catch (com.sun.star.uno.Exception e) {
Activator.log.error(e);
}
}
}
/**
* Get text document.
*
* @return
*/
public XTextDocument getXTextDocument() {
return xTextDocument;
}
/**
* Get XMultiComponentFactory.
*
* @return
*/
public XMultiComponentFactory getXMultiComponentFactory() {
return xMultiComponentFactory;
}
/**
* Get XMultiServiceFactory.
*
* @return
*/
public XMultiServiceFactory getXMultiServiceFactory() {
return xMultiServiceFactory;
}
/**
* Get XComponentContext.
*
* @return
*/
public XComponentContext getXComponentContext() {
return xComponentContext;
}
/**
* This method allows to insert the image in the final LibreOffice container (a bitmap table)
* and return this container (which can be used to get the image and insert it in the generated document)
*
* @param imagePath
* the path of an image to insert in the document
* @return
* the BitmapTable containing the image
*/
public XNameContainer getBitmapTable(final String imagePath) {
XNameContainer bitmapTable = null;
try {
// the system always returns the same instance
bitmapTable = UnoRuntime.queryInterface(XNameContainer.class, xMultiServiceFactory.createInstance("com.sun.star.drawing.BitmapTable")); //$NON-NLS-1$
} catch (com.sun.star.uno.Exception e) {
Activator.log.error(e);
}
// we need to check if the image is already inserted in the table, because if we don't check that,
// the image will be inserted only one time in the final document, even if we want it several time (image in footer for example)
//
if (bitmapTable != null && !bitmapTable.hasByName(imagePath)) {
try {
bitmapTable.insertByName(imagePath, imagePath);
} catch (IllegalArgumentException | ElementExistException | WrappedTargetException e) {
Activator.log.error(e);
}
}
return bitmapTable;
}
/**
* Get Page Cursor.
*
* @return
*/
public XPageCursor getPageCursor() {
XModel model = UnoRuntime.queryInterface(XModel.class, xTextDocument);
XController xController = model.getCurrentController();
XTextViewCursorSupplier supplier = UnoRuntime.queryInterface(XTextViewCursorSupplier.class, xController);
return UnoRuntime.queryInterface(XPageCursor.class, supplier.getViewCursor());
}
/**
* Open document.
*
* @param documentURL
* the full URL of document (LibreOffice format, ex.: file:///C:/folder/projectName/document.odt)
*/
public void openDocument(String documentURL) {
XComponent xComponent = getDocument(documentURL);
if (xComponent != null) {
XModel model = UnoRuntime.queryInterface(XModel.class, xComponent);
XWindow xWindow = model.getCurrentController().getFrame().getContainerWindow();
// Open or not LibreOffice Writer
xWindow.setVisible(true);
xWindow.setFocus();
}
}
/**
* Save document.
*
* @param fileName
*/
public String save(String fileName) {
// Save document
return saveAs(fileName, ODTEditorService.DOCUMENT_KIND); // $NON-NLS-1$
}
/**
* Save document as "extension".
*
* @param fileName
* @param extension
*
* @return
* the full URL of exported document (LibreOffice format, ex.: file:///C:/folder/projectName/document.pdf)
*/
public String saveAs(String fileName, String extension) {
// Save document
String exportedDocumentURL = saveDocument(fileName, extension); // $NON-NLS-1$
// Close document
// TODO : the save and save as must be callable without closing the document
closeDocument();
// Close LibreOffice
closeLibreOffice();
return exportedDocumentURL;
}
/**
* Export document.
*
* @param fileName
* @param documentURL
* the full URL of document (LibreOffice format, ex.: file:///C:/folder/projectName/document.odt)
* @param extension
*
* @return
* the full URL of exported document (LibreOffice format, ex.: file:///C:/folder/projectName/document.pdf)
*/
public String exportDocument(String fileName, String documentURL, String extension) {
XComponent xComponent = getDocument(documentURL);
if (xComponent != null) {
xTextDocument = UnoRuntime.queryInterface(XTextDocument.class, xComponent);
return saveAs(fileName, extension);
}
return null;
}
/**
*
* @param inputFileURI
* the input file URI
* @param outputFileURI
* the output file URI
* @param storeProperties
* the configuration of the save represented by this array of {@link PropertyValue}
* @return
* the output file URI, or {@link NullPointerException} when it fails
* @throws Exception
*/
public String saveAs(String inputFileURI, String outputFileURI, final PropertyValue[] storeProperties) throws Exception {
inputFileURI = convertToLibreOfficeFileURI(inputFileURI);
if (!inputFileURI.startsWith(ODTFileIOUtil.ODT_FILE_PREFIX)) {
throw new Exception("The fileURI is not conform to the expected one"); //$NON-NLS-1$
}
outputFileURI = convertToLibreOfficeFileURI(outputFileURI);
if (!outputFileURI.startsWith(ODTFileIOUtil.ODT_FILE_PREFIX)) {
throw new Exception("The fileURI is not conform to the expected one"); //$NON-NLS-1$
}
XComponent xComponent = getDocument(inputFileURI);
if (xComponent != null) {
this.xTextDocument = UnoRuntime.queryInterface(XTextDocument.class, xComponent);
XStorable store = UnoRuntime.queryInterface(XStorable.class, xTextDocument);
try {
store.storeToURL(outputFileURI, storeProperties);
return outputFileURI;
} catch (com.sun.star.io.IOException e) {
Activator.log.error(outputFileURI + " have not been saved: ", e); //$NON-NLS-1$
}
}
return null;
}
/**
* Get format by extension.
*
* @param extention
* @return
*/
public String getFormat(String extension) {
switch (extension) {
case ODTEditorService.DOCUMENT_KIND: // $NON-NLS-1$
return "writer8"; //$NON-NLS-1$
case ExtensionConstants.PDF: // $NON-NLS-1$
return "writer_pdf_Export"; //$NON-NLS-1$
default:
return "writer8"; //$NON-NLS-1$
}
}
/**
* Send mail with attached document.
*
* @param subject
* @param documentToAttach
* the full URL of document (LibreOffice format, ex.: file:///C:/folder/projectName/document.odt)
*/
public void sendMail(String subject, String documentToAttach) {
System.out.println("Subject: " + subject + " DocumentURL: " + documentToAttach); //$NON-NLS-1$ //$NON-NLS-2$
XSimpleMailClientSupplier xSimpleMailClientSupplier = createSimpleMailClientSupplier();
if (xSimpleMailClientSupplier != null) {
XSimpleMailClient xSimpleMailClient = xSimpleMailClientSupplier.querySimpleMailClient();
XSimpleMailMessage mailMessage = xSimpleMailClient.createSimpleMailMessage();
mailMessage.setSubject(subject);
if (documentToAttach != null) {
String[] attachs = new String[1];
attachs[0] = documentToAttach;
mailMessage.setAttachement(attachs);
}
try {
xSimpleMailClient.sendSimpleMailMessage(mailMessage, SimpleMailClientFlags.DEFAULTS);
} catch (IllegalArgumentException | com.sun.star.uno.Exception e) {
Activator.log.error(e);
}
} else {
MessageDialog.openWarning(Display.getDefault().getActiveShell(), Messages.ODTEditor_SendMail, Messages.ODTEditor_MailClientWasNotFound);
}
}
/**
* Close document.
*
* @param documentURL
* the full URL of document (LibreOffice format, ex.: file:///C:/folder/projectName/document.odt)
*/
public void closeDocument(String documentURL) {
XComponent xComponent = getDocument(documentURL);
if (xComponent != null) {
xTextDocument = UnoRuntime.queryInterface(XTextDocument.class, xComponent);
closeDocument();
closeLibreOffice();
}
}
/**
* Get document object.
*
* @param documentURL
* the full URL of document (LibreOffice format, ex.: file:///C:/folder/projectName/document.odt)
* @return
*/
public XComponent getDocument(String documentURL) {
XComponent xComponent = null;
if (officeLoader != null) {
PropertyValue[] props = new PropertyValue[2];
props[0] = new PropertyValue();
props[0].Name = "Hidden"; //$NON-NLS-1$
props[0].Value = true;
try {
xComponent = officeLoader.loadComponentFromURL(documentURL, "_blank", 0, props); //$NON-NLS-1$
} catch (IllegalArgumentException | IOException e) {
Activator.log.error("Unable to get document object from" + documentURL + " : ", e); //$NON-NLS-1$ //$NON-NLS-2$
}
}
return xComponent;
}
/**
* This method does the UNO bootstrapping, gets the remote service manager and
* the loader object.
*/
private void loadOffice() {
final String oooExeFolder = LibreOfficeInstallationPathUtil.getLibreOfficeInstallationPath();
if (null == oooExeFolder || oooExeFolder.isEmpty()) {
Activator.log.warn("The path for the LibreOffice installation is null or empty. We stop the documentation generation."); //$NON-NLS-1$
}
try {
// Get the remote LibreOffice component context
xComponentContext = BootstrapSocketConnector.bootstrap(oooExeFolder);
// Get the remote LibreOffice service manager
xMultiComponentFactory = xComponentContext.getServiceManager();
if (xMultiComponentFactory != null) {
Object oDesktop = xMultiComponentFactory.createInstanceWithContext("com.sun.star.frame.Desktop", xComponentContext); //$NON-NLS-1$
xDesktop = UnoRuntime.queryInterface(XDesktop.class, oDesktop);
officeLoader = UnoRuntime.queryInterface(XComponentLoader.class, xDesktop);
}
} catch (Exception e) {
Activator.log.error(e);
} finally {
// If it is not possible load LibreOffice.
if (officeLoader == null) {
MessageDialog.openError(Display.getCurrent().getActiveShell(), Messages.ODTEditor_LibreOfficeDocumentGenerator_ErrorTitle,
Messages.ODTEditor_LibreOfficeDocumentGenerator_ErrorMessage);
// in this case we erase the libreOffice path
LibreOfficeInstallationPathUtil.eraseLibreOfficeInstallationPath();
}
}
}
/**
* Create text document.
*
* @param templateURL
* @return
*/
private XTextDocument createTextDocument(String templateURL) {
try {
XComponent xComponent = null;
if (templateURL != null && !templateURL.isEmpty()) {
xComponent = createNewDocumentFromTemplate(convertToLibreOfficeFileURI(templateURL));
} else {
xComponent = createNewDocument();
}
XModel model = UnoRuntime.queryInterface(XModel.class, xComponent);
XWindow xWindow = model.getCurrentController().getFrame().getContainerWindow();
// Open or not LibreOffice Writer
xWindow.setVisible(false);
xWindow.setFocus();
xTextDocument = UnoRuntime.queryInterface(XTextDocument.class, xComponent);
} catch (Exception e) {
Activator.log.error(e);
}
return xTextDocument;
}
/**
* Create new document without template.
*
* @return
*/
private XComponent createNewDocument() {
String sURL = "private:factory/swriter"; //$NON-NLS-1$
XComponent xComponent = null;
PropertyValue[] xEmptyArgs = new PropertyValue[0];
try {
xComponent = officeLoader.loadComponentFromURL(sURL, "_blank", 0, xEmptyArgs); //$NON-NLS-1$
} catch (Exception e) {
Activator.log.error(e);
}
return xComponent;
}
/**
* Create new document from template.
*
* @param templateURL
* should be in the format : file:/OS_path or file:///OS_path
* @return
*/
private XComponent createNewDocumentFromTemplate(String templateURL) {
XComponent xComponent = null;
try {
officeLoader = UnoRuntime.queryInterface(XComponentLoader.class, officeLoader);
PropertyValue[] props = new PropertyValue[2];
props[0] = new PropertyValue();
props[0].Name = "Hidden"; //$NON-NLS-1$
props[0].Value = true;
props[1] = new PropertyValue();
props[1].Name = "AsTemplate"; //$NON-NLS-1$
props[1].Value = true;
xComponent = officeLoader.loadComponentFromURL(convertToLibreOfficeFileURI(templateURL), "_blank", 0, props); //$NON-NLS-1$
} catch (Exception e) {
Activator.log.error(e);
}
if (xComponent == null) {
xComponent = createNewDocument();
}
return xComponent;
}
/**
* Create Simple Mail Client Supplier.
*
* @return
*/
private XSimpleMailClientSupplier createSimpleMailClientSupplier() {
XSimpleMailClientSupplier xSimpleMailClientSupplier = null;
Object oSimpleMailClientSupplier = null;
try {
oSimpleMailClientSupplier = xMultiComponentFactory.createInstanceWithContext("com.sun.star.system.SimpleSystemMail", xComponentContext); //$NON-NLS-1$
// If user uses Windows, the result is not null
if (oSimpleMailClientSupplier != null) {
xSimpleMailClientSupplier = UnoRuntime.queryInterface(XSimpleMailClientSupplier.class, oSimpleMailClientSupplier);
} else {
oSimpleMailClientSupplier = xMultiComponentFactory.createInstanceWithContext("com.sun.star.system.SimpleCommandMail", xComponentContext); //$NON-NLS-1$
// If user uses Linux or Mac, the result is not null
if (oSimpleMailClientSupplier != null) {
xSimpleMailClientSupplier = UnoRuntime.queryInterface(XSimpleMailClientSupplier.class, oSimpleMailClientSupplier);
}
}
} catch (com.sun.star.uno.Exception e) {
Activator.log.error(e);
}
return xSimpleMailClientSupplier;
}
/**
* Save document.
*
* @param fileName
* @param extension
*
* @return
* the full URL of exported document (LibreOffice format, ex.: file:///C:/folder/projectName/document.pdf)
* TODO : improve me, to return a java file or URI?!
*/
private String saveDocument(String fileName, String extension) {
XStorable store = UnoRuntime.queryInterface(XStorable.class, xTextDocument);
final String saveFileURL = convertToLibreOfficeFileURI(configuration.createDocumentOutputAccessor().createOutputFileURL(configuration.getDocumentName(), extension).toString());
if (saveFileURL == null) {// not possible with the current implementation
return null;
}
PropertyValue[] storeProps = new PropertyValue[2];
storeProps[0] = new PropertyValue();
storeProps[0].Name = StorePropertiesConstants.OVERWRITE;
storeProps[0].Value = true;
storeProps[1] = new PropertyValue();
storeProps[1].Name = StorePropertiesConstants.FILTER_NAME;
storeProps[1].Value = getFormat(extension);
try {
store.storeToURL(saveFileURL, storeProps);
} catch (com.sun.star.io.IOException e) {
Activator.log.error(saveFileURL + " have not been saved: ", e); //$NON-NLS-1$
}
return saveFileURL;
}
/**
* Close document.
*
*/
private void closeDocument() {
XCloseable closeable = UnoRuntime.queryInterface(XCloseable.class, xTextDocument);
if (closeable == null) {
return;
}
try {
closeable.close(false);
} catch (com.sun.star.lang.DisposedException e) {
Activator.log.warn("Document close failed since LibreOffice link disposed."); //$NON-NLS-1$
} catch (CloseVetoException e) {
Activator.log.error("Close was vetoed: ", e); //$NON-NLS-1$
}
}
/**
* Close LibreOffice.
*
*/
private void closeLibreOffice() {
if (xDesktop == null) {
return;
}
try {
boolean isOfficeTerminated = xDesktop.terminate();
if (false == isOfficeTerminated) {
Activator.log.warn("LibreOffice failed to terminate."); //$NON-NLS-1$
}
} catch (com.sun.star.lang.DisposedException e) {
Activator.log.warn("LibreOffice link disposed."); //$NON-NLS-1$
} catch (java.lang.Exception e) {
Activator.log.error("Termination exception: ", e); //$NON-NLS-1$
}
}
/**
*
* @param fileURL
* a file url, starting with file:/ or file:///
* @return
* a file url starting with file:///
*/
public static final String convertToLibreOfficeFileURI(final String fileURI) {
String newFileURI = fileURI;
if (newFileURI.startsWith(STANDART_FILE_PREFIX) && false == newFileURI.startsWith(LIBREOFFICE_FILE_PREFIX)) {
newFileURI = newFileURI.replaceFirst(STANDART_FILE_PREFIX, LIBREOFFICE_FILE_PREFIX);
} else if (newFileURI.startsWith(FILE_SEPARATOR)) {
newFileURI = newFileURI.replaceFirst(FILE_SEPARATOR, LIBREOFFICE_FILE_PREFIX);
}
return newFileURI;
}
public static final String convertToGenericFileURI(final String fileURI) {
String newFileURI = fileURI;
if (newFileURI.startsWith(LIBREOFFICE_FILE_PREFIX)) {
newFileURI = newFileURI.replaceFirst(LIBREOFFICE_FILE_PREFIX, STANDART_FILE_PREFIX);
}
return newFileURI;
}
/**
*
* @return
* the dispatch helper. This element can be called during the edition of the file to force a refresh
*/
public final XDispatchHelper getDispatchHelper() {
return this.dispatchHelper;
}
}