blob: b2113df5b6ee4c31e4e64c1c54435f78e6a53e8f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2013, 2020 Dirk Fauth and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Dirk Fauth <dirk.fauth@googlemail.com> - initial API and implementation
*******************************************************************************/
package org.eclipse.nebula.widgets.nattable.layer.event;
import java.util.ArrayList;
import java.util.Collection;
import org.eclipse.collections.api.collection.primitive.MutableIntCollection;
import org.eclipse.collections.api.list.primitive.MutableIntList;
import org.eclipse.collections.impl.factory.primitive.IntLists;
import org.eclipse.nebula.widgets.nattable.coordinate.Range;
import org.eclipse.nebula.widgets.nattable.layer.ILayer;
import org.eclipse.nebula.widgets.nattable.layer.event.StructuralDiff.DiffTypeEnum;
/**
* Helper class providing support for modifying cached index lists for
* IStructuralChangeEvents.
*/
public class StructuralChangeEventHelper {
/**
* Will check for events that indicate that rows has been deleted. In that
* case the given cached indexes for the given layer need to be updated
* because the index of the rows might have changed. E.g. Row with index 3
* is hidden in the given layer, deleting row at index 1 will cause the row
* at index 3 to be moved at index 2. Without transforming the index
* regarding the delete event, the wrong row would be hidden.
*
* @param rowDiffs
* The collection of {@link StructuralDiff}s to handle
* @param underlyingLayer
* The underlying layer of the layer who caches the indexes.
* Needed to translate the transported row positions to indexes,
* because the conversion to the layer who caches the index is
* done before it is fired further in the layer stack
* @param cachedRowIndexes
* The collection of indexes that is cached by the layer that
* needs transformation
* @param handleNotFound
* flag to tell whether the not found row indexes should be taken
* into account or not. Needed for last row checks
*/
public static void handleRowDelete(
Collection<StructuralDiff> rowDiffs, ILayer underlyingLayer,
Collection<Integer> cachedRowIndexes, boolean handleNotFound) {
// the number of all deleted rows that don't have a corresponding index
// anymore (last row cases)
int numberOfNoIndex = 0;
ArrayList<Integer> toRemove = new ArrayList<>();
for (StructuralDiff rowDiff : rowDiffs) {
if (rowDiff.getDiffType() != null
&& rowDiff.getDiffType().equals(DiffTypeEnum.DELETE)) {
Range beforePositionRange = rowDiff.getBeforePositionRange();
for (int i = beforePositionRange.start; i < beforePositionRange.end; i++) {
int index = i;// underlyingLayer.getRowIndexByPosition(i);
if (index >= 0) {
toRemove.add(index);
} else {
numberOfNoIndex++;
}
}
}
}
// remove the row indexes that are deleted
cachedRowIndexes.removeAll(toRemove);
// modify row indexes regarding the deleted rows
ArrayList<Integer> modifiedRows = new ArrayList<>();
for (Integer row : cachedRowIndexes) {
// check number of removed indexes that are lower than the current
// one
int deletedBefore = handleNotFound ? numberOfNoIndex : 0;
for (Integer removed : toRemove) {
if (removed < row) {
deletedBefore++;
}
}
int modRow = row - deletedBefore;
if (modRow >= 0) {
modifiedRows.add(modRow);
}
}
cachedRowIndexes.clear();
cachedRowIndexes.addAll(modifiedRows);
}
/**
* Will check for events that indicate that rows has been deleted. In that
* case the given cached indexes for the given layer need to be updated
* because the index of the rows might have changed. E.g. Row with index 3
* is hidden in the given layer, deleting row at index 1 will cause the row
* at index 3 to be moved at index 2. Without transforming the index
* regarding the delete event, the wrong row would be hidden.
*
* @param rowDiffs
* The collection of {@link StructuralDiff}s to handle
* @param underlyingLayer
* The underlying layer of the layer who caches the indexes.
* Needed to translate the transported row positions to indexes,
* because the conversion to the layer who caches the index is
* done before it is fired further in the layer stack
* @param cachedRowIndexes
* The collection of indexes that is cached by the layer that
* needs transformation
* @param handleNotFound
* flag to tell whether the not found row indexes should be taken
* into account or not. Needed for last row checks
*
* @since 2.0
*/
public static void handleRowDelete(
Collection<StructuralDiff> rowDiffs, ILayer underlyingLayer,
MutableIntCollection cachedRowIndexes, boolean handleNotFound) {
// the number of all deleted rows that don't have a corresponding index
// anymore (last row cases)
int numberOfNoIndex = 0;
MutableIntList toRemove = IntLists.mutable.empty();
for (StructuralDiff rowDiff : rowDiffs) {
if (rowDiff.getDiffType() != null
&& rowDiff.getDiffType().equals(DiffTypeEnum.DELETE)) {
Range beforePositionRange = rowDiff.getBeforePositionRange();
for (int i = beforePositionRange.start; i < beforePositionRange.end; i++) {
int index = i;// underlyingLayer.getRowIndexByPosition(i);
if (index >= 0) {
toRemove.add(index);
} else {
numberOfNoIndex++;
}
}
}
}
final int noIndexCount = numberOfNoIndex;
// remove the row indexes that are deleted
cachedRowIndexes.removeAll(toRemove);
// modify row indexes regarding the deleted rows
MutableIntList modifiedRows = IntLists.mutable.empty();
cachedRowIndexes.forEach(row -> {
// check number of removed indexes that are lower than the current
// one
int deletedBefore = handleNotFound ? noIndexCount : 0;
deletedBefore += toRemove.count(removed -> removed < row);
int modRow = row - deletedBefore;
if (modRow >= 0) {
modifiedRows.add(modRow);
}
});
cachedRowIndexes.clear();
cachedRowIndexes.addAll(modifiedRows);
}
/**
* Will check for events that indicate that rows are added. In that case the
* given cached indexes need to be updated because the index of the rows
* might have changed. E.g. Row with index 3 is hidden in the given layer,
* adding a row at index 1 will cause the row at index 3 to be moved to
* index 4. Without transforming the index regarding the add event, the
* wrong row would be hidden.
*
* @param rowDiffs
* The collection of {@link StructuralDiff}s to handle
* @param underlyingLayer
* The underlying layer of the layer who caches the indexes.
* Needed to translate the transported row positions to indexes,
* because the conversion to the layer who caches the index is
* done before it is fired further in the layer stack
* @param cachedRowIndexes
* The collection of indexes that is cached by the layer that
* needs transformation
* @param addToCache
* Flag to configure if the added value should be added to the
* cache or not. This is necessary to differ whether
* cachedRowIndexes are a collection of all indexes that need to
* be updated (e.g. row reordering) or just a collection of
* indexes that are applied for a specific state (e.g. row hide
* state)
*/
public static void handleRowInsert(
Collection<StructuralDiff> rowDiffs, ILayer underlyingLayer,
Collection<Integer> cachedRowIndexes, boolean addToCache) {
for (StructuralDiff rowDiff : rowDiffs) {
if (rowDiff.getDiffType() != null
&& rowDiff.getDiffType().equals(DiffTypeEnum.ADD)) {
Range beforePositionRange = rowDiff.getBeforePositionRange();
ArrayList<Integer> modifiedRows = new ArrayList<>();
int beforeIndex = underlyingLayer.getRowIndexByPosition(beforePositionRange.start);
for (Integer row : cachedRowIndexes) {
if (row >= beforeIndex) {
modifiedRows.add(row + 1);
} else {
modifiedRows.add(row);
}
}
if (addToCache) {
modifiedRows.add(beforeIndex, beforePositionRange.start);
}
cachedRowIndexes.clear();
cachedRowIndexes.addAll(modifiedRows);
}
}
}
/**
* Will check for events that indicate that rows are added. In that case the
* given cached indexes need to be updated because the index of the rows
* might have changed. E.g. Row with index 3 is hidden in the given layer,
* adding a row at index 1 will cause the row at index 3 to be moved to
* index 4. Without transforming the index regarding the add event, the
* wrong row would be hidden.
*
* @param rowDiffs
* The collection of {@link StructuralDiff}s to handle
* @param underlyingLayer
* The underlying layer of the layer who caches the indexes.
* Needed to translate the transported row positions to indexes,
* because the conversion to the layer who caches the index is
* done before it is fired further in the layer stack
* @param cachedRowIndexes
* The collection of indexes that is cached by the layer that
* needs transformation
* @param addToCache
* Flag to configure if the added value should be added to the
* cache or not. This is necessary to differ whether
* cachedRowIndexes are a collection of all indexes that need to
* be updated (e.g. row reordering) or just a collection of
* indexes that are applied for a specific state (e.g. row hide
* state)
* @since 2.0
*/
public static void handleRowInsert(
Collection<StructuralDiff> rowDiffs, ILayer underlyingLayer,
MutableIntCollection cachedRowIndexes, boolean addToCache) {
for (StructuralDiff rowDiff : rowDiffs) {
if (rowDiff.getDiffType() != null
&& rowDiff.getDiffType().equals(DiffTypeEnum.ADD)) {
Range beforePositionRange = rowDiff.getBeforePositionRange();
MutableIntList modifiedRows = IntLists.mutable.empty();
int beforeIndex = underlyingLayer.getRowIndexByPosition(beforePositionRange.start);
cachedRowIndexes.forEach(row -> {
if (row >= beforeIndex) {
modifiedRows.add(row + 1);
} else {
modifiedRows.add(row);
}
});
if (addToCache) {
modifiedRows.addAtIndex(beforeIndex, beforePositionRange.start);
}
cachedRowIndexes.clear();
cachedRowIndexes.addAll(modifiedRows);
}
}
}
/**
* Will check for events that indicate that columns has been deleted. In
* that case the given cached indexes for the given layer need to be updated
* because the index of the columns might have changed. E.g. Column with
* index 3 is hidden in the given layer, deleting column at index 1 will
* cause the column at index 3 to be moved at index 2. Without transforming
* the index regarding the delete event, the wrong column would be hidden.
*
* @param columnDiffs
* The collection of {@link StructuralDiff}s to handle
* @param underlyingLayer
* The underlying layer of the layer who caches the indexes.
* Needed to translate the transported column positions to
* indexes, because the conversion to the layer who caches the
* index is done before it is fired further in the layer stack
* @param cachedColumnIndexes
* The collection of indexes that is cached by the layer that
* needs transformation
* @param handleNotFound
* flag to tell whether the not found column indexes should be
* taken into account or not. Needed for last column checks
*/
public static void handleColumnDelete(
Collection<StructuralDiff> columnDiffs, ILayer underlyingLayer,
Collection<Integer> cachedColumnIndexes, boolean handleNotFound) {
// the number of all deleted columns that don't have a corresponding
// index anymore (last column cases)
int numberOfNoIndex = 0;
ArrayList<Integer> toRemove = new ArrayList<>();
for (StructuralDiff columnDiff : columnDiffs) {
if (columnDiff.getDiffType() != null
&& columnDiff.getDiffType().equals(DiffTypeEnum.DELETE)) {
Range beforePositionRange = columnDiff.getBeforePositionRange();
for (int i = beforePositionRange.start; i < beforePositionRange.end; i++) {
int index = i;// underlyingLayer.getColumnIndexByPosition(i);
if (index >= 0) {
toRemove.add(index);
} else {
numberOfNoIndex++;
}
}
}
}
// remove the column indexes that are deleted
cachedColumnIndexes.removeAll(toRemove);
// modify column indexes regarding the deleted columns
ArrayList<Integer> modifiedColumns = new ArrayList<>();
for (Integer column : cachedColumnIndexes) {
// check number of removed indexes that are lower than the current
// one
int deletedBefore = handleNotFound ? numberOfNoIndex : 0;
for (Integer removed : toRemove) {
if (removed < column) {
deletedBefore++;
}
}
int modColumn = column - deletedBefore;
if (modColumn >= 0) {
modifiedColumns.add(modColumn);
}
}
cachedColumnIndexes.clear();
cachedColumnIndexes.addAll(modifiedColumns);
}
/**
* Will check for events that indicate that columns has been deleted. In
* that case the given cached indexes for the given layer need to be updated
* because the index of the columns might have changed. E.g. Column with
* index 3 is hidden in the given layer, deleting column at index 1 will
* cause the column at index 3 to be moved at index 2. Without transforming
* the index regarding the delete event, the wrong column would be hidden.
*
* @param columnDiffs
* The collection of {@link StructuralDiff}s to handle
* @param underlyingLayer
* The underlying layer of the layer who caches the indexes.
* Needed to translate the transported column positions to
* indexes, because the conversion to the layer who caches the
* index is done before it is fired further in the layer stack
* @param cachedColumnIndexes
* The collection of indexes that is cached by the layer that
* needs transformation
* @param handleNotFound
* flag to tell whether the not found column indexes should be
* taken into account or not. Needed for last column checks.
* @since 2.0
*/
public static void handleColumnDelete(
Collection<StructuralDiff> columnDiffs, ILayer underlyingLayer,
MutableIntCollection cachedColumnIndexes, boolean handleNotFound) {
// the number of all deleted columns that don't have a corresponding
// index anymore (last column cases)
int numberOfNoIndex = 0;
MutableIntList toRemove = IntLists.mutable.empty();
for (StructuralDiff columnDiff : columnDiffs) {
if (columnDiff.getDiffType() != null
&& columnDiff.getDiffType().equals(DiffTypeEnum.DELETE)) {
Range beforePositionRange = columnDiff.getBeforePositionRange();
for (int i = beforePositionRange.start; i < beforePositionRange.end; i++) {
int index = i;// underlyingLayer.getColumnIndexByPosition(i);
if (index >= 0) {
toRemove.add(index);
} else {
numberOfNoIndex++;
}
}
}
}
final int noIndexCount = numberOfNoIndex;
// remove the column indexes that are deleted
cachedColumnIndexes.removeAll(toRemove);
// modify column indexes regarding the deleted columns
MutableIntList modifiedColumns = IntLists.mutable.empty();
cachedColumnIndexes.forEach(column -> {
// check number of removed indexes that are lower than the current
// one
int deletedBefore = handleNotFound ? noIndexCount : 0;
deletedBefore += toRemove.count(removed -> removed < column);
int modColumn = column - deletedBefore;
if (modColumn >= 0) {
modifiedColumns.add(modColumn);
}
});
cachedColumnIndexes.clear();
cachedColumnIndexes.addAll(modifiedColumns);
}
/**
* Will check for events that indicate that columns are added. In that case
* the given cached indexes need to be updated because the index of the
* columns might have changed. E.g. Column with index 3 is hidden in the
* given layer, adding a column at index 1 will cause the column at index 3
* to be moved to index 4. Without transforming the index regarding the add
* event, the wrong column would be hidden.
*
* @param columnDiffs
* The collection of {@link StructuralDiff}s to handle
* @param underlyingLayer
* The underlying layer of the layer who caches the indexes.
* Needed to translate the transported column positions to
* indexes, because the conversion to the layer who caches the
* index is done before it is fired further in the layer stack
* @param cachedColumnIndexes
* The collection of indexes that is cached by the layer that
* needs transformation
* @param addToCache
* Flag to configure if the added value should be added to the
* cache or not. This is necessary to differ whether
* cachedColumnIndexes are a collection of all indexes that need
* to be updated (e.g. column reordering) or just a collection of
* indexes that are applied for a specific state (e.g. column
* hide state)
*/
public static void handleColumnInsert(
Collection<StructuralDiff> columnDiffs, ILayer underlyingLayer,
Collection<Integer> cachedColumnIndexes, boolean addToCache) {
for (StructuralDiff columnDiff : columnDiffs) {
if (columnDiff.getDiffType() != null
&& columnDiff.getDiffType().equals(DiffTypeEnum.ADD)) {
Range beforePositionRange = columnDiff.getBeforePositionRange();
ArrayList<Integer> modifiedColumns = new ArrayList<>();
int beforeIndex = underlyingLayer.getColumnIndexByPosition(beforePositionRange.start);
for (Integer column : cachedColumnIndexes) {
if (column >= beforeIndex) {
modifiedColumns.add(column + 1);
} else {
modifiedColumns.add(column);
}
}
if (addToCache) {
modifiedColumns.add(beforeIndex, beforePositionRange.start);
}
cachedColumnIndexes.clear();
cachedColumnIndexes.addAll(modifiedColumns);
}
}
}
/**
* Will check for events that indicate that columns are added. In that case
* the given cached indexes need to be updated because the index of the
* columns might have changed. E.g. Column with index 3 is hidden in the
* given layer, adding a column at index 1 will cause the column at index 3
* to be moved to index 4. Without transforming the index regarding the add
* event, the wrong column would be hidden.
*
* @param columnDiffs
* The collection of {@link StructuralDiff}s to handle
* @param underlyingLayer
* The underlying layer of the layer who caches the indexes.
* Needed to translate the transported column positions to
* indexes, because the conversion to the layer who caches the
* index is done before it is fired further in the layer stack
* @param cachedColumnIndexes
* The collection of indexes that is cached by the layer that
* needs transformation
* @param addToCache
* Flag to configure if the added value should be added to the
* cache or not. This is necessary to differ whether
* cachedColumnIndexes are a collection of all indexes that need
* to be updated (e.g. column reordering) or just a collection of
* indexes that are applied for a specific state (e.g. column
* hide state)
* @since 2.0
*/
public static void handleColumnInsert(
Collection<StructuralDiff> columnDiffs, ILayer underlyingLayer,
MutableIntCollection cachedColumnIndexes, boolean addToCache) {
for (StructuralDiff columnDiff : columnDiffs) {
if (columnDiff.getDiffType() != null
&& columnDiff.getDiffType().equals(DiffTypeEnum.ADD)) {
Range beforePositionRange = columnDiff.getBeforePositionRange();
MutableIntList modifiedColumns = IntLists.mutable.empty();
int beforeIndex = underlyingLayer.getColumnIndexByPosition(beforePositionRange.start);
cachedColumnIndexes.forEach(column -> {
if (column >= beforeIndex) {
modifiedColumns.add(column + 1);
} else {
modifiedColumns.add(column);
}
});
if (addToCache) {
modifiedColumns.addAtIndex(beforeIndex, beforePositionRange.start);
}
cachedColumnIndexes.clear();
cachedColumnIndexes.addAll(modifiedColumns);
}
}
}
/**
* Method to indicate if the collection of StructuralDiffs marks a reorder
* event. This is necessary because reordering itself contains out of two
* diffs, one for deleting columns/rows, one for adding them at another
* position. As these diffs are completely separated from each other, but
* the handling does have impact, this check is added. Also on reordering
* there is no need for special handling of diffs in the layers that call
* this method.
* <p>
* Here is a small example to explain the impact on handling:
* <ul>
* <li>column at position 2 gets hidden</li>
* <li>reorder column at index 0 to index 4</li>
* <li>on handling the deletion of index 0 the hidden index 2 would be
* reduced to 1 (which is wrong because it is reordered not really
* deleted)</li>
* <li>adding the column at index 4 would undo the hidden index
* modification, because the insertion is handled separately and does not
* know about the former deletion</li>
* </ul>
*
* @param structuralDiffs
* The collection of StructuralDiffs to check for reordering
* @return <code>true</code> if the diff indicates a reordering happened,
* <code>false</code> if if was not a reordering based on the
* explanation above.
*/
public static boolean isReorder(Collection<StructuralDiff> structuralDiffs) {
// if there is a diff that deletes columns and a diff that adds the same
// amount of columns at once
// it seems to be a reordering and therefore it is not a real
// deletion/insertion so we don't need to handle
if (structuralDiffs != null && (structuralDiffs.size() % 2) == 0) {
int numberOfDeleteCols = 0;
int numberOfInsertCols = 0;
for (StructuralDiff columnDiff : structuralDiffs) {
if (columnDiff.getDiffType() != null
&& columnDiff.getDiffType().equals(DiffTypeEnum.DELETE)) {
numberOfDeleteCols = columnDiff.getBeforePositionRange().end
- columnDiff.getBeforePositionRange().start;
}
if (columnDiff.getDiffType() != null
&& columnDiff.getDiffType().equals(DiffTypeEnum.ADD)) {
numberOfInsertCols = columnDiff.getAfterPositionRange().end
- columnDiff.getAfterPositionRange().start;
}
}
if (numberOfDeleteCols == numberOfInsertCols) {
return true;
}
}
return false;
}
}