From 9cf3ad8b40ae5eb44af1626c3a508a8e5dc5f0fe Mon Sep 17 00:00:00 2001 From: Marius Heine Date: Thu, 2 Jul 2026 13:00:59 +0200 Subject: [PATCH 01/11] detect and display changes in footnotes between main and compare planning --- .../table/ToolboxTableFootnoteView.java | 14 +- .../META-INF/MANIFEST.MF | 8 +- .../extensions/TableExtensionsTest.java | 454 ++++++++++++++++++ .../extensions/TableExtensions.xtend | 136 ++++-- 4 files changed, 578 insertions(+), 34 deletions(-) create mode 100644 java/bundles/org.eclipse.set.model.tablemodel.extensions.test/src/org/eclipse/set/model/tablemodel/extensions/TableExtensionsTest.java diff --git a/java/bundles/org.eclipse.set.feature.table/src/org/eclipse/set/feature/table/ToolboxTableFootnoteView.java b/java/bundles/org.eclipse.set.feature.table/src/org/eclipse/set/feature/table/ToolboxTableFootnoteView.java index 9e388bb1c9..0400f20f01 100644 --- a/java/bundles/org.eclipse.set.feature.table/src/org/eclipse/set/feature/table/ToolboxTableFootnoteView.java +++ b/java/bundles/org.eclipse.set.feature.table/src/org/eclipse/set/feature/table/ToolboxTableFootnoteView.java @@ -80,22 +80,26 @@ public void updateFootnotes(final Table table) { final String text = footnote.toReferenceText(); lines.add(text); + final StyleRange styleRange = new StyleRange(startOffset, + text.length(), null, null); switch (footnote.type) { case NEW_FOOTNOTE: - styles.add(new StyleRange(startOffset, text.length(), - new Color(255, 0, 0), null)); + styleRange.foreground = new Color(255, 0, 0); break; case OLD_FOOTNOTE: - final StyleRange styleRange = new StyleRange(startOffset, - text.length(), null, new Color(255, 255, 0)); + styleRange.background = new Color(255, 255, 0); styleRange.strikeout = true; - styles.add(styleRange); break; case COMMON_FOOTNOTE: default: break; } + if (footnote.changedInCompare) { + styleRange.borderStyle = SWT.BORDER_SOLID; + styleRange.borderColor = new Color(0, 0, 255) + } + styles.add(styleRange); startOffset += text.length() + 1; } diff --git a/java/bundles/org.eclipse.set.model.tablemodel.extensions.test/META-INF/MANIFEST.MF b/java/bundles/org.eclipse.set.model.tablemodel.extensions.test/META-INF/MANIFEST.MF index 089e46e48c..d6e4c9c62d 100644 --- a/java/bundles/org.eclipse.set.model.tablemodel.extensions.test/META-INF/MANIFEST.MF +++ b/java/bundles/org.eclipse.set.model.tablemodel.extensions.test/META-INF/MANIFEST.MF @@ -5,8 +5,12 @@ Bundle-SymbolicName: org.eclipse.set.model.tablemodel.extensions.test Bundle-Version: 2.7.0.qualifier Fragment-Host: org.eclipse.set.model.tablemodel.extensions;bundle-version="0.0.0" Bundle-RequiredExecutionEnvironment: JavaSE-21 -Require-Bundle: org.hamcrest.core -Import-Package: org.junit.jupiter.api, +Require-Bundle: org.hamcrest.core, + org.mockito.mockito-core +Import-Package: net.bytebuddy, + net.bytebuddy.agent, + org.eclipse.emf.ecore, + org.junit.jupiter.api, org.junit.jupiter.api.function, org.junit.jupiter.params, org.junit.jupiter.params.provider diff --git a/java/bundles/org.eclipse.set.model.tablemodel.extensions.test/src/org/eclipse/set/model/tablemodel/extensions/TableExtensionsTest.java b/java/bundles/org.eclipse.set.model.tablemodel.extensions.test/src/org/eclipse/set/model/tablemodel/extensions/TableExtensionsTest.java new file mode 100644 index 0000000000..c763bcacce --- /dev/null +++ b/java/bundles/org.eclipse.set.model.tablemodel.extensions.test/src/org/eclipse/set/model/tablemodel/extensions/TableExtensionsTest.java @@ -0,0 +1,454 @@ +/** + * Copyright (c) 2017 DB Netz AG and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v20.html + */ +package org.eclipse.set.model.tablemodel.extensions; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.collection.IsIterableWithSize.iterableWithSize; +import static org.hamcrest.core.Is.is; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.stream.Collectors; + +import org.eclipse.emf.common.util.TreeIterator; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.set.model.planpro.Basisobjekte.BasisobjekteFactory; +import org.eclipse.set.model.planpro.Basisobjekte.Bearbeitungsvermerk; +import org.eclipse.set.model.tablemodel.CompareFootnoteContainer; +import org.eclipse.set.model.tablemodel.CompareTableFootnoteContainer; +import org.eclipse.set.model.tablemodel.Footnote; +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.TablemodelFactory; +import org.eclipse.set.model.tablemodel.extensions.TableExtensions.FootnoteInfo; +import org.eclipse.set.model.tablemodel.extensions.TableExtensions.FootnoteType; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +/** + * Tests for {@link TableExtensions}. + * + * @author Heine + */ +@SuppressWarnings("nls") +public class TableExtensionsTest { + @Nested + @DisplayName("Special cases") + class SpecialCases { + @Test + @DisplayName("No footnote containers available") + public void testGetAllFootnotesEmpty() { + final Table table = setupTable(Collections.emptyList()); + + final Iterable footnotes = TableExtensions + .getAllFootnotes(table); + + assertThat(footnotes, is(iterableWithSize(0))); + } + + @Test + @DisplayName("Footnote with no Bearbeitungsvermerk") + public void testGetAllFootnotesWithOneEmptyCommonFootnote() { + final Table table = setupTable(Arrays + .asList(createSimpleFootnoteContainer(createFootnote()))); + + final Iterable footnotes = TableExtensions + .getAllFootnotes(table); + + assertThat(footnotes, is(iterableWithSize(1))); + + final FootnoteInfo info = footnotes.iterator().next(); + assertFootnoteInfo(info, null, 1, FootnoteType.COMMON_FOOTNOTE); + } + } + + @Nested + @DisplayName("SimpleFootnoteContainer") + class SimpleFootnoteContainerTests { + @Test + @DisplayName("One single footnote container") + public void testGetAllFootnotesWithOneCommonFootnote() { + final Bearbeitungsvermerk bv = createBearbeitungsvermerkt("bv-1", + "bv 1"); + final Table table = setupTable(Arrays + .asList(createSimpleFootnoteContainer(createFootnote(bv)))); + + final Iterable footnotes = TableExtensions + .getAllFootnotes(table); + + assertThat(footnotes, is(iterableWithSize(1))); + + final FootnoteInfo info = footnotes.iterator().next(); + assertFootnoteInfo(info, bv, 1, FootnoteType.COMMON_FOOTNOTE); + } + } + + @Nested + @DisplayName("CompareFootnoteContainer") + class CompareFootenoteContainerTests { + @Test + @DisplayName("One single footnote container with unchanged, new and old footnote") + public void testGetAllFootnotesWithCompareFootnotes() { + final Bearbeitungsvermerk bv1 = createBearbeitungsvermerkt("bv-1", + "bv 1"); + final Bearbeitungsvermerk bv2 = createBearbeitungsvermerkt("bv-2", + "bv 2"); + final Bearbeitungsvermerk bv3 = createBearbeitungsvermerkt("bv-3", + "bv 3"); + final Table table = setupTable( + Arrays.asList(createCompareFootnoteContainer( + createSimpleFootnoteContainer(createFootnote(bv1)), + createSimpleFootnoteContainer(createFootnote(bv2)), + createSimpleFootnoteContainer( + createFootnote(bv3))))); + + final Iterable footnotes = TableExtensions + .getAllFootnotes(table); + + assertThat(footnotes, is(iterableWithSize(3))); + + final Iterator infoIt = footnotes.iterator(); + assertFootnoteInfo(infoIt.next(), bv1, 1, + FootnoteType.COMMON_FOOTNOTE); + assertFootnoteInfo(infoIt.next(), bv2, 2, + FootnoteType.NEW_FOOTNOTE); + assertFootnoteInfo(infoIt.next(), bv3, 3, + FootnoteType.OLD_FOOTNOTE); + } + } + + @Nested + @DisplayName("CompareTableFootnoteContainer") + class CompareTableFootnoteContainerTests { + @Test + @DisplayName("simple compare planning with one removed and one new footnote") + public void testGetAllFootnotesWithCompareTableFootnotes() { + final Bearbeitungsvermerk bv1 = createBearbeitungsvermerkt("bv-1", + "bv 1"); + + final Bearbeitungsvermerk bv2 = createBearbeitungsvermerkt("bv-2", + "bv 2"); + final Bearbeitungsvermerk bv3 = createBearbeitungsvermerkt("bv-3", + "bv 3"); + final Table table = setupTable( + Arrays.asList(createCompareTableFootnoteContainer( + createSimpleFootnoteContainer(createFootnote(bv1), + createFootnote(bv2)), + createSimpleFootnoteContainer(createFootnote(bv1), + createFootnote(bv3))))); + + final Iterable footnotes = TableExtensions + .getAllFootnotes(table); + + assertThat(footnotes, is(iterableWithSize(2))); + + final Iterator infoIt = footnotes.iterator(); + assertFootnoteInfo(infoIt.next(), bv1, 1, + FootnoteType.COMMON_FOOTNOTE); + assertFootnoteInfo(infoIt.next(), bv2, 2, + FootnoteType.COMMON_FOOTNOTE, true); + } + } + + @Nested + @DisplayName("Mix of footnote container types") + class MixedTests { + @Test + @DisplayName("combined simple and compare footnote containers") + public void testGetAllFootnotesWithSimpleAndCompareFootnotes() { + final Bearbeitungsvermerk bv1 = createBearbeitungsvermerkt("bv-1", + "bv 1"); + + final Bearbeitungsvermerk bv2 = createBearbeitungsvermerkt("bv-2", + "bv 2"); + final Bearbeitungsvermerk bv3 = createBearbeitungsvermerkt("bv-3", + "bv 3"); + final Bearbeitungsvermerk bv4 = createBearbeitungsvermerkt("bv-4", + "bv 4"); + final Table table = setupTable(Arrays.asList( + createSimpleFootnoteContainer(createFootnote(bv1)), + createCompareFootnoteContainer( + createSimpleFootnoteContainer(createFootnote(bv2)), + createSimpleFootnoteContainer(createFootnote(bv3)), + createSimpleFootnoteContainer( + createFootnote(bv4))))); + + final Iterable footnotes = TableExtensions + .getAllFootnotes(table); + + assertThat(footnotes, is(iterableWithSize(4))); + + final Iterator infoIt = footnotes.iterator(); + assertFootnoteInfo(infoIt.next(), bv1, 1, + FootnoteType.COMMON_FOOTNOTE); + assertFootnoteInfo(infoIt.next(), bv2, 2, + FootnoteType.COMMON_FOOTNOTE); + assertFootnoteInfo(infoIt.next(), bv3, 3, + FootnoteType.NEW_FOOTNOTE); + assertFootnoteInfo(infoIt.next(), bv4, 4, + FootnoteType.OLD_FOOTNOTE); + } + + @Test + @DisplayName("footnote removed in one container of compare planning but still used in another") + public void testGetAllFootnotesWithCompareTableFootnotesRemovedInOneRow() { + final Bearbeitungsvermerk bv1 = createBearbeitungsvermerkt("bv-1", + "bv 1"); + + final Bearbeitungsvermerk bv2 = createBearbeitungsvermerkt("bv-2", + "bv 2"); + final Bearbeitungsvermerk bv3 = createBearbeitungsvermerkt("bv-3", + "bv 3"); + final Table table = setupTable(Arrays.asList( + createSimpleFootnoteContainer(createFootnote(bv3)), + createCompareTableFootnoteContainer( + createSimpleFootnoteContainer(createFootnote(bv1), + createFootnote(bv2)), + createSimpleFootnoteContainer(createFootnote(bv1), + createFootnote(bv3))))); + + final Iterable footnotes = TableExtensions + .getAllFootnotes(table); + + assertThat(footnotes, is(iterableWithSize(3))); + + final Iterator infoIt = footnotes.iterator(); + assertFootnoteInfo(infoIt.next(), bv1, 1, + FootnoteType.COMMON_FOOTNOTE); + assertFootnoteInfo(infoIt.next(), bv2, 2, + FootnoteType.COMMON_FOOTNOTE, true); + assertFootnoteInfo(infoIt.next(), bv3, 3, + FootnoteType.COMMON_FOOTNOTE); + } + + @Test + @DisplayName("footnote not changed in compare planning") + public void testGetAllFootnotesWithCompareTableFootnotes() { + final Bearbeitungsvermerk bv1 = createBearbeitungsvermerkt("bv-1", + "bv 1"); + + final Table table = setupTable(Arrays.asList( + createSimpleFootnoteContainer(createFootnote(bv1)), + createCompareTableFootnoteContainer( + createSimpleFootnoteContainer(), + createSimpleFootnoteContainer()))); + + final Iterable footnotes = TableExtensions + .getAllFootnotes(table); + + assertThat(footnotes, is(iterableWithSize(1))); + + final Iterator infoIt = footnotes.iterator(); + assertFootnoteInfo(infoIt.next(), bv1, 1, + FootnoteType.COMMON_FOOTNOTE); + } + + @Test + @DisplayName("footnotes with overlaps between start-/zielzustand and change in compare planning") + public void testGetAllFootnotesWithOverlapsBetweenCompareTableFootnotesAndCompareFootnotes() { + final Bearbeitungsvermerk bv1 = createBearbeitungsvermerkt("bv-1", + "bv 1"); + + final Bearbeitungsvermerk bv2 = createBearbeitungsvermerkt("bv-2", + "bv 2"); + final Table table = setupTable(Arrays.asList( + createCompareFootnoteContainer( + createSimpleFootnoteContainer(createFootnote(bv1)), + createSimpleFootnoteContainer(), + createSimpleFootnoteContainer()), + createCompareTableFootnoteContainer( + createSimpleFootnoteContainer(createFootnote(bv1), + createFootnote(bv2)), + createSimpleFootnoteContainer( + createFootnote(bv1))))); + + final Iterable footnotes = TableExtensions + .getAllFootnotes(table); + + assertThat(footnotes, is(iterableWithSize(2))); + + final Iterator infoIt = footnotes.iterator(); + assertFootnoteInfo(infoIt.next(), bv1, 1, + FootnoteType.COMMON_FOOTNOTE); + assertFootnoteInfo(infoIt.next(), bv2, 2, + FootnoteType.COMMON_FOOTNOTE, true); + } + } + + // ############## utility functions ############## + + private static List getFootnoteContainer( + final FootnoteContainer fnContainer) { + return switch (fnContainer) { + case final SimpleFootnoteContainer simple: + yield getFootnoteContainer(simple); + case final CompareFootnoteContainer compare: + yield getFootnoteContainer(compare); + case final CompareTableFootnoteContainer compareTable: + yield getFootnoteContainer(compareTable); + default: + yield Collections.emptyList(); + }; + } + + private static List getFootnoteContainer( + final SimpleFootnoteContainer fnContainer) { + return Arrays.asList(fnContainer); + } + + private static List getFootnoteContainer( + final CompareFootnoteContainer fnContainer) { + return Arrays.asList(fnContainer); + } + + private static List getFootnoteContainer( + final CompareTableFootnoteContainer fnContainer) { + final List fnContainers = new ArrayList<>(); + fnContainers.add(fnContainer); + if (fnContainer + .getMainPlanFootnoteContainer() instanceof CompareFootnoteContainer) { + fnContainers.addAll(getFootnoteContainer( + fnContainer.getMainPlanFootnoteContainer())); + } + if (fnContainer + .getComparePlanFootnoteContainer() instanceof CompareFootnoteContainer) { + fnContainers.addAll(getFootnoteContainer( + fnContainer.getComparePlanFootnoteContainer())); + } + return fnContainers; + } + + private static TreeIterator createTreeIterator( + final List footnoteContainers) { + final List allFootnoteContainers = footnoteContainers + .stream() + .flatMap(fC -> getFootnoteContainer(fC).stream()) + .collect(Collectors.toList()); + final Iterator iterator = allFootnoteContainers + .iterator(); + return new TreeIterator<>() { + @Override + public boolean hasNext() { + return iterator.hasNext(); + } + + @Override + public EObject next() { + return iterator.next(); + } + + @Override + public void prune() { + throw new UnsupportedOperationException("Not yet implemented"); + } + }; + } + + private static Bearbeitungsvermerk createBearbeitungsvermerkt( + final String identitaet, final String kommentar) { + final Bearbeitungsvermerk bv = BasisobjekteFactory.eINSTANCE + .createBearbeitungsvermerk(); + bv.setIdentitaet( + BasisobjekteFactory.eINSTANCE.createIdentitaet_TypeClass()); + bv.getIdentitaet().setWert(identitaet); + bv.setBearbeitungsvermerkAllg(BasisobjekteFactory.eINSTANCE + .createBearbeitungsvermerk_Allg_AttributeGroup()); + bv.getBearbeitungsvermerkAllg() + .setKommentar(BasisobjekteFactory.eINSTANCE + .createKommentar_TypeClass()); + bv.getBearbeitungsvermerkAllg().getKommentar().setWert(kommentar); + return bv; + } + + private static Footnote createFootnote() { + return createFootnote(null, null); + } + + private static Footnote createFootnote(final Bearbeitungsvermerk bv) { + return createFootnote(bv, null); + } + + private static Footnote createFootnote(final Bearbeitungsvermerk bv, + final String referenceColumn) { + final Footnote footnote = TablemodelFactory.eINSTANCE.createFootnote(); + footnote.setBearbeitungsvermerk(bv); + footnote.setReferenceColumn(referenceColumn); + return footnote; + } + + private static SimpleFootnoteContainer createSimpleFootnoteContainer( + final Footnote... footnotes) { + final SimpleFootnoteContainer fnContainer = TablemodelFactory.eINSTANCE + .createSimpleFootnoteContainer(); + fnContainer.getFootnotes().addAll(Arrays.asList(footnotes)); + return fnContainer; + } + + private static CompareFootnoteContainer createCompareFootnoteContainer( + final SimpleFootnoteContainer unchangedFootnotes, + final SimpleFootnoteContainer newFootnotes, + final SimpleFootnoteContainer oldFootnotes) { + final CompareFootnoteContainer fnContainer = TablemodelFactory.eINSTANCE + .createCompareFootnoteContainer(); + fnContainer.setUnchangedFootnotes(unchangedFootnotes); + fnContainer.setNewFootnotes(newFootnotes); + fnContainer.setOldFootnotes(oldFootnotes); + return fnContainer; + } + + private static CompareTableFootnoteContainer createCompareTableFootnoteContainer( + final FootnoteContainer mainPlanFootnotes, + final FootnoteContainer comparePlanFootnotes) { + final CompareTableFootnoteContainer fnContainer = TablemodelFactory.eINSTANCE + .createCompareTableFootnoteContainer(); + fnContainer.setMainPlanFootnoteContainer(mainPlanFootnotes); + fnContainer.setComparePlanFootnoteContainer(comparePlanFootnotes); + return fnContainer; + } + + private static Table setupTable(final List contents) { + final Table table = Mockito.mock(Table.class); + // need to register return 3 times as it is called 3 times in + // TableExtensions#getAllFootnotes + when(table.eAllContents()).thenReturn(createTreeIterator(contents)) + .thenReturn(createTreeIterator(contents)) + .thenReturn(createTreeIterator(contents)); + return table; + } + + private static void assertFootnoteInfo(final FootnoteInfo info, + final Bearbeitungsvermerk bv, final int index, + final FootnoteType type) { + assertFootnoteInfo(info, bv, index, type, false); + } + + private static void assertFootnoteInfo(final FootnoteInfo info, + final Bearbeitungsvermerk bv, final int index, + final FootnoteType type, final boolean changedInCompare) { + final String kommentar = bv == null ? "" + : bv.getBearbeitungsvermerkAllg().getKommentar().getWert(); + assertThat("Footnote has wrong Bearbeitungsvermerk", + info.bearbeitungsvermerk, is(bv)); + assertThat("Footnote has wrong type", info.type, is(type)); + assertThat("Footnote has wrong flag changedInCompare", + info.changedInCompare, is(changedInCompare)); + assertThat("Footnote has wrong shorthand", info.toShorthand(), + is("*" + index)); + assertThat("Footnote has wrong text", info.toText(), is(kommentar)); + assertThat("Footnote has wrong reference text", info.toReferenceText(), + is("*" + index + ": " + kommentar)); + } +} 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 d0c9e5981a..b13f3ec79b 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 @@ -419,14 +419,17 @@ class TableExtensions { } static class FootnoteInfo { - new(Footnote fn, FootnoteType ft) { - this(fn.bearbeitungsvermerk, ft, fn.referenceColumn) + new(Footnote fn, FootnoteType ft, boolean changedInCompare) { + this(fn.bearbeitungsvermerk, ft, fn.referenceColumn, + changedInCompare) } - new(Bearbeitungsvermerk bv, FootnoteType ft, String refCol) { + new(Bearbeitungsvermerk bv, FootnoteType ft, String refCol, + boolean changedInCompare) { this.bearbeitungsvermerk = bv this.type = ft this.referenceColumn = refCol + this.changedInCompare = changedInCompare } def String toShorthand() { @@ -442,43 +445,122 @@ class TableExtensions { »«bearbeitungsvermerk?.bearbeitungsvermerkAllg?.kommentar?.wert»''' } + def boolean sameBv(FootnoteInfo other) { + return toText == other.toText + } + + override String toString() { + return '''«toReferenceText» («type» #«bearbeitungsvermerk.identitaet.wert»)''' + } + public Bearbeitungsvermerk bearbeitungsvermerk; public int index; public FootnoteType type; - public String referenceColumn + public String referenceColumn; + public boolean changedInCompare; } - static def Iterable getAllFootnotes(Table table) { - val simpleFootnoteContainer = table.eAllContents.filter( - SimpleFootnoteContainer).toList - val compareFootnoteContainer = table.eAllContents.filter( - CompareFootnoteContainer).toList + static def Iterable getFootnoteInfos( + FootnoteContainer fnContainer) { + return switch (fnContainer) { + SimpleFootnoteContainer: fnContainer.footnoteInfos + CompareFootnoteContainer: fnContainer.footnoteInfos + default: #[] + } + } - val common = (simpleFootnoteContainer.map [ - footnotes.map[new FootnoteInfo(it, FootnoteType.COMMON_FOOTNOTE)] - ] + compareFootnoteContainer.map [ - unchangedFootnotes.footnotes.map [ - new FootnoteInfo(it, FootnoteType.COMMON_FOOTNOTE) - ] - ]).toList.flatten + static def Iterable getFootnoteInfos( + SimpleFootnoteContainer fnContainer) { + val common = fnContainer.footnotes.map [ + new FootnoteInfo(it, FootnoteType.COMMON_FOOTNOTE, false) + ].toList - val old = compareFootnoteContainer.map [ - oldFootnotes.footnotes.map [ - new FootnoteInfo(it, FootnoteType.OLD_FOOTNOTE) - ] - ].toList.flatten + return common.sortBy[toText] + } - val newF = compareFootnoteContainer.map [ + static def Iterable getFootnoteInfos( + CompareFootnoteContainer fnContainer) { - newFootnotes.footnotes.map [ - new FootnoteInfo(it, FootnoteType.NEW_FOOTNOTE) - ] - ].toList.flatten + val common = fnContainer.unchangedFootnotes.footnotes.map [ + new FootnoteInfo(it, FootnoteType.COMMON_FOOTNOTE, false) + ].toList + + val old = fnContainer.oldFootnotes.footnotes.map [ + new FootnoteInfo(it, FootnoteType.OLD_FOOTNOTE, false) + ].toList + + val newF = fnContainer.newFootnotes.footnotes.map [ + new FootnoteInfo(it, FootnoteType.NEW_FOOTNOTE, false) + ].toList // sort new and common together by text, then append old entries val footnotes = (common + newF).sortBy[toText] + old.sortBy[toText] + return footnotes + } + + static def Iterable getAllFootnotes(Table table) { + // collect all FootnoteContainer in table + val allSimpleFootnoteContainer = table.eAllContents.filter( + SimpleFootnoteContainer).toList + val allCompareFootnoteContainer = table.eAllContents.filter( + CompareFootnoteContainer).toList + val compareTableFootnoteContainer = table.eAllContents.filter( + CompareTableFootnoteContainer).toList + + val subCompareTableContainer = compareTableFootnoteContainer.map [ + #[mainPlanFootnoteContainer, comparePlanFootnoteContainer] + ].flatten; + + // retrieve only those CompareFootnoteContainer that are not used inside CompareTableFootnoteContainer + val nestedCompareFootnoteContainer = subCompareTableContainer.filter( + CompareFootnoteContainer).toList; + val compareFootnoteContainer = allCompareFootnoteContainer.filter [ + !nestedCompareFootnoteContainer.contains(it) + ] + + // retrieve only those SimpleFootnoteContainer that are not used inside any other FootnoteContainer + val nestedSimpleFootnoteContainer = (subCompareTableContainer.filter( + SimpleFootnoteContainer) + compareFootnoteContainer.map [ + #[newFootnotes, oldFootnotes, unchangedFootnotes] + ].flatten); + val simpleFootnoteContainer = allSimpleFootnoteContainer.filter [ + !nestedSimpleFootnoteContainer.contains(it) + ]; + + // collect and sort all footnotes and keep only unique ones of main planning + val normalFootnotes = (simpleFootnoteContainer + + compareFootnoteContainer).map [ + footnoteInfos + ].flatten.distinctBy[toText] + val mainTableFootnotes = compareTableFootnoteContainer.map [ + mainPlanFootnoteContainer.footnoteInfos + ].flatten.distinctBy[toText] + val unsortedFootnotes = normalFootnotes + mainTableFootnotes + val footnotes = (unsortedFootnotes.filter [ + type != FootnoteType.OLD_FOOTNOTE + ].sortBy[toText] + unsortedFootnotes.filter [ + type == FootnoteType.OLD_FOOTNOTE + ]).distinctBy[toText] + + // flag changes between main and compare table if necessary + if (compareTableFootnoteContainer.size > 0) { + val compareTableFootnotes = compareTableFootnoteContainer.map [ + comparePlanFootnoteContainer.footnoteInfos + ].flatten.distinctBy[toText] + + // a footnote only changed if it is not existing in any compare table container + // and was not used in some other (not changed) row + mainTableFootnotes.filter [ mF | + val compareFootnote = compareTableFootnotes.findFirst [ cF | + cF.sameBv(mF) + ] + val existsAsNormalFootnote = normalFootnotes. + empty ? false : normalFootnotes.exists[nF|nF.sameBv(mF)] + compareFootnote === null && !existsAsNormalFootnote + ].forEach[changedInCompare = true] + } - return footnotes.distinctBy[toText].indexed.map [ + return footnotes.indexed.map [ value.index = key + 1 return value ] From 07e8ea0087f9fde2ce94222d634b490eece8ed2c Mon Sep 17 00:00:00 2001 From: Marius Heine Date: Fri, 3 Jul 2026 10:29:18 +0200 Subject: [PATCH 02/11] fix missing semicolon --- .../org/eclipse/set/feature/table/ToolboxTableFootnoteView.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/bundles/org.eclipse.set.feature.table/src/org/eclipse/set/feature/table/ToolboxTableFootnoteView.java b/java/bundles/org.eclipse.set.feature.table/src/org/eclipse/set/feature/table/ToolboxTableFootnoteView.java index 0400f20f01..ea29c3cc58 100644 --- a/java/bundles/org.eclipse.set.feature.table/src/org/eclipse/set/feature/table/ToolboxTableFootnoteView.java +++ b/java/bundles/org.eclipse.set.feature.table/src/org/eclipse/set/feature/table/ToolboxTableFootnoteView.java @@ -97,7 +97,7 @@ public void updateFootnotes(final Table table) { } if (footnote.changedInCompare) { styleRange.borderStyle = SWT.BORDER_SOLID; - styleRange.borderColor = new Color(0, 0, 255) + styleRange.borderColor = new Color(0, 0, 255); } styles.add(styleRange); startOffset += text.length() + 1; From 3163645f4cb65f3a20f8536be2fe70562fa8ff8a Mon Sep 17 00:00:00 2001 From: Marius Heine Date: Mon, 6 Jul 2026 09:49:32 +0200 Subject: [PATCH 03/11] display removed footnotes as empty blue entries --- .../extensions/TableExtensionsTest.java | 25 ++++++++++- .../extensions/TableExtensions.xtend | 41 +++++++++++++++---- 2 files changed, 57 insertions(+), 9 deletions(-) diff --git a/java/bundles/org.eclipse.set.model.tablemodel.extensions.test/src/org/eclipse/set/model/tablemodel/extensions/TableExtensionsTest.java b/java/bundles/org.eclipse.set.model.tablemodel.extensions.test/src/org/eclipse/set/model/tablemodel/extensions/TableExtensionsTest.java index c763bcacce..1653fa87f1 100644 --- a/java/bundles/org.eclipse.set.model.tablemodel.extensions.test/src/org/eclipse/set/model/tablemodel/extensions/TableExtensionsTest.java +++ b/java/bundles/org.eclipse.set.model.tablemodel.extensions.test/src/org/eclipse/set/model/tablemodel/extensions/TableExtensionsTest.java @@ -11,6 +11,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.collection.IsIterableWithSize.iterableWithSize; import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsNull.nullValue; import static org.mockito.Mockito.when; import java.util.ArrayList; @@ -153,13 +154,14 @@ public void testGetAllFootnotesWithCompareTableFootnotes() { final Iterable footnotes = TableExtensions .getAllFootnotes(table); - assertThat(footnotes, is(iterableWithSize(2))); + assertThat(footnotes, is(iterableWithSize(3))); final Iterator infoIt = footnotes.iterator(); assertFootnoteInfo(infoIt.next(), bv1, 1, FootnoteType.COMMON_FOOTNOTE); assertFootnoteInfo(infoIt.next(), bv2, 2, FootnoteType.COMMON_FOOTNOTE, true); + assertRemovedCompareFootnoteInfo(infoIt.next(), 3); } } @@ -445,10 +447,31 @@ private static void assertFootnoteInfo(final FootnoteInfo info, assertThat("Footnote has wrong type", info.type, is(type)); assertThat("Footnote has wrong flag changedInCompare", info.changedInCompare, is(changedInCompare)); + assertThat("Footnote has wrong flag removedInMain", info.removedInMain, + is(false)); assertThat("Footnote has wrong shorthand", info.toShorthand(), is("*" + index)); assertThat("Footnote has wrong text", info.toText(), is(kommentar)); assertThat("Footnote has wrong reference text", info.toReferenceText(), is("*" + index + ": " + kommentar)); } + + private static final String REMOVED_FOOTNOTE_TEXT = " "; + + private static void assertRemovedCompareFootnoteInfo( + final FootnoteInfo info, final int index) { + assertThat("Footnote must have no Bearbeitungsvermerk", + info.bearbeitungsvermerk, is(nullValue())); + assertThat("Removed compare footnotes are handled as COMMON_FOOTNOTE", + info.type, is(FootnoteType.COMMON_FOOTNOTE)); + assertThat("Footnote has wrong flag changedInCompare", + info.changedInCompare, is(true)); + assertThat("Footnote has wrong flag removedInMain", info.removedInMain, + is(true)); + assertThat("Footnote has wrong shorthand", info.toShorthand(), + is("*" + index)); + assertThat("Footnote has wrong text", info.toText(), is("")); + assertThat("Footnote has wrong reference text", info.toReferenceText(), + is(REMOVED_FOOTNOTE_TEXT)); + } } 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 b13f3ec79b..a2d24966d4 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 @@ -419,17 +419,20 @@ class TableExtensions { } static class FootnoteInfo { + static val REMOVED_FOOTNOTE_TEXT = " " + new(Footnote fn, FootnoteType ft, boolean changedInCompare) { this(fn.bearbeitungsvermerk, ft, fn.referenceColumn, - changedInCompare) + changedInCompare, false) } new(Bearbeitungsvermerk bv, FootnoteType ft, String refCol, - boolean changedInCompare) { + boolean changedInCompare, boolean removedInMain) { this.bearbeitungsvermerk = bv this.type = ft this.referenceColumn = refCol this.changedInCompare = changedInCompare + this.removedInMain = removedInMain } def String toShorthand() { @@ -437,10 +440,16 @@ class TableExtensions { } def String toReferenceText() { + if (changedInCompare && removedInMain) { + return REMOVED_FOOTNOTE_TEXT + } return '''*«index»: «toText»''' } def String toText() { + if (changedInCompare && removedInMain) { + return "" + } return '''«IF referenceColumn !== null && !referenceColumn.isEmpty»«referenceColumn»: «ENDIF»« »«bearbeitungsvermerk?.bearbeitungsvermerkAllg?.kommentar?.wert»''' } @@ -458,6 +467,7 @@ class TableExtensions { public FootnoteType type; public String referenceColumn; public boolean changedInCompare; + public boolean removedInMain; } static def Iterable getFootnoteInfos( @@ -513,7 +523,7 @@ class TableExtensions { // retrieve only those CompareFootnoteContainer that are not used inside CompareTableFootnoteContainer val nestedCompareFootnoteContainer = subCompareTableContainer.filter( - CompareFootnoteContainer).toList; + CompareFootnoteContainer); val compareFootnoteContainer = allCompareFootnoteContainer.filter [ !nestedCompareFootnoteContainer.contains(it) ] @@ -531,12 +541,12 @@ class TableExtensions { val normalFootnotes = (simpleFootnoteContainer + compareFootnoteContainer).map [ footnoteInfos - ].flatten.distinctBy[toText] + ].flatten.distinctBy[toText].toList val mainTableFootnotes = compareTableFootnoteContainer.map [ mainPlanFootnoteContainer.footnoteInfos - ].flatten.distinctBy[toText] + ].flatten.distinctBy[toText].toList val unsortedFootnotes = normalFootnotes + mainTableFootnotes - val footnotes = (unsortedFootnotes.filter [ + var footnotes = (unsortedFootnotes.filter [ type != FootnoteType.OLD_FOOTNOTE ].sortBy[toText] + unsortedFootnotes.filter [ type == FootnoteType.OLD_FOOTNOTE @@ -546,7 +556,7 @@ class TableExtensions { if (compareTableFootnoteContainer.size > 0) { val compareTableFootnotes = compareTableFootnoteContainer.map [ comparePlanFootnoteContainer.footnoteInfos - ].flatten.distinctBy[toText] + ].flatten.distinctBy[toText].toList // a footnote only changed if it is not existing in any compare table container // and was not used in some other (not changed) row @@ -558,12 +568,27 @@ class TableExtensions { empty ? false : normalFootnotes.exists[nF|nF.sameBv(mF)] compareFootnote === null && !existsAsNormalFootnote ].forEach[changedInCompare = true] + + // a compare table footnote that is not existing in any table container + // is a removed footnote + val removedFootnotes = compareTableFootnotes.filter [ cF | + val compareFootnote = mainTableFootnotes.findFirst [ mF | + mF.sameBv(cF) + ] + val existsAsNormalFootnote = normalFootnotes. + empty ? false : normalFootnotes.exists[nF|nF.sameBv(cF)] + compareFootnote === null && !existsAsNormalFootnote + ].map [ + new FootnoteInfo(null, FootnoteType.COMMON_FOOTNOTE, null, true, + true) + ] + footnotes = footnotes + removedFootnotes } return footnotes.indexed.map [ value.index = key + 1 return value - ] + ].toList } static def FootnoteInfo getFootnoteInfo(Table table, Footnote fn) { From efad976fbcc2e8bb820c48f7d470d1efa2868e0b Mon Sep 17 00:00:00 2001 From: Marius Heine Date: Mon, 6 Jul 2026 10:09:10 +0200 Subject: [PATCH 04/11] add some orizontal padding to changed footnotes --- .../eclipse/set/feature/table/ToolboxTableFootnoteView.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/java/bundles/org.eclipse.set.feature.table/src/org/eclipse/set/feature/table/ToolboxTableFootnoteView.java b/java/bundles/org.eclipse.set.feature.table/src/org/eclipse/set/feature/table/ToolboxTableFootnoteView.java index ea29c3cc58..fe04790060 100644 --- a/java/bundles/org.eclipse.set.feature.table/src/org/eclipse/set/feature/table/ToolboxTableFootnoteView.java +++ b/java/bundles/org.eclipse.set.feature.table/src/org/eclipse/set/feature/table/ToolboxTableFootnoteView.java @@ -98,9 +98,12 @@ public void updateFootnotes(final Table table) { if (footnote.changedInCompare) { styleRange.borderStyle = SWT.BORDER_SOLID; styleRange.borderColor = new Color(0, 0, 255); + // add some artificial padding to left and right border of + lines.set(lines.size() - 1, " " + text + " "); //$NON-NLS-1$ //$NON-NLS-2$ + styleRange.length += 2; } styles.add(styleRange); - startOffset += text.length() + 1; + startOffset += styleRange.length + 1; } setVisible(!lines.isEmpty()); From e8b5b3d902075b3ae8ed0ab2ec64c12112f8f6f9 Mon Sep 17 00:00:00 2001 From: Marius Heine Date: Mon, 6 Jul 2026 10:18:55 +0200 Subject: [PATCH 05/11] use dispatch declaration --- .../tablemodel/extensions/TableExtensions.xtend | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) 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 a2d24966d4..5ae940833d 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 @@ -470,16 +470,11 @@ class TableExtensions { public boolean removedInMain; } - static def Iterable getFootnoteInfos( - FootnoteContainer fnContainer) { - return switch (fnContainer) { - SimpleFootnoteContainer: fnContainer.footnoteInfos - CompareFootnoteContainer: fnContainer.footnoteInfos - default: #[] - } + static def dispatch Iterable getFootnoteInfos(FootnoteContainer fnContainer) { + return #[] } - static def Iterable getFootnoteInfos( + static def dispatch Iterable getFootnoteInfos( SimpleFootnoteContainer fnContainer) { val common = fnContainer.footnotes.map [ new FootnoteInfo(it, FootnoteType.COMMON_FOOTNOTE, false) @@ -488,7 +483,7 @@ class TableExtensions { return common.sortBy[toText] } - static def Iterable getFootnoteInfos( + static def dispatch Iterable getFootnoteInfos( CompareFootnoteContainer fnContainer) { val common = fnContainer.unchangedFootnotes.footnotes.map [ From 0d38370ef3eca689511026a9db89bf9c1bb11a0b Mon Sep 17 00:00:00 2001 From: Marius Heine Date: Mon, 6 Jul 2026 10:19:02 +0200 Subject: [PATCH 06/11] fix pipeline build --- .../META-INF/MANIFEST.MF | 1 + 1 file changed, 1 insertion(+) diff --git a/java/bundles/org.eclipse.set.model.tablemodel.extensions.test/META-INF/MANIFEST.MF b/java/bundles/org.eclipse.set.model.tablemodel.extensions.test/META-INF/MANIFEST.MF index d6e4c9c62d..fc7f4698bd 100644 --- a/java/bundles/org.eclipse.set.model.tablemodel.extensions.test/META-INF/MANIFEST.MF +++ b/java/bundles/org.eclipse.set.model.tablemodel.extensions.test/META-INF/MANIFEST.MF @@ -6,6 +6,7 @@ Bundle-Version: 2.7.0.qualifier Fragment-Host: org.eclipse.set.model.tablemodel.extensions;bundle-version="0.0.0" Bundle-RequiredExecutionEnvironment: JavaSE-21 Require-Bundle: org.hamcrest.core, + org.hamcrest, org.mockito.mockito-core Import-Package: net.bytebuddy, net.bytebuddy.agent, From effe1fb2cf531ca61bc3f079536441daacac8e7f Mon Sep 17 00:00:00 2001 From: Marius Heine Date: Tue, 7 Jul 2026 12:12:41 +0200 Subject: [PATCH 07/11] use leftPad instead of magic string --- .../set/model/tablemodel/extensions/TableExtensionsTest.java | 4 +++- .../META-INF/MANIFEST.MF | 3 ++- .../set/model/tablemodel/extensions/TableExtensions.xtend | 3 ++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/java/bundles/org.eclipse.set.model.tablemodel.extensions.test/src/org/eclipse/set/model/tablemodel/extensions/TableExtensionsTest.java b/java/bundles/org.eclipse.set.model.tablemodel.extensions.test/src/org/eclipse/set/model/tablemodel/extensions/TableExtensionsTest.java index 1653fa87f1..47ef1f8f85 100644 --- a/java/bundles/org.eclipse.set.model.tablemodel.extensions.test/src/org/eclipse/set/model/tablemodel/extensions/TableExtensionsTest.java +++ b/java/bundles/org.eclipse.set.model.tablemodel.extensions.test/src/org/eclipse/set/model/tablemodel/extensions/TableExtensionsTest.java @@ -21,6 +21,7 @@ import java.util.List; import java.util.stream.Collectors; +import org.apache.commons.lang3.StringUtils; import org.eclipse.emf.common.util.TreeIterator; import org.eclipse.emf.ecore.EObject; import org.eclipse.set.model.planpro.Basisobjekte.BasisobjekteFactory; @@ -456,7 +457,8 @@ private static void assertFootnoteInfo(final FootnoteInfo info, is("*" + index + ": " + kommentar)); } - private static final String REMOVED_FOOTNOTE_TEXT = " "; + private static final String REMOVED_FOOTNOTE_TEXT = StringUtils.leftPad("", + 40); private static void assertRemovedCompareFootnoteInfo( final FootnoteInfo info, final int index) { diff --git a/java/bundles/org.eclipse.set.model.tablemodel.extensions/META-INF/MANIFEST.MF b/java/bundles/org.eclipse.set.model.tablemodel.extensions/META-INF/MANIFEST.MF index b0b21bf6f8..72584ed24c 100644 --- a/java/bundles/org.eclipse.set.model.tablemodel.extensions/META-INF/MANIFEST.MF +++ b/java/bundles/org.eclipse.set.model.tablemodel.extensions/META-INF/MANIFEST.MF @@ -25,4 +25,5 @@ Import-Package: org.eclipse.emf.common.util, org.eclipse.set.ppmodel.extensions, org.eclipse.set.ppmodel.extensions.utils, org.eclipse.set.utils, - org.eclipse.set.utils.math + org.eclipse.set.utils.math, + org.apache.commons.lang3 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 5ae940833d..8fb900d572 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 @@ -42,6 +42,7 @@ 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 +import org.apache.commons.lang3.StringUtils; /** * Extensions for {@link Table}. @@ -419,7 +420,7 @@ class TableExtensions { } static class FootnoteInfo { - static val REMOVED_FOOTNOTE_TEXT = " " + static val REMOVED_FOOTNOTE_TEXT = StringUtils.leftPad("", 40) new(Footnote fn, FootnoteType ft, boolean changedInCompare) { this(fn.bearbeitungsvermerk, ft, fn.referenceColumn, From c364c41738d3ecbb0398f527c48686a998fb8afa Mon Sep 17 00:00:00 2001 From: Marius Heine Date: Tue, 7 Jul 2026 12:17:19 +0200 Subject: [PATCH 08/11] simplify filtering for nested footnote containers --- .../extensions/TableExtensions.xtend | 38 ++++++------------- 1 file changed, 12 insertions(+), 26 deletions(-) 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 8fb900d572..cf8be06951 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 @@ -421,7 +421,7 @@ class TableExtensions { static class FootnoteInfo { static val REMOVED_FOOTNOTE_TEXT = StringUtils.leftPad("", 40) - + new(Footnote fn, FootnoteType ft, boolean changedInCompare) { this(fn.bearbeitungsvermerk, ft, fn.referenceColumn, changedInCompare, false) @@ -471,7 +471,8 @@ class TableExtensions { public boolean removedInMain; } - static def dispatch Iterable getFootnoteInfos(FootnoteContainer fnContainer) { + static def dispatch Iterable getFootnoteInfos( + FootnoteContainer fnContainer) { return #[] } @@ -506,33 +507,18 @@ class TableExtensions { static def Iterable getAllFootnotes(Table table) { // collect all FootnoteContainer in table - val allSimpleFootnoteContainer = table.eAllContents.filter( - SimpleFootnoteContainer).toList - val allCompareFootnoteContainer = table.eAllContents.filter( - CompareFootnoteContainer).toList + val simpleFootnoteContainer = table.eAllContents.filter( + SimpleFootnoteContainer).filter [ + !(eContainer instanceof CompareFootnoteContainer) && + !(eContainer instanceof CompareTableFootnoteContainer) + ].toList + val compareFootnoteContainer = table.eAllContents.filter( + CompareFootnoteContainer).filter [ + !(eContainer instanceof CompareTableFootnoteContainer) + ].toList val compareTableFootnoteContainer = table.eAllContents.filter( CompareTableFootnoteContainer).toList - val subCompareTableContainer = compareTableFootnoteContainer.map [ - #[mainPlanFootnoteContainer, comparePlanFootnoteContainer] - ].flatten; - - // retrieve only those CompareFootnoteContainer that are not used inside CompareTableFootnoteContainer - val nestedCompareFootnoteContainer = subCompareTableContainer.filter( - CompareFootnoteContainer); - val compareFootnoteContainer = allCompareFootnoteContainer.filter [ - !nestedCompareFootnoteContainer.contains(it) - ] - - // retrieve only those SimpleFootnoteContainer that are not used inside any other FootnoteContainer - val nestedSimpleFootnoteContainer = (subCompareTableContainer.filter( - SimpleFootnoteContainer) + compareFootnoteContainer.map [ - #[newFootnotes, oldFootnotes, unchangedFootnotes] - ].flatten); - val simpleFootnoteContainer = allSimpleFootnoteContainer.filter [ - !nestedSimpleFootnoteContainer.contains(it) - ]; - // collect and sort all footnotes and keep only unique ones of main planning val normalFootnotes = (simpleFootnoteContainer + compareFootnoteContainer).map [ From 15b39f0de4ccfc80b9a41d30ab7dcf6ce70982e9 Mon Sep 17 00:00:00 2001 From: Marius Heine Date: Tue, 7 Jul 2026 12:37:17 +0200 Subject: [PATCH 09/11] simplify footnote determination at all --- .../extensions/TableExtensions.xtend | 102 ++++++++++-------- 1 file changed, 57 insertions(+), 45 deletions(-) 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 cf8be06951..d7f2f78be3 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 @@ -516,56 +516,22 @@ class TableExtensions { CompareFootnoteContainer).filter [ !(eContainer instanceof CompareTableFootnoteContainer) ].toList - val compareTableFootnoteContainer = table.eAllContents.filter( - CompareTableFootnoteContainer).toList - // collect and sort all footnotes and keep only unique ones of main planning val normalFootnotes = (simpleFootnoteContainer + compareFootnoteContainer).map [ footnoteInfos ].flatten.distinctBy[toText].toList - val mainTableFootnotes = compareTableFootnoteContainer.map [ - mainPlanFootnoteContainer.footnoteInfos - ].flatten.distinctBy[toText].toList - val unsortedFootnotes = normalFootnotes + mainTableFootnotes - var footnotes = (unsortedFootnotes.filter [ - type != FootnoteType.OLD_FOOTNOTE - ].sortBy[toText] + unsortedFootnotes.filter [ - type == FootnoteType.OLD_FOOTNOTE - ]).distinctBy[toText] - - // flag changes between main and compare table if necessary - if (compareTableFootnoteContainer.size > 0) { - val compareTableFootnotes = compareTableFootnoteContainer.map [ - comparePlanFootnoteContainer.footnoteInfos - ].flatten.distinctBy[toText].toList - - // a footnote only changed if it is not existing in any compare table container - // and was not used in some other (not changed) row - mainTableFootnotes.filter [ mF | - val compareFootnote = compareTableFootnotes.findFirst [ cF | - cF.sameBv(mF) - ] - val existsAsNormalFootnote = normalFootnotes. - empty ? false : normalFootnotes.exists[nF|nF.sameBv(mF)] - compareFootnote === null && !existsAsNormalFootnote - ].forEach[changedInCompare = true] - - // a compare table footnote that is not existing in any table container - // is a removed footnote - val removedFootnotes = compareTableFootnotes.filter [ cF | - val compareFootnote = mainTableFootnotes.findFirst [ mF | - mF.sameBv(cF) - ] - val existsAsNormalFootnote = normalFootnotes. - empty ? false : normalFootnotes.exists[nF|nF.sameBv(cF)] - compareFootnote === null && !existsAsNormalFootnote - ].map [ - new FootnoteInfo(null, FootnoteType.COMMON_FOOTNOTE, null, true, - true) - ] - footnotes = footnotes + removedFootnotes - } + + val allFootnotes = normalFootnotes + + table.getCompareTableFootnotes(normalFootnotes) + + // collect and sort all footnotes and keep only unique ones + // except the ones removedInMain which should be appended at the end + var footnotes = (allFootnotes.filter [ + type != FootnoteType.OLD_FOOTNOTE && !removedInMain + ].sortBy[toText] + allFootnotes.filter [ + type == FootnoteType.OLD_FOOTNOTE && !removedInMain + ]).distinctBy[toText] + allFootnotes.filter[removedInMain] return footnotes.indexed.map [ value.index = key + 1 @@ -573,6 +539,52 @@ class TableExtensions { ].toList } + private static def getCompareTableFootnotes(Table table, + Iterable footnotes) { + val compareTableFootnoteContainer = table.eAllContents.filter( + CompareTableFootnoteContainer).toList + + if (compareTableFootnoteContainer.size === 0) { + return footnotes + } + + val mainTableFootnotes = compareTableFootnoteContainer.map [ + mainPlanFootnoteContainer.footnoteInfos + ].flatten.distinctBy[toText].toList + + val compareTableFootnotes = compareTableFootnoteContainer.map [ + comparePlanFootnoteContainer.footnoteInfos + ].flatten.distinctBy[toText].toList + + // a footnote only changed if it is not existing in any compare table container + // and was not used in some other (not changed) row + mainTableFootnotes.filter [ mF | + val compareFootnote = compareTableFootnotes.findFirst [ cF | + cF.sameBv(mF) + ] + val existsAsNormalFootnote = footnotes.empty + ? false + : footnotes.exists[nF|nF.sameBv(mF)] + compareFootnote === null && !existsAsNormalFootnote + ].forEach[changedInCompare = true] + + // a compare table footnote that is not existing in any table container + // is a removed footnote + val removedFootnotes = compareTableFootnotes.filter [ cF | + val compareFootnote = mainTableFootnotes.findFirst [ mF | + mF.sameBv(cF) + ] + val existsAsNormalFootnote = footnotes.empty + ? false + : footnotes.exists[nF|nF.sameBv(cF)] + compareFootnote === null && !existsAsNormalFootnote + ].map [ + new FootnoteInfo(null, FootnoteType.COMMON_FOOTNOTE, null, true, + true) + ] + return mainTableFootnotes + removedFootnotes + } + static def FootnoteInfo getFootnoteInfo(Table table, Footnote fn) { return table.getFootnoteInfo(fn.bearbeitungsvermerk) } From 4e0a0dec1d6e9716aa45635536398af8a5475df0 Mon Sep 17 00:00:00 2001 From: Marius Heine Date: Tue, 7 Jul 2026 12:58:29 +0200 Subject: [PATCH 10/11] fix return value --- .../set/model/tablemodel/extensions/TableExtensions.xtend | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 d7f2f78be3..24eb86acae 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 @@ -545,7 +545,7 @@ class TableExtensions { CompareTableFootnoteContainer).toList if (compareTableFootnoteContainer.size === 0) { - return footnotes + return #[] } val mainTableFootnotes = compareTableFootnoteContainer.map [ From e02b928a677007c45d7966d15aca3dd584e5d1ae Mon Sep 17 00:00:00 2001 From: Marius Heine Date: Wed, 8 Jul 2026 12:27:55 +0200 Subject: [PATCH 11/11] add blue border around footnotes in pdf export --- .../export/pdf/TableToTableDocument.xtend | 18 +++++-- .../rootdir/data/export/pdf/common.xsl | 50 +++++++++++-------- 2 files changed, 44 insertions(+), 24 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 68519ecfa3..16d17066ff 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 @@ -602,16 +602,28 @@ class TableToTableDocument { private def Element transform(FootnoteInfo footnote) { val it = doc.createElement("Footnote") - val footNoteType = doc.createElement(footnote.type.toString) - footNoteType.attributeNode = createFootnoteAttribute(footnote.index) + val footNoteType = doc.createElement( + footnote.changedInCompare && + footnote.removedInMain ? 'REMOVED_COMPARE_FOOTNOTE' : footnote. + type.toString) + footNoteType.attributeNode = createFootnoteNumberAttribute( + footnote.index) + if (footnote.changedInCompare) { + it.attributeNode = createFootnoteCompareAttribute() + } footNoteType.textContent = footnote.toText appendChild(footNoteType) return it } - private def Attr createFootnoteAttribute(Integer number) { + private def Attr createFootnoteNumberAttribute(Integer number) { val footnoteAttr = doc.createAttribute("footnote-number") footnoteAttr.value = Integer.toString(number) return footnoteAttr } + + private def Attr createFootnoteCompareAttribute() { + val footnoteAttr = doc.createAttribute("footnote-changed-in-compare") + return footnoteAttr + } } diff --git a/java/bundles/org.eclipse.set.feature/rootdir/data/export/pdf/common.xsl b/java/bundles/org.eclipse.set.feature/rootdir/data/export/pdf/common.xsl index 5cde898e55..979b804684 100644 --- a/java/bundles/org.eclipse.set.feature/rootdir/data/export/pdf/common.xsl +++ b/java/bundles/org.eclipse.set.feature/rootdir/data/export/pdf/common.xsl @@ -268,37 +268,45 @@ http://www.eclipse.org/legal/epl-v20.html - + + + 0.5mm + 0.5mm + + + + 0.5mm + + + - + * + + : + + + + * : - - - - - - * - - : - - - + - - - * - - : - - - + + * + + : + + + + + +