| /******************************************************************************* |
| * Copyright (c) 2012, 2020 Original authors 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: |
| * Original authors and others - initial API and implementation |
| * Thorsten Schlathölter <tschlat@gmx.de> - Bug 467047 |
| ******************************************************************************/ |
| package org.eclipse.nebula.widgets.nattable.search.strategy; |
| |
| import java.util.ArrayList; |
| import java.util.Comparator; |
| import java.util.List; |
| import java.util.regex.Pattern; |
| import java.util.regex.PatternSyntaxException; |
| |
| import org.eclipse.nebula.widgets.nattable.config.CellConfigAttributes; |
| import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry; |
| import org.eclipse.nebula.widgets.nattable.coordinate.PositionCoordinate; |
| import org.eclipse.nebula.widgets.nattable.data.convert.IDisplayConverter; |
| import org.eclipse.nebula.widgets.nattable.layer.ILayer; |
| import org.eclipse.nebula.widgets.nattable.layer.LabelStack; |
| import org.eclipse.nebula.widgets.nattable.layer.cell.ILayerCell; |
| import org.eclipse.nebula.widgets.nattable.search.strategy.GridSearchStrategy.GridRectangle; |
| import org.eclipse.nebula.widgets.nattable.style.DisplayMode; |
| |
| public final class CellDisplayValueSearchUtil { |
| |
| private CellDisplayValueSearchUtil() { |
| // private default constructor for helper class |
| } |
| |
| static List<PositionCoordinate> getCellCoordinates( |
| ILayer contextLayer, |
| int startingColumnPosition, |
| int startingRowPosition, |
| int width, |
| int height) { |
| List<PositionCoordinate> coordinates = new ArrayList<>(width * height); |
| for (int columnPosition = 0; columnPosition < width; columnPosition++) { |
| for (int rowPosition = 0; rowPosition < height; rowPosition++) { |
| PositionCoordinate coordinate = new PositionCoordinate( |
| contextLayer, |
| startingColumnPosition, |
| startingRowPosition++); |
| coordinates.add(coordinate); |
| } |
| startingColumnPosition++; |
| } |
| return coordinates; |
| } |
| |
| static List<PositionCoordinate> getDescendingCellCoordinates( |
| ILayer contextLayer, |
| int startingColumnPosition, |
| int startingRowPosition, |
| int width, |
| int height) { |
| List<PositionCoordinate> coordinates = new ArrayList<>(width * height); |
| for (int columnPosition = width; columnPosition >= 0 && startingColumnPosition >= 0; columnPosition--) { |
| for (int rowPosition = height; rowPosition >= 0 && startingRowPosition >= 0; rowPosition--) { |
| PositionCoordinate coordinate = new PositionCoordinate( |
| contextLayer, |
| startingColumnPosition, |
| startingRowPosition--); |
| coordinates.add(coordinate); |
| } |
| startingColumnPosition--; |
| } |
| return coordinates; |
| } |
| |
| static List<PositionCoordinate> getCellCoordinatesRowFirst( |
| ILayer contextLayer, |
| int startingColumnPosition, |
| int startingRowPosition, |
| int width, |
| int height) { |
| List<PositionCoordinate> coordinates = new ArrayList<>(width * height); |
| for (int rowPosition = 0; rowPosition < height; rowPosition++) { |
| for (int columnPosition = 0; columnPosition < width; columnPosition++) { |
| PositionCoordinate coordinate = new PositionCoordinate( |
| contextLayer, |
| startingColumnPosition++, |
| startingRowPosition); |
| coordinates.add(coordinate); |
| } |
| startingRowPosition++; |
| } |
| return coordinates; |
| } |
| |
| static List<PositionCoordinate> getDescendingCellCoordinatesRowFirst( |
| ILayer contextLayer, |
| int startingColumnPosition, |
| int startingRowPosition, |
| int width, |
| int height) { |
| List<PositionCoordinate> coordinates = new ArrayList<>(width * height); |
| for (int rowPosition = height; rowPosition >= 0 && startingRowPosition >= 0; rowPosition--) { |
| for (int columnPosition = width; columnPosition >= 0 && startingColumnPosition >= 0; columnPosition--) { |
| PositionCoordinate coordinate = new PositionCoordinate( |
| contextLayer, |
| startingColumnPosition--, |
| startingRowPosition); |
| coordinates.add(coordinate); |
| } |
| startingRowPosition--; |
| } |
| return coordinates; |
| } |
| |
| /** |
| * Finds the first matching cell in a list of cells. |
| * |
| * @param layer |
| * @param configRegistry |
| * @param cellsToSearch |
| * @param valueToMatch |
| * @param comparator |
| * @param caseSensitive |
| * @param wholeWord |
| * @param regex |
| * @param includeCollapsed |
| * TODO currently ignored |
| * @return |
| * @throws PatternSyntaxException |
| */ |
| static PositionCoordinate findCell( |
| final ILayer layer, |
| final IConfigRegistry configRegistry, |
| final PositionCoordinate[] cellsToSearch, |
| final Object valueToMatch, |
| final Comparator<String> comparator, |
| final boolean caseSensitive, |
| final boolean wholeWord, |
| final boolean regex, |
| final boolean includeCollapsed) { |
| String stringValue = caseSensitive ? valueToMatch.toString() : valueToMatch.toString().toLowerCase(); |
| Pattern pattern = regex ? Pattern.compile(stringValue) : null; |
| for (int cellIndex = 0; cellIndex < cellsToSearch.length; cellIndex++) { |
| final PositionCoordinate cellCoordinate = cellsToSearch[cellIndex]; |
| if (compare( |
| layer, |
| configRegistry, |
| pattern, |
| stringValue, |
| comparator, |
| caseSensitive, |
| wholeWord, |
| regex, |
| cellCoordinate.columnPosition, |
| cellCoordinate.rowPosition)) { |
| return cellCoordinate; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Finds the first matching cell in a list of grid cell rectangles. |
| * |
| * @param layer |
| * @param configRegistry |
| * @param cellRectangles |
| * @param valueToMatch |
| * @param comparator |
| * @param caseSensitive |
| * @param wholeWord |
| * @param regex |
| * @param includeCollapsed |
| * TODO currently ignored |
| * @return |
| * @throws PatternSyntaxException |
| */ |
| static PositionCoordinate findCell( |
| final ILayer layer, |
| final IConfigRegistry configRegistry, |
| final List<GridRectangle> cellRectangles, |
| final Object valueToMatch, |
| final Comparator<String> comparator, |
| final boolean caseSensitive, |
| final boolean wholeWord, |
| final boolean regex, |
| final boolean columnFirst, |
| final boolean includeCollapsed) { |
| String stringValue = caseSensitive ? valueToMatch.toString() : valueToMatch.toString().toLowerCase(); |
| Pattern pattern = regex ? Pattern.compile(stringValue) : null; |
| for (GridRectangle cellRectangle : cellRectangles) { |
| int direction = cellRectangle.firstDim.size() > 0 || cellRectangle.secondDim.size() > 0 ? 1 : -1; |
| for (int i = cellRectangle.firstDim.start; Math.abs(cellRectangle.firstDim.end - i) > 0; i += direction) { |
| PositionCoordinate result = findCell( |
| layer, |
| configRegistry, |
| i, |
| cellRectangle.secondDim.start, |
| cellRectangle.secondDim.end, |
| direction, |
| pattern, |
| stringValue, |
| comparator, |
| caseSensitive, |
| wholeWord, |
| regex, |
| columnFirst, |
| includeCollapsed); |
| if (result != null) { |
| return result; |
| } |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Finds the first matching cell in a table slice. |
| * |
| * @param layer |
| * @param configRegistry |
| * @param firstDimIndex |
| * @param secondDimStart |
| * @param secondDimEnd |
| * @param direction |
| * @param pattern |
| * @param stringValue |
| * @param comparator |
| * @param caseSensitive |
| * @param wholeWord |
| * @param regex |
| * @param columnFirst |
| * @param includeCollapsed |
| * @return |
| * @throws PatternSyntaxException |
| */ |
| private static PositionCoordinate findCell( |
| ILayer layer, |
| IConfigRegistry configRegistry, |
| int firstDimIndex, |
| int secondDimStart, |
| int secondDimEnd, |
| int direction, |
| Pattern pattern, |
| String stringValue, |
| Comparator<String> comparator, |
| boolean caseSensitive, |
| boolean wholeWord, |
| boolean regex, |
| final boolean columnFirst, |
| boolean includeCollapsed) { |
| |
| int columnPosition; |
| int rowPosition; |
| if (columnFirst) { |
| columnPosition = firstDimIndex; |
| rowPosition = secondDimStart; |
| } else { |
| columnPosition = secondDimStart; |
| rowPosition = firstDimIndex; |
| } |
| |
| for (int i = secondDimStart; direction * (secondDimEnd - i) > 0; i += direction) { |
| ILayerCell cellByPosition = layer.getCellByPosition(columnPosition, rowPosition); |
| |
| // on backwards search we only consider the origin position for |
| // spanned cells, otherwise the find order is different from the |
| // forward search order |
| if (!(direction < 0 && |
| (cellByPosition.getOriginColumnPosition() != cellByPosition.getColumnPosition() || |
| cellByPosition.getOriginRowPosition() != cellByPosition.getRowPosition()))) { |
| PositionCoordinate searchAnchor = getSearchAnchor(cellByPosition, direction); |
| |
| // If we do not hit the searchAnchor with our current position |
| // it means that we have hit a spanned cell somewhere else than |
| // in the top left (for direction == 1) or bottom right (for |
| // direction == -1). That in turn means that we have already |
| // visited that cell. Thus we skip the compare and proceed to |
| // the next position. |
| if (searchAnchor.columnPosition == columnPosition && searchAnchor.rowPosition == rowPosition) { |
| if (compare( |
| layer, |
| configRegistry, |
| pattern, |
| stringValue, |
| comparator, |
| caseSensitive, |
| wholeWord, |
| regex, |
| columnPosition, |
| rowPosition)) { |
| return new PositionCoordinate(layer, columnPosition, rowPosition); |
| } |
| } |
| } |
| |
| if (columnFirst) { |
| rowPosition += direction; |
| } else { |
| columnPosition += direction; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Get an anchor for the search of the given cell. |
| * |
| * @param cell |
| * @param direction |
| * @return |
| */ |
| private static PositionCoordinate getSearchAnchor(ILayerCell cell, int direction) { |
| return new PositionCoordinate(cell.getLayer(), cell.getOriginColumnPosition(), cell.getOriginRowPosition()); |
| } |
| |
| private static boolean compare( |
| ILayer layer, |
| IConfigRegistry configRegistry, |
| Pattern pattern, |
| String stringValue, |
| Comparator<String> comparator, |
| boolean caseSensitive, |
| boolean wholeWord, |
| boolean regex, |
| int columnPosition, |
| int rowPosition) { |
| |
| // Convert cell's data |
| LabelStack labels = layer.getConfigLabelsByPosition(columnPosition, rowPosition); |
| if (!labels.hasLabel(ISearchStrategy.SKIP_SEARCH_RESULT_LABEL)) { |
| final IDisplayConverter displayConverter = configRegistry.getConfigAttribute( |
| CellConfigAttributes.DISPLAY_CONVERTER, |
| DisplayMode.NORMAL, |
| labels); |
| Object dataValue = null; |
| if (displayConverter != null) { |
| ILayerCell cell = layer.getCellByPosition(columnPosition, rowPosition); |
| if (cell != null) { |
| dataValue = displayConverter.canonicalToDisplayValue(cell, configRegistry, cell.getDataValue()); |
| } |
| } |
| |
| // Compare with valueToMatch |
| if (dataValue instanceof Comparable<?>) { |
| String dataValueString = caseSensitive ? dataValue.toString() : dataValue.toString().toLowerCase(); |
| if (regex) { |
| if (pattern.matcher(dataValueString).matches()) { |
| return true; |
| } |
| } else if (comparator.compare(stringValue, dataValueString) == 0) { |
| return true; |
| } else if (!wholeWord && dataValueString.contains(stringValue)) { |
| return true; |
| } else if (wholeWord) { |
| // we also need to check single words in a multi word value |
| String[] split = dataValueString.split("\\b"); //$NON-NLS-1$ |
| for (String word : split) { |
| if (comparator.compare(stringValue, word) == 0) { |
| return true; |
| } |
| } |
| } |
| } |
| } |
| return false; |
| } |
| } |