From eab66c3d1e00b49e597b30d15344fc2ce8ba8e60 Mon Sep 17 00:00:00 2001 From: Dirk Fauth Date: Fri, 22 Aug 2025 13:51:45 +0200 Subject: [PATCH] Impl #168 - Add alternative GroupByComparator Signed-off-by: Dirk Fauth --- ..._808_SortableGroupByWithFilterExample.java | 16 +- .../groupBy/SortModelGroupByComparator.java | 155 ++++++++++++++++++ 2 files changed, 166 insertions(+), 5 deletions(-) create mode 100644 org.eclipse.nebula.widgets.nattable.extension.glazedlists/src/org/eclipse/nebula/widgets/nattable/extension/glazedlists/groupBy/SortModelGroupByComparator.java diff --git a/org.eclipse.nebula.widgets.nattable.examples/src/org/eclipse/nebula/widgets/nattable/examples/_800_Integration/_808_SortableGroupByWithFilterExample.java b/org.eclipse.nebula.widgets.nattable.examples/src/org/eclipse/nebula/widgets/nattable/examples/_800_Integration/_808_SortableGroupByWithFilterExample.java index bd2080aee..1c0622dbd 100644 --- a/org.eclipse.nebula.widgets.nattable.examples/src/org/eclipse/nebula/widgets/nattable/examples/_800_Integration/_808_SortableGroupByWithFilterExample.java +++ b/org.eclipse.nebula.widgets.nattable.examples/src/org/eclipse/nebula/widgets/nattable/examples/_800_Integration/_808_SortableGroupByWithFilterExample.java @@ -46,6 +46,7 @@ import org.eclipse.nebula.widgets.nattable.extension.glazedlists.groupBy.GroupByHeaderLayer; import org.eclipse.nebula.widgets.nattable.extension.glazedlists.groupBy.GroupByHeaderMenuConfiguration; import org.eclipse.nebula.widgets.nattable.extension.glazedlists.groupBy.GroupByModel; +import org.eclipse.nebula.widgets.nattable.extension.glazedlists.groupBy.SortModelGroupByComparator; import org.eclipse.nebula.widgets.nattable.extension.glazedlists.groupBy.summary.IGroupBySummaryProvider; import org.eclipse.nebula.widgets.nattable.extension.glazedlists.groupBy.summary.SummationGroupBySummaryProvider; import org.eclipse.nebula.widgets.nattable.filterrow.FilterRowHeaderComposite; @@ -171,12 +172,16 @@ public Control createExampleControl(Composite parent) { columnHeaderDataLayer), false); - // connect sortModel to GroupByDataLayer to support sorting by group by + SortModelGroupByComparator groupByComparator = new SortModelGroupByComparator<>( + bodyLayerStack.getGroupByModel(), + columnPropertyAccessor, + bodyLayerStack.getBodyDataLayer()); + bodyLayerStack.getBodyDataLayer().setComparator(groupByComparator); + + // connect sortModel to SortModelGroupByComparator to support sorting by + // group by // summary values - bodyLayerStack.getBodyDataLayer().initializeTreeComparator( - sortHeaderLayer.getSortModel(), - bodyLayerStack.getTreeLayer(), - true); + groupByComparator.setSortModel(sortHeaderLayer.getSortModel()); // add the filter row functionality final FilterRowHeaderComposite filterRowHeaderLayer = @@ -462,6 +467,7 @@ public BodyLayerStack(List values, this.filterList, columnPropertyAccessor, configRegistry); + // get the IDataProvider that was created by the GroupByDataLayer this.bodyDataProvider = this.bodyDataLayer.getDataProvider(); diff --git a/org.eclipse.nebula.widgets.nattable.extension.glazedlists/src/org/eclipse/nebula/widgets/nattable/extension/glazedlists/groupBy/SortModelGroupByComparator.java b/org.eclipse.nebula.widgets.nattable.extension.glazedlists/src/org/eclipse/nebula/widgets/nattable/extension/glazedlists/groupBy/SortModelGroupByComparator.java new file mode 100644 index 000000000..d495f83fb --- /dev/null +++ b/org.eclipse.nebula.widgets.nattable.extension.glazedlists/src/org/eclipse/nebula/widgets/nattable/extension/glazedlists/groupBy/SortModelGroupByComparator.java @@ -0,0 +1,155 @@ +/******************************************************************************* + * Copyright (c) 2025 Dirk Fauth and others. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Dirk Fauth - initial API and implementation + ******************************************************************************/ +package org.eclipse.nebula.widgets.nattable.extension.glazedlists.groupBy; + +import java.util.Comparator; +import java.util.List; + +import org.eclipse.nebula.widgets.nattable.config.DefaultComparator; +import org.eclipse.nebula.widgets.nattable.data.IColumnAccessor; +import org.eclipse.nebula.widgets.nattable.layer.IUniqueIndexLayer; +import org.eclipse.nebula.widgets.nattable.sort.ISortModel; +import org.eclipse.nebula.widgets.nattable.sort.SortDirectionEnum; +import org.eclipse.nebula.widgets.nattable.tree.TreeLayer; + +/** + * Specialization of {@link GroupByComparator} that primarily uses the + * {@link ISortModel} to sort the list by values, and the {@link GroupByObject}s + * in second place. This changes the behavior when sorting a table with active + * groupby in that way, that the groupby tree structure could also update when + * sorting a column that is used for grouping. + * + * As the name suggests, this comparator requires the {@link ISortModel} to be + * set. This can be done via + * {@link GroupByDataLayer#initializeTreeComparator(ISortModel, IUniqueIndexLayer, boolean)} + * or directly via {@link #setSortModel(ISortModel)} as the {@link TreeLayer} is + * not needed by this implementation and the {@link GroupByDataLayer} needs to + * be passed already via constructor. + * + * @since 2.6 + */ +public class SortModelGroupByComparator extends GroupByComparator { + + private boolean sortBySummaryRows = true; + + /** + * + * @param groupByModel + * The {@link GroupByModel} necessary to retrieve information + * about the current groupBy state. + * @param columnAccessor + * The {@link IColumnAccessor} necessary to retrieve the column + * values of elements. + * @param dataLayer + * The {@link GroupByDataLayer} that should be used to retrieve + * groupBy summary values for sorting the tree structure. Can be + * null to avoid retrieving and inspecting summary + * values on sorting. + */ + public SortModelGroupByComparator(GroupByModel groupByModel, IColumnAccessor columnAccessor, GroupByDataLayer dataLayer) { + super(groupByModel, columnAccessor, dataLayer); + } + + @Override + public int compare(Object o1, Object o2) { + int result = 0; + + if (o1 instanceof GroupByObject && o2 instanceof GroupByObject) { + result = compareGroupByObjects((GroupByObject) o1, (GroupByObject) o2); + } else if (!(o1 instanceof GroupByObject) && !(o2 instanceof GroupByObject)) { + // already compared by the GroupByObjects + // so just return 0 + result = 0; + } else { + throw new IllegalStateException( + "Comparison of GroupByObjects with non-GroupByObjects is not supported: " + o1 + " vs. " + o2); //$NON-NLS-1$//$NON-NLS-2$ + } + + return result; + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + protected int compareGroupByObjects(GroupByObject g1, GroupByObject g2) { + List sortColumns = this.sortModel.getSortedColumnIndexes(); + int result = 0; + + for (int sortColumnIndex : sortColumns) { + // check if a comparison for summary rows is needed + result = compareSummaryRows(g1, g2, sortColumnIndex); + + // if there is no summary row comparison result, compare the values + // of the column + if (result == 0) { + Object o1 = g1.getDescriptor().get(sortColumnIndex); + Object o2 = g2.getDescriptor().get(sortColumnIndex); + result = getComparator(sortColumnIndex).compare(o1, o2); + } + + if (result != 0) { + if (SortDirectionEnum.DESC.equals(this.sortModel.getSortDirection(sortColumnIndex))) { + result *= -1; + } + break; + } + } + + // if there is no result after comparing all sorted columns, compare the + // values in the GroupByObjects + if (result == 0) { + Comparator comparator = DefaultComparator.getInstance(); + result = comparator.compare(g1.getValue(), g2.getValue()); + } + + return result; + } + + @SuppressWarnings("unchecked") + protected int compareSummaryRows(GroupByObject g1, GroupByObject g2, int columnIndex) { + if (!isSortBySummaryRows()) { + return 0; + } + + // group building columns are also interpreted as summary columns + // we do not want that here, so we just return 0 + if (this.groupByModel.getGroupByColumnIndexes().contains(columnIndex)) { + return 0; + } + + Boolean summaryColumn = isSummaryColumn(g1, columnIndex); + if (summaryColumn == null) { + // in case we were not able to retrieve the + // information for the one GroupByObject we + // try to find the information the other + // GroupByObject + summaryColumn = isSummaryColumn(g2, columnIndex); + } + + if (summaryColumn != null && summaryColumn) { + // compare GroupByObjects by summary value + Object sumValue1 = getSummaryValueFromCache(g1, columnIndex); + Object sumValue2 = getSummaryValueFromCache(g2, columnIndex); + return getComparator(columnIndex).compare(sumValue1, sumValue2); + } + + return 0; + } + + public boolean isSortBySummaryRows() { + return this.sortBySummaryRows; + } + + public void setSortBySummaryRows(boolean sortBySummaryRows) { + this.sortBySummaryRows = sortBySummaryRows; + } + +}