diff --git a/org.eclipse.scout.rt.client.test/src/test/java/org/eclipse/scout/rt/client/ui/basic/table/HierarchicalTableTest.java b/org.eclipse.scout.rt.client.test/src/test/java/org/eclipse/scout/rt/client/ui/basic/table/HierarchicalTableTest.java index 445d8c51706..b89a3603572 100644 --- a/org.eclipse.scout.rt.client.test/src/test/java/org/eclipse/scout/rt/client/ui/basic/table/HierarchicalTableTest.java +++ b/org.eclipse.scout.rt.client.test/src/test/java/org/eclipse/scout/rt/client/ui/basic/table/HierarchicalTableTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2023 BSI Business Systems Integration AG + * Copyright (c) 2010, 2025 BSI Business Systems Integration AG * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 @@ -50,9 +50,8 @@ public void testCorrectRowOrderAfterAddRows() { } /** - * Table expect always to have all parent rows + * Table ignores rows with non-existing parent. */ - @Test(expected = IllegalArgumentException.class) public void testAddRowsWithInvalidRowList() { P_SinglePrimaryKeyColumnTable table = new P_SinglePrimaryKeyColumnTable(); table.init(); @@ -61,12 +60,14 @@ public void testAddRowsWithInvalidRowList() { rows.add(table.createRow(new Object[]{2, null})); rows.add(table.createRow(new Object[]{3, 4})); table.addRows(rows); + + assertEquals(3, table.getRowCount()); + assertEquals(2, table.getFilteredRowCount()); } /** - * Table expect always to have all parent rows + * Table ignores rows with non-existing parent. */ - @Test(expected = IllegalArgumentException.class) public void testAddRowWithUnresolvedParentRow() { P_SinglePrimaryKeyColumnTable table = new P_SinglePrimaryKeyColumnTable(); table.init(); @@ -74,8 +75,12 @@ public void testAddRowWithUnresolvedParentRow() { rows.add(table.createRow(new Object[]{1, null})); rows.add(table.createRow(new Object[]{2, null})); table.addRows(rows); + assertEquals(2, table.getRowCount()); + assertEquals(2, table.getFilteredRowCount()); table.addRow(table.createRow(new Object[]{3, 4})); + assertEquals(3, table.getRowCount()); + assertEquals(2, table.getFilteredRowCount()); } /** diff --git a/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/ui/basic/table/AbstractTable.java b/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/ui/basic/table/AbstractTable.java index 6f9f0cb5e07..43c5464d2ec 100644 --- a/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/ui/basic/table/AbstractTable.java +++ b/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/ui/basic/table/AbstractTable.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2023 BSI Business Systems Integration AG + * Copyright (c) 2010, 2025 BSI Business Systems Integration AG * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 @@ -649,10 +649,10 @@ protected CheckableStyle getConfiguredCheckableStyle() { * Subclasses can override this method. Default is {@link TriState#UNDEFINED} * * @return + *
  • {@link TriState#TRUE} if the tooltip should always be shown if the cell content is truncated
  • + *
  • {@link TriState#FALSE} if the tooltip should never be shown
  • + *
  • {@link TriState#UNDEFINED} cell tooltip is only shown if it is not possible to resize the column
  • + * */ @ConfigProperty(ConfigProperty.BOOLEAN) @Order(270) @@ -3250,13 +3250,18 @@ private void rebuildTreeStructure() { private void rebuildTreeStructureInternal() { List rootNodes = new ArrayList<>(); + Set orphanRows = new HashSet<>(); Map /*child rows*/> parentToChildren = new HashMap<>(); m_rows.forEach(row -> { List parentRowKeys = getParentRowKeys(row); if (parentRowKeys.stream().filter(Objects::nonNull).findAny().orElse(null) != null) { ITableRow parentRow = getRowByKey(parentRowKeys); if (parentRow == null) { - throw new IllegalArgumentException("Could not find the parent row of '" + row + "'. parent keys are defined."); + LOG.warn("Ignoring row with key {} because its parent key {} could not be found. {}", row.getKeyValues(), row.getParentKeyValues(), row); + row.setParentRowInternal(null); + rootNodes.add(row); + orphanRows.add(row); + return; } parentToChildren.computeIfAbsent(parentRow, children -> new ArrayList<>()) .add(row); @@ -3266,6 +3271,10 @@ private void rebuildTreeStructureInternal() { rootNodes.add(row); } }); + getRowFilters().stream().filter(f -> f instanceof P_OrphanRowFilter).forEach(this::removeRowFilter); + if (!orphanRows.isEmpty()) { + addRowFilter(new P_OrphanRowFilter(orphanRows)); + } m_rootRows = Collections.synchronizedList(rootNodes); boolean hierarchical = !parentToChildren.isEmpty(); @@ -5117,6 +5126,32 @@ public IFormField getFormField() { } } + /** + * Filter that hides orphaned rows, i.e. rows that have a non-null {@link ITableRow#getParentKeyValues() parent key}, + * but no such parent row exists. This can happen for example due to a row limit. + */ + protected static class P_OrphanRowFilter implements ITableRowFilter { + + protected final Set m_orphanRows; + + /** + * @param orphanRows + * set of orphaned rows (rows with non-existing parent) that are to be filtered by this filter + */ + public P_OrphanRowFilter(Set orphanRows) { + m_orphanRows = Assertions.assertNotNull(orphanRows); + } + + public Set getOrphanRows() { + return m_orphanRows; + } + + @Override + public boolean accept(ITableRow row) { + return !m_orphanRows.contains(row); + } + } + protected static class LocalTableExtension extends AbstractExtension
    implements ITableExtension
    { public LocalTableExtension(TABLE owner) {