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

Java tutorial

Introduction

Here is the source code for org.obeonetwork.m2doc.generator.UserContentRawCopy.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.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.xwpf.usermodel.IBody;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFHeaderFooter;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFPicture;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.apache.poi.xwpf.usermodel.XWPFStyle;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.apache.poi.xwpf.usermodel.XWPFTableRow;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlToken;
import org.obeonetwork.m2doc.template.IConstruct;
import org.obeonetwork.m2doc.template.Table;
import org.obeonetwork.m2doc.template.UserContent;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTbl;

/**
 * UserContent Raw Copy.
 * 
 * @author ohaegi
 */
public class UserContentRawCopy {

    /**
     * Current Input Paragraph.
     */
    private XWPFParagraph currentInputParagraph;

    /**
     * Current Output Paragraph.
     */
    private XWPFParagraph currentOutputParagraph;
    /**
     * Previous Input Paragraph of UserContent tag content.
     */
    private XWPFParagraph previousInputParagraph;

    /**
     * List of Output Paragraphs.
     * Used to custom id relation picture.
     */
    private List<XWPFParagraph> listOutputParagraphs = new ArrayList<XWPFParagraph>();
    /**
     * List Output Tables.
     * Used to custom id relation picture.
     */
    private List<XWPFTable> listOutputTables = new ArrayList<XWPFTable>();
    /**
     * List Output Runs.
     * Those runs is located at begin of usercontent tag content before first paragraph
     * Used to custom id relation picture.
     */
    private List<XWPFRun> listOutputRuns = new ArrayList<XWPFRun>();

    /**
     * Input Picute Id To Output map.
     * Used to custom id relation picture.
     */
    private Map<String, String> inputPicuteIdToOutputmap = new HashMap<String, String>();

    /**
     * Need new paragraph after copy.
     * Last Content Run And EndUserContent are not In Same Paragraph.
     */
    private boolean needNewParagraph = true;

    /**
     * Is last Content Run And EndUserContent are In Same Paragraph.
     * 
     * @return the needNewParagraph
     */
    public boolean needNewParagraph() {
        return needNewParagraph;
    }

    /**
     * Copy.
     * 
     * @param userContent
     *            UserContent EObject
     * @param outputParagraphBeforeUserDocContent
     *            Output Paragraph Before User Doc Dest content (User Code dest is writen by {@link M2DocEvaluator} )
     * @param outputBody
     *            output body
     * @return last paragraph created by copy
     * @throws InvalidFormatException
     *             InvalidFormatException
     * @throws XmlException
     *             XmlException
     * @throws IOException
     *             if the copy fails
     */
    @SuppressWarnings("resource")
    public XWPFParagraph copy(UserContent userContent, XWPFParagraph outputParagraphBeforeUserDocContent,
            IBody outputBody) throws InvalidFormatException, XmlException, IOException {
        XWPFDocument containerOutputDocument = outputParagraphBeforeUserDocContent.getDocument();
        // Test if run before userContent is in same XWPFParagraph than first run of userContent
        if (!userDocContentIsFirstRunOfParagraph(userContent)) {
            previousInputParagraph = (XWPFParagraph) userContent.getRuns().get(userContent.getRuns().size() - 1)
                    .getParent();
            currentInputParagraph = previousInputParagraph;
            currentOutputParagraph = outputParagraphBeforeUserDocContent;
        }
        XWPFParagraph currentRunParagraph = null;
        for (IConstruct abstractConstruct : userContent.getBody().getStatements()) {
            for (XWPFRun inputRun : abstractConstruct.getRuns()) {
                currentRunParagraph = (XWPFParagraph) inputRun.getParent();
                if (currentRunParagraph != currentInputParagraph) {
                    currentInputParagraph = currentRunParagraph;
                    // currentOutputParagraph = outputDocument.createParagraph();
                    currentOutputParagraph = createNewParagraph(outputBody);
                    // Copy new paragraph
                    currentOutputParagraph.getCTP().set(currentInputParagraph.getCTP());
                    listOutputParagraphs.add(currentOutputParagraph);
                }
                // Test if some run exist between userContent tag and first paragraph in this tag
                if (currentRunParagraph == previousInputParagraph) {
                    // Clone run directly, paragraph is already generate by normal processing
                    XWPFRun outputRun = currentOutputParagraph.createRun();
                    outputRun.getCTR().set(inputRun.getCTR());
                    // Keep run to change relation id later
                    listOutputRuns.add(outputRun);
                }
                // Create picture embedded in run and keep relation id in map (input to output)
                createPictures(inputRun, containerOutputDocument);
            }
            // In case of table (no run in abstractConstruct)
            if (abstractConstruct instanceof Table) {
                Table table = (Table) abstractConstruct;
                XWPFTable inputTable = table.getTable();
                // XWPFTable outputTable = contenerOutputDocument.createTable();
                XWPFTable outputTable = createNewTable(outputBody, inputTable);
                outputTable.getCTTbl().set(inputTable.getCTTbl());
                copyTableStyle(inputTable, containerOutputDocument);
                listOutputTables.add(outputTable);
                // Inspect table to extract all picture ID in run
                collectRelationId(inputTable, containerOutputDocument);

            }
        }
        // Change Picture Id by xml replacement
        changePictureId();

        if (userContent.getClosingRuns().size() != 0
                && currentInputParagraph == userContent.getClosingRuns().get(0).getParent()) {
            needNewParagraph = false;
        }
        return currentOutputParagraph;
    }

