diff --git a/src/main/java/at/ac/tuwien/damap/conversion/AbstractTemplateExportFunctions.java b/src/main/java/at/ac/tuwien/damap/conversion/AbstractTemplateExportFunctions.java index 1ad716e1..53bdb73b 100644 --- a/src/main/java/at/ac/tuwien/damap/conversion/AbstractTemplateExportFunctions.java +++ b/src/main/java/at/ac/tuwien/damap/conversion/AbstractTemplateExportFunctions.java @@ -2,6 +2,7 @@ import java.io.IOException; import java.io.InputStream; +import java.lang.reflect.Field; import java.util.*; import java.text.SimpleDateFormat; @@ -35,7 +36,7 @@ public abstract class AbstractTemplateExportFunctions { * @return * @throws Exception */ - public XWPFDocument loadTemplate (InputStream template, String startChar, String endChar) throws Exception{ + public XWPFDocument loadTemplate(InputStream template, String startChar, String endChar) throws Exception { //Extract document using Apache POI https://poi.apache.org/ XWPFDocument document = new XWPFDocument(template); @@ -119,7 +120,7 @@ public void addReplacement(Map replacements, String variable, Ob * @throws XmlException * @throws Exception */ - public XWPFTableRow insertNewTableRow(XWPFTableRow sourceTableRow, int pos) throws XmlException, IOException { + public XWPFTableRow insertNewTableRow(XWPFTableRow sourceTableRow, int pos) throws XmlException, IOException { XWPFTable table = sourceTableRow.getTable(); CTRow newCTRrow = CTRow.Factory.parse(sourceTableRow.getCtRow().newInputStream()); XWPFTableRow tableRow = new XWPFTableRow(newCTRrow, table); @@ -152,7 +153,7 @@ static void insertTableCells(XWPFTable table, XWPFTableRow newRow, ArrayList replacements){ + static void replaceTableVariables(XWPFTable table, Map replacements) { //this replaces variables in tables (e.g. costcurrency) List tableRows = table.getRows(); for (XWPFTableRow xwpfTableRow : tableRows) { @@ -260,7 +261,7 @@ public XWPFDocument templateFormatting(XWPFDocument document, String startChar, * @param startChar * @param endChar */ - public void formattingTable(List xwpfTables, String startChar, String endChar){ + public void formattingTable(List xwpfTables, String startChar, String endChar) { if (xwpfTables != null) { for (XWPFTable xwpfTable : xwpfTables) { for (XWPFTableRow row : xwpfTable.getRows()) { @@ -297,20 +298,18 @@ public void formattingParagraph(List xwpfParagraphs, String start if (!xwpfRunText.contains(endChar)) { removeRunIndex.add(xwpfRuns.indexOf(xwpfRun)); mergeRun = true; - if (sb.length()>0) { + if (sb.length() > 0) { sb.delete(0, sb.length()); } sb.append(xwpfRunText); } - } - else { + } else { if (mergeRun) { sb.append(xwpfRunText); if (xwpfRunText.contains(endChar)) { mergeRun = false; - xwpfRun.setText(sb.toString(),0); - } - else { + xwpfRun.setText(sb.toString(), 0); + } else { removeRunIndex.add(xwpfRuns.indexOf(xwpfRun)); } } @@ -326,4 +325,108 @@ public void formattingParagraph(List xwpfParagraphs, String start } } } + + /** + * Removes a table inside a document or nested inside another table. + * For nested tables, this works only for depth 1. + * If the table is not to be found inside the doc or at depth 1, nothing happens. + * + * @param doc + * @param table + */ + public void removeTable(XWPFDocument doc, XWPFTable table) { + int pos = doc.getPosOfTable(table); + if (pos != -1) { + doc.removeBodyElement(pos); + } else { // table not found in document -> nestedTable + removeNestedTable(doc, table); + } + } + + /** + * Removes a table which is nested inside another table. Max nested depth is 1. + * + * @param doc + * @param table + */ + private void removeNestedTable(XWPFDocument doc, XWPFTable table) { + for (XWPFTableCell cell : getAllOuterTableCells(doc)) { + for (XWPFTable nestedTable : cell.getTables()) { + if (nestedTable.equals(table)) { + int pos = cell.getTables().indexOf(nestedTable); + // dirty hack since POI XWPF does not offer functionality for removing nested tables + try { + Field beField = cell.getClass().getDeclaredField("tables"); + beField.setAccessible(true); + ((List) beField.get(cell)).remove(pos); // higher level representation + } catch (Exception ignored) { + } + cell.getCTTc().removeTbl(pos); // low level cell representation + + return; + } + } + } + } + + /** + * Removes a table inside a document or nested inside another table. Also removes the paragraph above it. + * For nested tables, this works only for depth 1. + * If the table is not to be found inside the doc or at depth 1, nothing happens. + * If the there is no paragraph above the table, only the table will be removed. + * + * @param doc + * @param table + */ + public void removeTableAndParagraphAbove(XWPFDocument doc, XWPFTable table) { + // paragraph above the table + if (doc.getPosOfTable(table) != -1) { + int paragraphPos = doc.getPosOfTable(table) - 1; + if (doc.getBodyElements().get(paragraphPos).getElementType().equals(BodyElementType.PARAGRAPH)) { + doc.removeBodyElement(paragraphPos); + } + } else { + for (XWPFTableCell cell : getAllOuterTableCells(doc)) { + for (XWPFTable nestedTable : cell.getTables()) { + if (nestedTable.equals(table)) { + int paragraphPos = cell.getBodyElements().indexOf(table) - 1; + if (cell.getBodyElements().get(paragraphPos).getElementType().equals(BodyElementType.PARAGRAPH)) { + XWPFParagraph paragraphToRemove = (XWPFParagraph) cell.getBodyElements().get(paragraphPos); + cell.removeParagraph(cell.getParagraphs().indexOf(paragraphToRemove)); + } + } + } + } + } + // delete unnecessary table + removeTable(doc, table); + } + + /** + * Returns a list of all table cells of non nested tables in the document. + * + * @param doc + */ + public List getAllOuterTableCells(XWPFDocument doc) { + List tableCells = new ArrayList<>(); + for (XWPFTable outerTable : doc.getTables()) { + for (XWPFTableRow row : outerTable.getRows()) { + tableCells.addAll(row.getTableCells()); + } + } + return tableCells; + } + + /** + * Returns a list of all tables and nested tables with depth 1. + * + * @param doc + */ + public List getAllTables(XWPFDocument doc) { + ArrayList tables = new ArrayList<>(doc.getTables()); + for (XWPFTableCell cell : getAllOuterTableCells(doc)) { + tables.addAll(cell.getTables()); + } + return tables; + } } diff --git a/src/main/java/at/ac/tuwien/damap/conversion/AbstractTemplateExportScienceEuropeComponents.java b/src/main/java/at/ac/tuwien/damap/conversion/AbstractTemplateExportScienceEuropeComponents.java index e602081c..bd2112aa 100644 --- a/src/main/java/at/ac/tuwien/damap/conversion/AbstractTemplateExportScienceEuropeComponents.java +++ b/src/main/java/at/ac/tuwien/damap/conversion/AbstractTemplateExportScienceEuropeComponents.java @@ -7,6 +7,7 @@ import org.apache.poi.xwpf.usermodel.*; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTVMerge; +import java.lang.reflect.Field; import java.text.NumberFormat; import java.time.ZoneId; import java.time.ZonedDateTime; @@ -706,8 +707,9 @@ protected static String format(long number) { } //All tables variables replacement - public void tableContent(List xwpfTables) { - for (XWPFTable xwpfTable : xwpfTables) { + // Takes care of filling tables and deletes certain empty tables + public void tableContent(XWPFDocument document, List xwpfTables) { + for (XWPFTable xwpfTable : new ArrayList<>(xwpfTables)) { XWPFTableRow tableIdentifierRow = xwpfTable.getRow(1); if (tableIdentifierRow != null) { @@ -728,7 +730,7 @@ public void tableContent(List xwpfTables) { composeTableNewDatasets(xwpfTable); break; case ("[reusedDatasetTable]"): - composeTableReusedDatasets(xwpfTable); + composeTableReusedDatasets(document, xwpfTable); break; case ("[datasetAccessTable]"): composeTableDataAccess(xwpfTable); @@ -740,14 +742,20 @@ public void tableContent(List xwpfTables) { composeTableDatasetRepository(xwpfTable); break; case ("[datasetDeleteTable]"): - composeTableDatasetDeletion(xwpfTable); + composeTableDatasetDeletion(document, xwpfTable); break; case ("[costTable]"): composeTableCost(xwpfTable); break; + default: + break; } } - replaceTableVariables(xwpfTable, replacements); + } + + // prevents replacing table variables of deleted tables + for (XWPFTable table : getAllTables(document)) { + replaceTableVariables(table, replacements); } } @@ -824,7 +832,7 @@ public List getReusedDatasets(){ return datasets.stream().filter(dataset -> dataset.getSource().equals(EDataSource.REUSED)).collect(Collectors.toList()); } - public void composeTableReusedDatasets(XWPFTable xwpfTable){ + public void composeTableReusedDatasets(XWPFDocument document, XWPFTable xwpfTable){ log.debug("Export steps: Reused Dataset Table"); List reusedDatasets = getReusedDatasets(); @@ -877,13 +885,10 @@ public void composeTableReusedDatasets(XWPFTable xwpfTable){ insertTableCells(xwpfTable, newRow, docVar); } xwpfTable.removeRow(xwpfTable.getRows().size() - 1); + xwpfTable.removeRow(1); } else { - //clean row - ArrayList emptyContent = new ArrayList(Arrays.asList("", "", "", "", "", "")); - insertTableCells(xwpfTable, xwpfTable.getRows().get(xwpfTable.getRows().size() - 1), emptyContent); + removeTableAndParagraphAbove(document, xwpfTable); } - //end of dynamic table rows code - xwpfTable.removeRow(1); } public void composeTableDataAccess(XWPFTable xwpfTable){ @@ -902,6 +907,7 @@ public void composeTableDataAccess(XWPFTable xwpfTable){ insertTableCells(xwpfTable, xwpfTable.getRows().get(xwpfTable.getRows().size() - 1), emptyContent); } xwpfTable.removeRow(1); + replaceTableVariables(xwpfTable, replacements); } private void insertComposeTableDataAccess(XWPFTable xwpfTable, List currentDatasets){ @@ -1100,7 +1106,7 @@ public void composeTableDatasetRepository(XWPFTable xwpfTable){ commitTableRows(xwpfTable); } - public void composeTableDatasetDeletion(XWPFTable xwpfTable){ + public void composeTableDatasetDeletion(XWPFDocument document, XWPFTable xwpfTable){ log.debug("Export steps: Dataset Deletion Table"); if (deletedDatasets.size() > 0) { @@ -1141,12 +1147,11 @@ public void composeTableDatasetDeletion(XWPFTable xwpfTable){ insertTableCells(xwpfTable, newRow, docVar); } xwpfTable.removeRow(xwpfTable.getRows().size() - 1); + xwpfTable.removeRow(1); } else { - //clean row - ArrayList emptyContent = new ArrayList(Arrays.asList("", "", "", "", "")); - insertTableCells(xwpfTable, xwpfTable.getRows().get(xwpfTable.getRows().size() - 1), emptyContent); + removeTableAndParagraphAbove(document, xwpfTable); } - xwpfTable.removeRow(1); + } public void composeTableCost(XWPFTable xwpfTable){ diff --git a/src/main/java/at/ac/tuwien/damap/conversion/ExportFWFTemplate.java b/src/main/java/at/ac/tuwien/damap/conversion/ExportFWFTemplate.java index b60ef89d..ccafaf8d 100644 --- a/src/main/java/at/ac/tuwien/damap/conversion/ExportFWFTemplate.java +++ b/src/main/java/at/ac/tuwien/damap/conversion/ExportFWFTemplate.java @@ -38,7 +38,7 @@ public XWPFDocument exportTemplate(long dmpId) { //in FWF template this replaces paragraphs within the template table replaceTableVariables(templateTable, replacements); //this replaces the tables with the main template table - tableContent(templateXwpfTables); + tableContent(document, templateXwpfTables); return document; } @@ -46,7 +46,7 @@ public XWPFDocument exportTemplate(long dmpId) { private List parseContentTables(XWPFTable templateTable) { List templateXwpfTables = new ArrayList<>(); for (XWPFTableRow row : templateTable.getRows()){ - if (row.getTableCells().size() > 1){ + if (row.getTableCells().size() > 1) { templateXwpfTables.addAll(row.getCell(1).getTables()); } } diff --git a/src/main/java/at/ac/tuwien/damap/conversion/ExportHorizonEuropeTemplate.java b/src/main/java/at/ac/tuwien/damap/conversion/ExportHorizonEuropeTemplate.java index ad5983f2..196095ac 100644 --- a/src/main/java/at/ac/tuwien/damap/conversion/ExportHorizonEuropeTemplate.java +++ b/src/main/java/at/ac/tuwien/damap/conversion/ExportHorizonEuropeTemplate.java @@ -51,7 +51,7 @@ public XWPFDocument exportTemplate(long dmpId) { // TO DO: combine the function with the first row generation to avoid double // code of similar modification. log.debug("Export steps: Replace in table"); - tableContent(xwpfTables); + tableContent(document, xwpfTables); // Fourth step of the export: modify the content of the document's footer log.debug("Export steps: Replace in footer"); diff --git a/src/main/java/at/ac/tuwien/damap/conversion/ExportScienceEuropeTemplate.java b/src/main/java/at/ac/tuwien/damap/conversion/ExportScienceEuropeTemplate.java index 360f18e6..d12d49f1 100644 --- a/src/main/java/at/ac/tuwien/damap/conversion/ExportScienceEuropeTemplate.java +++ b/src/main/java/at/ac/tuwien/damap/conversion/ExportScienceEuropeTemplate.java @@ -38,7 +38,7 @@ public XWPFDocument exportTemplate(long dmpId) { //Third step of the export: dynamic table in all sections will be added from row number two until the end of data list. //TO DO: combine the function with the first row generation to avoid double code of similar modification. log.debug("Export steps: Replace in table"); - tableContent(xwpfTables); + tableContent(document, xwpfTables); //Fourth step of the export: modify the content of the document's footer log.debug("Export steps: Replace in footer");