Java tutorial
/******************************************************************************* * 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 com.google.common.base.Predicates; import com.google.common.collect.Iterables; import java.util.List; import java.util.Map; import org.apache.poi.xwpf.usermodel.IBody; import org.apache.poi.xwpf.usermodel.IRunBody; import org.apache.poi.xwpf.usermodel.UnderlinePatterns; import org.apache.poi.xwpf.usermodel.XWPFDocument; import org.apache.poi.xwpf.usermodel.XWPFParagraph; import org.apache.poi.xwpf.usermodel.XWPFRun; import org.apache.poi.xwpf.usermodel.XWPFTable; import org.apache.poi.xwpf.usermodel.XWPFTableCell; import org.apache.poi.xwpf.usermodel.XWPFTableRow; import org.obeonetwork.m2doc.provider.AbstractTableProvider; import org.obeonetwork.m2doc.provider.AbstractTableProvider.MCell; import org.obeonetwork.m2doc.provider.AbstractTableProvider.MColumn; import org.obeonetwork.m2doc.provider.AbstractTableProvider.MRow; import org.obeonetwork.m2doc.provider.AbstractTableProvider.MStyle; import org.obeonetwork.m2doc.provider.AbstractTableProvider.MTable; import org.obeonetwork.m2doc.provider.ProviderException; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTbl; import static com.google.common.base.Preconditions.checkNotNull; import static org.obeonetwork.m2doc.provider.ProviderConstants.HIDE_TITLE_KEY; /** * Class that inserts a table in a document for a m:wtable tag. * * @author ldelaigue */ public final class TableClientProcessor { /** The current generated document. */ private final IBody body; /** Arguments of the m:wtable tag. */ private final Map<String, Object> parameters; /** The table provider. */ private final AbstractTableProvider provider; /** * Constructor. * * @param body * The current generated document, must not be <code>null</code> * @param provider * The table provider, must not be <code>null</code> * @param arguments * The map of arguments, cannot be <code>null</code> */ public TableClientProcessor(IBody body, AbstractTableProvider provider, Map<String, Object> arguments) { this.body = checkNotNull(body); this.provider = checkNotNull(provider); this.parameters = checkNotNull(arguments); } /** * Generate the table(s) in the document. * * @param run * The run where the tables must be output * @throws ProviderException * If the retrieval of the tables from the provider goes wrong. */ public void generate(XWPFRun run) throws ProviderException { List<MTable> tables = provider.getTables(parameters); boolean first = true; for (MTable mtable : tables) { XWPFTable table = createTable(run, first, mtable); if (table != null) { fillTable(table, mtable); first = false; } } } /** * Do we output the table title. * * @return Whether the table must have a title or not. */ private boolean showTitle() { if (parameters.containsKey(HIDE_TITLE_KEY)) { Object hide = parameters.get(HIDE_TITLE_KEY); if (hide instanceof String) { return !Boolean.valueOf((String) hide).booleanValue(); } } return true; } /** * Create a table justa after a given run. * * @param tableRun * The run * @param first * Whether it's the 1st table to create or not * @param mtable * The table description * @return The newly created table, can be <code>null</code> if the tag was used in an unsupported context. */ private XWPFTable createTable(XWPFRun tableRun, boolean first, MTable mtable) { XWPFTable table = null; if (body instanceof XWPFDocument) { table = createTableInDocument(tableRun, first, mtable); } else if (body instanceof XWPFTableCell) { XWPFTableCell tcell = (XWPFTableCell) body; table = createTableInCell(tableRun, first, mtable, tcell); } else { TemplateProcessor.setErrorMessageToRun("m:table is not supported in headers, footers, or footnotes.", tableRun); } return table; } /** * Create a table in a document. * * @param tableRun * the run after which the table must be created * @param first * Whether it's the first table to insert or not * @param mtable * The table description * @return The newly created table. */ private XWPFTable createTableInDocument(XWPFRun tableRun, boolean first, MTable mtable) { XWPFTable table; if (!first) { ((XWPFDocument) body).createParagraph(); } if (showTitle() && mtable.getLabel() != null) { XWPFRun captionRun; if (first) { captionRun = tableRun; IRunBody runBody = captionRun.getParent(); if (runBody instanceof XWPFParagraph) { ((XWPFParagraph) runBody).setSpacingAfter(0); } } else { XWPFParagraph captionParagraph = ((XWPFDocument) body).createParagraph(); captionParagraph.setSpacingAfter(0); captionRun = captionParagraph.createRun(); } captionRun.setText(mtable.getLabel()); captionRun.setBold(true); } table = ((XWPFDocument) body).createTable(); return table; } /** * Create a table in a table cell. * * @param tableRun * The table run * @param first * whether it's the 1st table created in this cell * @param mtable * The table description * @param tcell * The cell in which to create a new table * @return The newly creatted table, located in the given cell. */ private XWPFTable createTableInCell(XWPFRun tableRun, boolean first, MTable mtable, XWPFTableCell tcell) { XWPFTable table; if (showTitle() && mtable.getLabel() != null) { XWPFRun captionRun; if (first) { captionRun = tableRun; IRunBody runBody = captionRun.getParent(); if (runBody instanceof XWPFParagraph) { ((XWPFParagraph) runBody).setSpacingAfter(0); } } else { XWPFParagraph captionParagraph = tcell.addParagraph(); captionParagraph.setSpacingAfter(0); captionRun = captionParagraph.createRun(); } captionRun.setText(mtable.getLabel()); captionRun.setBold(true); } CTTbl ctTbl = tcell.getCTTc().addNewTbl(); table = new XWPFTable(ctTbl, tcell); int tableRank = tcell.getTables().size(); tcell.insertTable(tableRank, table); // A paragraph is mandatory at the end of a cell, so let's always add one. tcell.addParagraph(); return table; } /** * Fill a newly created word table with the data from an MTable. * * @param table * The newly created word table * @param mtable * The MTable that describes the data and styles to insert */ private void fillTable(XWPFTable table, MTable mtable) { XWPFTableRow headerRow = table.getRow(0); initializeEmptyTableCell(headerRow.getCell(0), null, null); Iterable<? extends MColumn> mcolumns = mtable.getColumns(); for (MColumn mcol : mcolumns) { XWPFTableCell cell; cell = headerRow.addNewTableCell(); initializeEmptyTableCell(cell, null, null); setCellContent(cell, mcol.getLabel(), null); } for (MRow mrow : mtable.getRows()) { XWPFTableRow row = table.createRow(); List<XWPFTableCell> cells = row.getTableCells(); for (int i = 0; i < cells.size(); i++) { XWPFTableCell cell = cells.get(i); // Make sure empty cells are empty and have the right style if (i > 0) { initializeEmptyTableCell(cell, mrow, Iterables.get(mtable.getColumns(), i - 1)); } else { initializeEmptyTableCell(cell, null, null); } } XWPFTableCell cell0 = row.getCell(0); setCellContent(cell0, mrow.getLabel(), null); for (MCell mcell : mrow.getCells()) { MColumn mcol = mcell.getColumn(); if (mcol != null) { XWPFTableCell cell = row.getCell(Iterables.indexOf(mcolumns, Predicates.equalTo(mcol)) + 1); setCellContent(cell, mcell.getLabel(), mcell.getStyle()); } } } } /** * Initialize properrly a new table cell to make it easy to insert the data in this cell. Deals with the style to apply to this cell * depending on the row and column it belongs to. * * @param cell * Newly created cell to initialize * @param row * The row the cell belongs to, the style of which will be used for the cell if defined. * @param column * The column the cell belongs to, the style of which will be used for the cell if defined and the row's style is * <code>null</code>. * @return The paragraph of the cell after initialization. */ private XWPFParagraph initializeEmptyTableCell(XWPFTableCell cell, MRow row, MColumn column) { XWPFParagraph cellParagraph = cell.getParagraphs().get(0); cellParagraph.setSpacingBefore(0); cellParagraph.setSpacingAfter(0); MStyle style = row == null ? null : row.getStyle(); if (style == null) { style = column == null ? null : column.getStyle(); } if (style != null) { cell.setColor(hexColor(style.getBackgroundColor())); } return cellParagraph; } /** * Create a new run in the cell's paragraph and set this run's text, and apply the given style to the cell and its paragraph. * * @param cell * Cell to fill in * @param text * Text to set in the cell * @param style * Style to use, can be <code>null</code> */ private void setCellContent(XWPFTableCell cell, String text, MStyle style) { XWPFParagraph cellParagraph = cell.getParagraphs().get(0); XWPFRun cellRun = cellParagraph.createRun(); cellRun.setText(text); if (style != null) { cell.setColor(hexColor(style.getBackgroundColor())); applyTableClientStyle(cellRun, style); } } /** * Apply the given style to the given run. Background color is not taken into account here since it does not apply to runs. * * @param run * The run to style * @param style * The style to apply, can be <code>null</code> */ private void applyTableClientStyle(XWPFRun run, MStyle style) { run.setFontSize(style.getFontSize()); run.setBold((style.getFontModifiers() & MStyle.FONT_BOLD) != 0); run.setItalic((style.getFontModifiers() & MStyle.FONT_ITALIC) != 0); if ((style.getFontModifiers() & MStyle.FONT_UNDERLINE) != 0) { run.setUnderline(UnderlinePatterns.SINGLE); } run.setStrikeThrough((style.getFontModifiers() & MStyle.FONT_STRIKE_THROUGH) != 0); run.setColor(hexColor(style.getForegroundColor())); } /** * Translate an int color from the {@link MStyle} format to the word format. * * @param color * The color, as an int * @return The color as a 6-digits string. */ private static String hexColor(int color) { String result = Integer.toHexString(color); while (result.length() < 6) { result = "0" + result; } if (result.length() > 6) { result = result.substring(result.length() - 6); } return result; } }