org.obeonetwork.m2doc.generator.UserContentManager.java Source code

Java tutorial

Introduction

Here is the source code for org.obeonetwork.m2doc.generator.UserContentManager.java

Source

/*******************************************************************************
 *  Copyright (c) 2016 Obeo. 
 *  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:
 *       Obeo - initial API and implementation
 *  
 *******************************************************************************/
package org.obeonetwork.m2doc.generator;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.xmlbeans.XmlException;
import org.eclipse.acceleo.query.runtime.IQueryEnvironment;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.URIConverter;
import org.obeonetwork.m2doc.POIServices;
import org.obeonetwork.m2doc.parser.ValidationMessageLevel;
import org.obeonetwork.m2doc.template.DocumentTemplate;
import org.obeonetwork.m2doc.template.UserContent;
import org.obeonetwork.m2doc.util.M2DocUtils;

/**
 * This class manage UserDoc Destination tag UserContent.
 * It launch destination parsing doc and keep map of UserDoc id / UserContent EObject element.
 * Be careful, parsing must be done on a copy of destination document because old destination document is remove by generation.
 * 
 * @author ohaegi
 */
public class UserContentManager {

    /**
     * The error copy message.
     */
    public static final String USERDOC_COPY_ERROR = "userdoc copy error : ";

    /**
     * Temporary Generated destination file name suffix.
     */
    public static final String TEMP_DEST_SUFFIX = "tmpDocDest";

    /**
     * Buffer size.
     */
    private static final int BUFFER_SIZE = 1024 * 8;

    /**
     * The {@link DateFormat} used to log lost {@link UserContent}.
     */
    private final DateFormat format = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");

    /**
     * Generated file.
     */
    private final File generatedFileCopy;

    /**
     * Map for id to the {@link List} of .
     */
    private final Map<String, List<UserContent>> mapIdUserContent = new HashMap<String, List<UserContent>>();

    /**
     * The input {@link DocumentTemplate}.
     */
    private final DocumentTemplate documentTemplate;

    /**
     * The destination {@link URI}.
     */
    private final URI destination;

    /**
     * The URI converter to use.
     */
    private URIConverter uriConverter;

    /**
     * Constructor.
     * 
     * @param uriConverter
     *            the {@link URIConverter uri converter} to use.
     * @param documentTemplate
     *            the input {@link DocumentTemplate}
     * @param destination
     *            the destination {@link URI}
     * @throws IOException
     *             IOException if the destination can't be copied to a temporary file
     */
    public UserContentManager(URIConverter uriConverter, DocumentTemplate documentTemplate, URI destination)
            throws IOException {
        this.uriConverter = uriConverter;
        this.documentTemplate = documentTemplate;
        this.destination = destination;
        if (uriConverter.exists(destination, Collections.EMPTY_MAP)) {
            // Copy file
            generatedFileCopy = tempCopyFile(destination);
            // Launch parsing
            launchParsing();
        } else {
            generatedFileCopy = null;
        }
    }

    /**
     * Launch Parsing.
     * 
     * @throws IOException
     *             IOException
     */
    private void launchParsing() throws IOException {
        IQueryEnvironment queryEnvironment = org.eclipse.acceleo.query.runtime.Query
                .newEnvironmentWithDefaultServices(null);

        try (DocumentTemplate userDocDocument = M2DocUtils.parseUserContent(uriConverter,
                URI.createURI(generatedFileCopy.toURI().toString()), queryEnvironment);) {
            final TreeIterator<EObject> iter = userDocDocument.eAllContents();
            while (iter.hasNext()) {
                EObject eObject = iter.next();
                if (eObject instanceof UserContent) {
                    UserContent userContent = (UserContent) eObject;
                    if (userContent.getId() != null) {
                        final String id = userContent.getId();
                        List<UserContent> userContents = mapIdUserContent.get(id);
                        if (userContents == null) {
                            userContents = new ArrayList<UserContent>();
                            mapIdUserContent.put(id, userContents);
                        }
                        userContents.add(userContent);
                    }
                }
            }
            // CHECKSTYLE:OFF
        } catch (Exception e) {
            // CHECKSTYLE:ON
            // In this case, we do nothing.
            // The old output doc is not a docx document and it will be overwrite at current generation.
            // And we have nothing to extract to a no docx document.
        }
    }

    /**
     * Consumes the next {@link UserContent} with the given {@link UserContent#getId() ID}.
     * 
     * @param id
     *            the {@link UserContent#getId() ID}
     * @return the consumed {@link UserContent} if any, <code>null</code> otherwise
     */
    public UserContent consumeUserContent(String id) {
        final UserContent res;

        List<UserContent> userContents = mapIdUserContent.get(id);
        if (userContents != null && !userContents.isEmpty()) {
            res = userContents.remove(0);
            if (userContents.isEmpty()) {
                mapIdUserContent.remove(id);
            }
        } else {
            res = null;
        }

        return res;
    }

    /**
     * Copy file in temporary file in same folder than original file.
     * 
     * @param source
     *            source file
     * @return source copy File
     * @throws IOException
     *             IOException
     */
    private File tempCopyFile(URI source) throws IOException {
        // Create temporary file
        File dest = File.createTempFile(source.lastSegment(), TEMP_DEST_SUFFIX);
        // Copy generated file in temp file
        copyFile(source, dest);
        return dest;
    }

