From 3c02ed472cf50189689ec89dee6eee6e7b9aa815 Mon Sep 17 00:00:00 2001 From: Quang Truong Date: Wed, 13 May 2026 10:01:46 +0200 Subject: [PATCH] Export footnote to excel --- .../export/pdf/TableToTableDocument.xtend | 2 +- .../export/xlsx/ExcelExportBuilder.java | 95 ++++++++++++++++++- .../extensions/TableExtensions.xtend | 37 +++++--- 3 files changed, 117 insertions(+), 17 deletions(-) diff --git a/java/bundles/org.eclipse.set.feature.export/src/org/eclipse/set/feature/export/pdf/TableToTableDocument.xtend b/java/bundles/org.eclipse.set.feature.export/src/org/eclipse/set/feature/export/pdf/TableToTableDocument.xtend index f4d9728199..68519ecfa3 100644 --- a/java/bundles/org.eclipse.set.feature.export/src/org/eclipse/set/feature/export/pdf/TableToTableDocument.xtend +++ b/java/bundles/org.eclipse.set.feature.export/src/org/eclipse/set/feature/export/pdf/TableToTableDocument.xtend @@ -66,7 +66,7 @@ class TableToTableDocument { public static val String FOOTNOTE_INLINE_TEXT_SEPARATOR = String. format("%n") - static val String FOOTNOTE_MARK_SEPRATOR = ", " + public static val String FOOTNOTE_MARK_SEPRATOR = ", " val Document doc var String tablename diff --git a/java/bundles/org.eclipse.set.feature.export/src/org/eclipse/set/feature/export/xlsx/ExcelExportBuilder.java b/java/bundles/org.eclipse.set.feature.export/src/org/eclipse/set/feature/export/xlsx/ExcelExportBuilder.java index 6b31bd8243..10ffd79a7a 100644 --- a/java/bundles/org.eclipse.set.feature.export/src/org/eclipse/set/feature/export/xlsx/ExcelExportBuilder.java +++ b/java/bundles/org.eclipse.set.feature.export/src/org/eclipse/set/feature/export/xlsx/ExcelExportBuilder.java @@ -19,7 +19,9 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; +import java.util.stream.Collectors; import org.apache.commons.io.FileUtils; import org.apache.poi.ss.usermodel.BorderStyle; @@ -38,9 +40,13 @@ import org.eclipse.set.basis.constants.ExportType; import org.eclipse.set.basis.constants.TableType; import org.eclipse.set.basis.exceptions.FileExportException; +import org.eclipse.set.feature.export.pdf.TableToTableDocument; +import org.eclipse.set.model.tablemodel.FootnoteContainer; +import org.eclipse.set.model.tablemodel.SimpleFootnoteContainer; import org.eclipse.set.model.tablemodel.Table; import org.eclipse.set.model.tablemodel.TableRow; import org.eclipse.set.model.tablemodel.extensions.TableExtensions; +import org.eclipse.set.model.tablemodel.extensions.TableExtensions.FootnoteInfo; import org.eclipse.set.model.tablemodel.extensions.TableRowExtensions; import org.eclipse.set.model.titlebox.Titlebox; import org.eclipse.set.services.export.TableExport; @@ -49,6 +55,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.common.collect.Streams; + /** * {@link TableExport} implementation for Excel with template files. * @@ -63,6 +71,7 @@ public class ExcelExportBuilder implements TableExport { .getLogger(ExcelExportBuilder.class); private static final String TEMPLATE_DIR = "./data/export/excel"; //$NON-NLS-1$ + private static final String FOONOTE_SHEET_NAME = "Bermerkungen"; //$NON-NLS-1$ private static int getFirstRowForContent(final Sheet sheet) { return getHeaderLastRowIndex(sheet) + 1; @@ -104,6 +113,8 @@ public void export(final Map tables, final OverwriteHandling overwriteHandling) throws FileExportException { final Table table = getTableToBeExported(tables); + final boolean isInlineFootnote = TableExtensions + .isInlineFootnote(table); // IMPROVE: this is only a temporary situation for the table // Sskp_dm final String tableShortcut = shortcut.equals("sskp_dm") ? "sskp" //$NON-NLS-1$//$NON-NLS-2$ @@ -135,7 +146,13 @@ public void export(final Map tables, final List rows = TableExtensions.getTableRows(table); // Fill sheet - fillSheet(sheet, rows, rowIndex, columnCount); + fillSheet(sheet, rows, rowIndex, columnCount, isInlineFootnote); + + if (!isInlineFootnote) { + final Sheet footnoteSheet = workbook + .createSheet(FOONOTE_SHEET_NAME); + fillFootnoteSheet(footnoteSheet, table); + } // Create spans addTableSpans(sheet, rows, rowIndex, columnCount); @@ -160,13 +177,54 @@ public void export(final Map tables, } } + @SuppressWarnings("boxing") + private static void fillFootnoteSheet(final Sheet footnoteSheet, + final Table table) { + final List allFootnotes = new ArrayList<>( + Streams.stream(TableExtensions.getAllFootnotes(table)) + .toList()); + if (allFootnotes.isEmpty()) { + return; + } + footnoteSheet.autoSizeColumn(1); + allFootnotes.sort( + (first, second) -> Integer.compare(first.index, second.index)); + for (int i = 0; i < allFootnotes.size(); i++) { + final Row row = footnoteSheet.createRow(i + 1); + final Cell fnIndexCell = row.createCell(0); + final FootnoteInfo footnoteInfo = allFootnotes.get(i); + // Currently export only the FINAL-State table to excel, therefore + // no need to handle cell style + fnIndexCell.setCellValue(String.format("*%d", footnoteInfo.index) //$NON-NLS-1$ + ); + final Cell fnContentCell = row.createCell(1); + fnContentCell.setCellValue(footnoteInfo.toText()); + + } + } + private static void fillSheet(final Sheet sheet, final List rows, - final int rowIndex, final int columnCount) { + final int rowIndex, final int columnCount, + final boolean inlineFootnote) { + if (rows.isEmpty()) { + return; + } + final Table table = TableRowExtensions.getTable(rows.getFirst()); + final List allFootnotes = Streams + .stream(TableExtensions.getAllFootnotes(table)) + .toList(); int contentRowIndex = rowIndex; + for (final TableRow row : rows) { final Row sheetRow = contentRowIndex == rowIndex ? sheet.getRow(contentRowIndex) : createNewRow(sheet, contentRowIndex, columnCount); + final FootnoteContainer footnotes = row.getFootnotes(); + // Currently export only FINAL-Table to excel, therefore the + // FootnoteContainer should be SimpleFootnoteContainer + if (!(footnotes instanceof SimpleFootnoteContainer)) { + throw new IllegalArgumentException(); + } for (int i = 0; i < columnCount; i++) { final String content = TableRowExtensions .getPlainStringValue(row, i); @@ -175,7 +233,12 @@ private static void fillSheet(final Sheet sheet, final List rows, if (cell == null) { cell = sheetRow.createCell(i + 1); } - + if (i == columnCount - 1 + && footnotes instanceof final SimpleFootnoteContainer simpleContainer) { + fillFootnoteCell(cell, content, allFootnotes, + simpleContainer, inlineFootnote); + continue; + } cell.setCellValue(content); } // Auto adjust row height @@ -184,6 +247,32 @@ private static void fillSheet(final Sheet sheet, final List rows, } } + private static void fillFootnoteCell(final Cell cell, + final String cellContent, final List allFootnotes, + final SimpleFootnoteContainer fnContainer, + final boolean inlineFootnote) { + final List fnInfo = fnContainer.getFootnotes() + .stream() + .map(fn -> TableExtensions.getFootnoteInfo(allFootnotes, fn)) + .filter(Objects::nonNull) + .toList(); + final StringBuilder builder = new StringBuilder(); + if (!cellContent.isEmpty() && !cellContent.isBlank()) { + builder.append(cellContent); + builder.append(TableToTableDocument.FOOTNOTE_INLINE_TEXT_SEPARATOR); + } + final String footnoteValue = inlineFootnote ? fnInfo.stream() + .map(FootnoteInfo::toText) + .collect(Collectors.joining( + TableToTableDocument.FOOTNOTE_INLINE_TEXT_SEPARATOR)) + : fnInfo.stream() + .map(fn -> "*" + fn.index) //$NON-NLS-1$ + .collect(Collectors.joining( + TableToTableDocument.FOOTNOTE_MARK_SEPRATOR)); + builder.append(footnoteValue); + cell.setCellValue(builder.toString()); + } + @SuppressWarnings("resource") private static Row createNewRow(final Sheet sheet, final int rowIndex, final int maxColIndex) { diff --git a/java/bundles/org.eclipse.set.model.tablemodel.extensions/src/org/eclipse/set/model/tablemodel/extensions/TableExtensions.xtend b/java/bundles/org.eclipse.set.model.tablemodel.extensions/src/org/eclipse/set/model/tablemodel/extensions/TableExtensions.xtend index 1ac69c1e4b..c921124102 100644 --- a/java/bundles/org.eclipse.set.model.tablemodel.extensions/src/org/eclipse/set/model/tablemodel/extensions/TableExtensions.xtend +++ b/java/bundles/org.eclipse.set.model.tablemodel.extensions/src/org/eclipse/set/model/tablemodel/extensions/TableExtensions.xtend @@ -41,6 +41,7 @@ import static extension org.eclipse.set.ppmodel.extensions.EObjectExtensions.* import static extension org.eclipse.set.ppmodel.extensions.UrObjectExtensions.* import static extension org.eclipse.set.ppmodel.extensions.utils.IterableExtensions.* import static extension org.eclipse.set.utils.StringExtensions.* +import org.eclipse.set.model.tablemodel.extensions.TableExtensions.FootnoteInfo /** * Extensions for {@link Table}. @@ -485,6 +486,29 @@ class TableExtensions { static def FootnoteInfo getFootnoteInfo(Table table, Bearbeitungsvermerk bv) { val allNotes = table.allFootnotes.toList + return allNotes.getFootnoteInfo(bv) + } + + static def FootnoteInfo getFootnoteInfo(EObject tableContent, Footnote fn) { + return getFootnoteInfo(tableContent, fn.bearbeitungsvermerk) + } + + static def FootnoteInfo getFootnoteInfo(EObject tableContent, + Bearbeitungsvermerk bv) { + var object = tableContent + while (!(object instanceof Table)) { + object = object.eContainer + } + return getFootnoteInfo(object as Table, bv) + } + + static def FootnoteInfo getFootnoteInfo(Iterable allNotes, + Footnote fn) { + return getFootnoteInfo(allNotes, fn.bearbeitungsvermerk) + } + + static def FootnoteInfo getFootnoteInfo(Iterable allNotes, + Bearbeitungsvermerk bv) { val sameId = allNotes.filter [ bearbeitungsvermerk?.identitaet?.wert == bv.identitaet?.wert ] @@ -506,19 +530,6 @@ class TableExtensions { return sameId.firstOrNull } - static def FootnoteInfo getFootnoteInfo(EObject tableContent, Footnote fn) { - return getFootnoteInfo(tableContent, fn.bearbeitungsvermerk) - } - - static def FootnoteInfo getFootnoteInfo(EObject tableContent, - Bearbeitungsvermerk bv) { - var object = tableContent - while (!(object instanceof Table)) { - object = object.eContainer - } - return getFootnoteInfo(object as Table, bv) - } - static def boolean isTableEmpty(Table table) { return table.tableRows.nullOrEmpty }