    /**
     * Create new Table.
     * TODO OHA fix bug in nested table on usercontent (else case in code)
     * 
     * @param document
     *            document
     * @param inputTable
     *            input Table
     * @return get Table
     */
    private XWPFTable createNewTable(IBody document, XWPFTable inputTable) {
        XWPFTable generatedTable;
        CTTbl copy = (CTTbl) inputTable.getCTTbl().copy();
        if (document instanceof XWPFDocument) {
            generatedTable = ((XWPFDocument) document).createTable();
        } else if (document instanceof XWPFTableCell) {
            XWPFTableCell tCell = (XWPFTableCell) document;
            int tableRank = tCell.getTables().size();
            generatedTable = new XWPFTable(copy, tCell, 0, 0);
            tCell.insertTable(tableRank, generatedTable);
            generatedTable = tCell.getTables().get(tableRank);
        } else {
            throw new UnsupportedOperationException("unknown type of IBody : " + document.getClass());
        }
        return generatedTable;
    }

    /**
     * Create New Paragraph.
     * 
     * @param document
     *            document
     * @return new paragraph
     */
    private XWPFParagraph createNewParagraph(IBody document) {
        XWPFParagraph newParagraph;
        if (document instanceof XWPFTableCell) {
            XWPFTableCell cell = (XWPFTableCell) document;
            newParagraph = cell.addParagraph();
        } else if (document instanceof XWPFDocument) {
            newParagraph = ((XWPFDocument) document).createParagraph();
        } else if (document instanceof XWPFHeaderFooter) {
            newParagraph = ((XWPFHeaderFooter) document).createParagraph();
        } else {
            throw new UnsupportedOperationException("unkown IBody type :" + document.getClass());
        }
        return newParagraph;
    }

    /**
     * Copy Table Style.
     * 
     * @param inputTable
     *            input Table
     * @param outputDoc
     *            outputDoc where copy style
     * @throws IOException
     *             if the copy fails
     */
    private static void copyTableStyle(XWPFTable inputTable, XWPFDocument outputDoc) throws IOException {
        try (XWPFDocument inputDoc = inputTable.getBody().getXWPFDocument();) {
            XWPFStyle style = inputDoc.getStyles().getStyle(inputTable.getStyleID());
            if (outputDoc == null || style == null) {
                return;
            }

            if (outputDoc.getStyles() == null) {
                outputDoc.createStyles();
            }

            List<XWPFStyle> usedStyleList = inputDoc.getStyles().getUsedStyleList(style);
            for (XWPFStyle xwpfStyle : usedStyleList) {
                outputDoc.getStyles().addStyle(xwpfStyle);
            }
        }
    }

    /**
     * Collect Relation Id in table.
     * Put picture in output document and keep old and new picture id in map.
     * 
     * @param inputWTable
     *            inputWTable
     * @param outputDoc
     *            outputDoc
     * @throws InvalidFormatException
     *             InvalidFormatException
     */
    private void collectRelationId(XWPFTable inputWTable, XWPFDocument outputDoc) throws InvalidFormatException {
        for (XWPFTableRow row : inputWTable.getRows()) {
            for (XWPFTableCell cell : row.getTableCells()) {
                for (XWPFParagraph paragraph : cell.getParagraphs()) {
                    collectRelationId(paragraph, outputDoc);
                }
            }
        }
    }