    /**
     * Dispose.
     * 
     * @throws IOException
     *             IOException
     */
    public void dispose() throws IOException {
        // Delete Temp Generated File.
        if (generatedFileCopy != null && generatedFileCopy.exists() && generatedFileCopy.isFile()) {
            generatedFileCopy.delete();
        }
    }

    /**
     * Copy File.
     * 
     * @param source
     *            source file
     * @param dest
     *            destination file
     * @throws IOException
     *             IOException
     */
    private void copyFile(URI source, File dest) throws IOException {
        try (InputStream is = uriConverter.createInputStream(source); //
                OutputStream os = new FileOutputStream(dest);) {
            byte[] buffer = new byte[BUFFER_SIZE];
            int length;
            while ((length = is.read(buffer)) > 0) {
                os.write(buffer, 0, length);
            }
        }
    }

    /**
     * Gets the {@link List} of duplicated {@link UserContent#getId() user content ID}.
     * 
     * @return the {@link List} of duplicated {@link UserContent#getId() user content ID}
     */
    public List<String> getDuplicatedUserContentIDs() {
        List<String> res = new ArrayList<String>();

        for (Entry<String, List<UserContent>> entry : mapIdUserContent.entrySet()) {
            if (entry.getValue().size() > 1) {
                res.add(entry.getKey());
            }
        }

        return res;
    }

    /**
     * Generates lost files if needed and update given {@link GenerationResult}.
     * 
     * @param result
     *            the {@link GenerationResult}
     * @throws IOException
     *             if the lost {@link UserContent} can't be written
     * @throws InvalidFormatException
     *             if the input {@link DocumentTemplate} can't be read
     */
    public void generateLostFiles(GenerationResult result) throws IOException, InvalidFormatException {
        for (Entry<String, List<UserContent>> entry : mapIdUserContent.entrySet()) {
            final URI lostUserContentURI = getLostUserContentURI(destination, entry.getKey());
            result.getLostUserContents().put(entry.getKey(), lostUserContentURI);
            final boolean isNewUserContentLoss;
            final URI inputURI;
            if (uriConverter.exists(lostUserContentURI, Collections.EMPTY_MAP)) {
                inputURI = lostUserContentURI;
                isNewUserContentLoss = false;
            } else {
                inputURI = documentTemplate.eResource().getURI();
                isNewUserContentLoss = true;
            }

            try (InputStream is = uriConverter.createInputStream(inputURI);
                    OPCPackage oPackage = OPCPackage.open(is);
                    XWPFDocument destinationDocument = new XWPFDocument(oPackage);) {
                if (isNewUserContentLoss) {
                    // clear the document
                    int size = destinationDocument.getBodyElements().size();
                    for (int i = 0; i < size; i++) {
                        destinationDocument.removeBodyElement(0);
                    }
                }
                XWPFParagraph currentGeneratedParagraph = destinationDocument.createParagraph();
                M2DocUtils.appendMessageRun(currentGeneratedParagraph, ValidationMessageLevel.WARNING,
                        format.format(new Date()) + " - Lost user content " + entry.getKey());
                result.updateLevel(ValidationMessageLevel.WARNING);
                currentGeneratedParagraph = destinationDocument.createParagraph();

                for (UserContent userContent : entry.getValue()) {
                    final UserContentRawCopy userContentRawCopy = new UserContentRawCopy();
                    try {
                        currentGeneratedParagraph = destinationDocument.createParagraph();
                        currentGeneratedParagraph = userContentRawCopy.copy(userContent, currentGeneratedParagraph,
                                destinationDocument);
                    } catch (InvalidFormatException e) {
                        M2DocUtils.appendMessageRun(currentGeneratedParagraph, ValidationMessageLevel.ERROR,
                                USERDOC_COPY_ERROR + e.getMessage());
                        result.updateLevel(ValidationMessageLevel.ERROR);
                    } catch (XmlException e) {
                        M2DocUtils.appendMessageRun(currentGeneratedParagraph, ValidationMessageLevel.ERROR,
                                USERDOC_COPY_ERROR + e.getMessage());
                        result.updateLevel(ValidationMessageLevel.ERROR);
                    } catch (IOException e) {
                        M2DocUtils.appendMessageRun(currentGeneratedParagraph, ValidationMessageLevel.ERROR,
                                USERDOC_COPY_ERROR + e.getMessage());
                        result.updateLevel(ValidationMessageLevel.ERROR);
                    }
                }

                POIServices.getInstance().saveFile(uriConverter, destinationDocument, lostUserContentURI);
            }
        }
    }

    /**
     * Gets the lost {@link UserContent} {@link URI} for the given destination {@link URI} and {@link UserContent#getId() user content ID}.
     * 
     * @param dest
     *            the destination {@link URI}
     * @param id
     *            the {@link UserContent#getId() user content ID}
     * @return the lost {@link UserContent} {@link URI} for the given destination {@link URI} and {@link UserContent#getId() user content
     *         ID}
     */
    protected URI getLostUserContentURI(URI dest, String id) {
        final URI res = URI.createURI("./" + dest.lastSegment() + "-" + id + "-lost.docx");

        return res.resolve(dest);
    }

}