    /**
     * Collect Relation Id in paragraph.
     * 
     * @param paragraph
     *            paragraph
     * @param outputDoc
     *            outputDoc
     * @throws InvalidFormatException
     *             InvalidFormatException
     */
    private void collectRelationId(XWPFParagraph paragraph, XWPFDocument outputDoc) throws InvalidFormatException {
        for (XWPFRun run : paragraph.getRuns()) {
            createPictures(run, outputDoc);
        }
    }

    /**
     * Change Picture Id.
     * 
     * @throws XmlException
     *             XmlException
     */
    private void changePictureId() throws XmlException {

        for (XWPFRun run : listOutputRuns) {
            XmlToken outputXmlObject = getXmlWithOuputId(run.getCTR().xmlText());
            if (outputXmlObject != null) {
                run.getCTR().set(outputXmlObject);
            }
        }

        for (XWPFParagraph paragraph : listOutputParagraphs) {
            XmlToken outputXmlObject = getXmlWithOuputId(paragraph.getCTP().xmlText());
            if (outputXmlObject != null) {
                paragraph.getCTP().set(outputXmlObject);
            }
        }

        for (XWPFTable table : listOutputTables) {
            XmlToken outputXmlObject = getXmlWithOuputId(table.getCTTbl().xmlText());
            if (outputXmlObject != null) {
                table.getCTTbl().set(outputXmlObject);
            }
        }

    }

    /**
     * Get Xml With Ouput picture Id.
     * 
     * @param xmlText
     *            xmlText
     * @return Xml With Ouput picture Id
     * @throws XmlException
     *             XmlException
     */
    private XmlToken getXmlWithOuputId(String xmlText) throws XmlException {
        String outputXmlStr = xmlText;
        for (Map.Entry<String, String> entry : inputPicuteIdToOutputmap.entrySet()) {
            String inputID = entry.getKey();
            String outputID = entry.getValue();
            outputXmlStr = outputXmlStr.replaceAll("<a:blip r:embed=\"" + inputID + "\"",
                    "<a:blip r:embed=NEW\"" + outputID + "\"");
        }
        // Clean build string
        outputXmlStr = outputXmlStr.replaceAll("<a:blip r:embed=NEW", "<a:blip r:embed=");

        XmlToken outputXmlObject = null;
        outputXmlObject = XmlToken.Factory.parse(outputXmlStr);
        return outputXmlObject;
    }

    /**
     * Test if first userContent content begin by a paragraph.
     * 
     * @param userContent
     *            userContent EObject
     * @return true if userContent content begin by a paragraph
     */
    private boolean userDocContentIsFirstRunOfParagraph(UserContent userContent) {
        boolean result = true;
        if (userContent.getBody() != null && userContent.getBody().getStatements().size() > 0
                && userContent.getBody().getStatements().get(0).getRuns().size() > 0) {
            XWPFRun userContentFirstRun = userContent.getBody().getStatements().get(0).getRuns().get(0);
            XWPFParagraph userContentFirstRunParagraph = (XWPFParagraph) userContentFirstRun.getParent();
            if (userContentFirstRunParagraph != null && userContentFirstRunParagraph.getRuns().size() > 0) {
                XWPFRun paragraphFirstRun = userContentFirstRunParagraph.getRuns().get(0);
                result = userContentFirstRun == paragraphFirstRun;
            }
        }
        return result;
    }

    /**
     * createPictures in document.
     * 
     * @param inputRun
     *            input Run
     * @param outputDoc
     *            output Document
     * @throws InvalidFormatException
     *             InvalidFormatException
     */
    private void createPictures(XWPFRun inputRun, XWPFDocument outputDoc) throws InvalidFormatException {
        // Add picture in document and keep relation id change idRelation reference
        for (XWPFPicture inputPic : inputRun.getEmbeddedPictures()) {
            byte[] img = inputPic.getPictureData().getData();
            // Put image in doc and get idRelation
            String idRelationOutput = outputDoc.addPictureData(img, inputPic.getPictureData().getPictureType());
            String idRelationInput = inputPic.getCTPicture().getBlipFill().getBlip().getEmbed();
            inputPicuteIdToOutputmap.put(idRelationInput, idRelationOutput);
        }
    }

}