blob: ac123d52d5173c1908d299b2cd2e11b95a48de0d [file] [log] [blame]
/*****************************************************************************
* Copyright (c) 2015, 2020 CEA LIST.
*
* 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 <dirk.fauth@googlemail.com> - Initial API and implementation
* Vincent Lorenzo <vincent.lorenzo@cea.fr> - Bug 486624
*****************************************************************************/
package org.eclipse.nebula.widgets.nattable.fillhandle.command;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Calendar;
import java.util.Date;
import org.eclipse.nebula.widgets.nattable.command.ILayerCommandHandler;
import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry;
import org.eclipse.nebula.widgets.nattable.coordinate.PositionCoordinate;
import org.eclipse.nebula.widgets.nattable.copy.InternalCellClipboard;
import org.eclipse.nebula.widgets.nattable.edit.command.EditUtils;
import org.eclipse.nebula.widgets.nattable.edit.command.UpdateDataCommand;
import org.eclipse.nebula.widgets.nattable.fillhandle.config.FillHandleConfigAttributes;
import org.eclipse.nebula.widgets.nattable.layer.ILayer;
import org.eclipse.nebula.widgets.nattable.layer.cell.ILayerCell;
import org.eclipse.nebula.widgets.nattable.selection.SelectionLayer;
import org.eclipse.nebula.widgets.nattable.selection.SelectionLayer.MoveDirectionEnum;
import org.eclipse.nebula.widgets.nattable.style.DisplayMode;
import org.eclipse.swt.graphics.Rectangle;
/**
* Command handler for the {@link FillHandlePasteCommand}.
*
* @see FillHandlePasteCommand
*
* @since 1.4
*/
public class FillHandlePasteCommandHandler implements ILayerCommandHandler<FillHandlePasteCommand> {
protected SelectionLayer selectionLayer;
protected InternalCellClipboard clipboard;
/**
* Creates a {@link FillHandlePasteCommandHandler}
*
* @param selectionLayer
* The {@link SelectionLayer} needed to determine the fill handle
* region and perform the update command.
* @param clipboard
* The internal clipboard that carries the cells for the copy
* &amp; paste operation triggered by using the fill handle.
*/
public FillHandlePasteCommandHandler(
SelectionLayer selectionLayer,
InternalCellClipboard clipboard) {
this.selectionLayer = selectionLayer;
this.clipboard = clipboard;
}
@Override
public boolean doCommand(ILayer targetLayer, FillHandlePasteCommand command) {
if (this.clipboard.getCopiedCells() != null) {
int pasteColumn = -1;
int pasteRow = -1;
int pasteWidth = this.clipboard.getCopiedCells().length;
int pasteHeight = this.clipboard.getCopiedCells()[0].length;
Rectangle handleRegion = this.selectionLayer.getFillHandleRegion();
if (handleRegion != null) {
pasteColumn = handleRegion.x;
pasteRow = handleRegion.y;
pasteWidth = handleRegion.width;
pasteHeight = handleRegion.height;
} else {
PositionCoordinate coord = this.selectionLayer.getSelectionAnchor();
pasteColumn = coord.getColumnPosition();
pasteRow = coord.getRowPosition();
}
int pasteStartColumn = pasteColumn;
int rowStartAdjustment = 0;
if (command.direction == MoveDirectionEnum.UP) {
rowStartAdjustment = pasteHeight % this.clipboard.getCopiedCells().length;
}
int columnStartAdjustment = 0;
if (command.direction == MoveDirectionEnum.LEFT) {
columnStartAdjustment = pasteWidth % this.clipboard.getCopiedCells()[0].length;
}
for (int i = 0; i < pasteHeight; i++) {
ILayerCell[] cells = this.clipboard.getCopiedCells()[(i + rowStartAdjustment) % this.clipboard.getCopiedCells().length];
for (int j = 0; j < pasteWidth; j++) {
ILayerCell cell = cells[(j + columnStartAdjustment) % this.clipboard.getCopiedCells()[0].length];
Object cellValue = getPasteValue(cell, command, pasteColumn, pasteRow);
if (EditUtils.isCellEditable(
new PositionCoordinate(this.selectionLayer,
pasteColumn,
pasteRow),
command.configRegistry)) {
this.selectionLayer.doCommand(new UpdateDataCommand(this.selectionLayer, pasteColumn, pasteRow, cellValue));
}
pasteColumn++;
if (pasteColumn >= this.selectionLayer.getColumnCount()) {
break;
}
}
pasteRow++;
pasteColumn = pasteStartColumn;
}
}
return true;
}
/**
* Returns the value from the given cell that should be pasted to the given
* position.
*
* @param cell
* The cell that is copied.
* @param command
* The command that contains the necessary information for the
* paste operation.
* @param toColumn
* The column position of the cell to which the value should be
* pasted.
* @param toRow
* The row position of the cell to which the value should be
* pasted.
* @return The value that should be set to the given position.
*/
protected Object getPasteValue(ILayerCell cell, FillHandlePasteCommand command, int toColumn, int toRow) {
if (cell != null) {
switch (command.operation) {
case COPY:
return cell.getDataValue();
case SERIES:
Object diff = 0;
if (command.direction == MoveDirectionEnum.LEFT || command.direction == MoveDirectionEnum.RIGHT) {
diff = calculateVerticalDiff(cell, toColumn, command.configRegistry);
} else if (command.direction == MoveDirectionEnum.UP || command.direction == MoveDirectionEnum.DOWN) {
diff = calculateHorizontalDiff(cell, toRow, command.configRegistry);
}
Object value = cell.getDataValue();
// if we can not determine a common diff value we perform a
// copy
if (diff != null) {
if (value instanceof Byte) {
return (byte) (((Byte) value).byteValue() + (Byte) diff);
} else if (value instanceof Short) {
return (short) (((Short) value).shortValue() + (Short) diff);
} else if (value instanceof Integer) {
return (Integer) value + (Integer) diff;
} else if (value instanceof Long) {
return (Long) value + (Long) diff;
} else if (value instanceof Float) {
return (Float) value + (Float) diff;
} else if (value instanceof Double) {
return (Double) value + (Double) diff;
} else if (value instanceof BigInteger) {
return ((BigInteger) value).add((BigInteger) diff);
} else if (value instanceof BigDecimal) {
return ((BigDecimal) value).add((BigDecimal) diff);
} else if (value instanceof Date) {
Calendar cal = Calendar.getInstance();
cal.setTime((Date) value);
cal.add(getIncrementDateField(cell, command.configRegistry), (Integer) diff);
return cal.getTime();
}
}
// if the value is neither a number nor a date simply return
// the value as we can't calculate a series for other data
// types
return value;
default:
return cell.getDataValue();
}
}
return null;
}
protected Number calculateHorizontalDiff(ILayerCell cell, int toRow, IConfigRegistry configRegistry) {
Class<?> type = cell.getDataValue() != null ? cell.getDataValue().getClass() : null;
ILayerCell[][] cells = this.clipboard.getCopiedCells();
int rowDiff = getRowDiff(cell, toRow);
if (cells.length == 1) {
return getCastValue(rowDiff, type);
} else if (type != null) {
int columnArrayIndex = cell.getColumnIndex() - this.clipboard.getCopiedCells()[0][0].getColumnIndex();
if (type == Byte.class) {
Byte diff = calculateByteDiff(cells[1][columnArrayIndex], cells[0][columnArrayIndex]);
if (diff == null) {
return null;
}
Byte temp = diff;
for (int i = 1; i < cells.length; i++) {
temp = calculateByteDiff(cells[i][columnArrayIndex], cells[i - 1][columnArrayIndex]);
if (temp == null || !temp.equals(diff)) {
return null;
}
}
return (byte) (diff * rowDiff);
} else if (type == Short.class) {
Short diff = calculateShortDiff(cells[1][columnArrayIndex], cells[0][columnArrayIndex]);
if (diff == null) {
return null;
}
Short temp = diff;
for (int i = 1; i < cells.length; i++) {
temp = calculateShortDiff(cells[i][columnArrayIndex], cells[i - 1][columnArrayIndex]);
if (temp == null || !temp.equals(diff)) {
return null;
}
}
return (short) (diff * rowDiff);
} else if (type == Integer.class) {
Integer diff = calculateIntDiff(cells[1][columnArrayIndex], cells[0][columnArrayIndex]);
if (diff == null) {
return null;
}
Integer temp = diff;
for (int i = 1; i < cells.length; i++) {
temp = calculateIntDiff(cells[i][columnArrayIndex], cells[i - 1][columnArrayIndex]);
if (temp == null || !temp.equals(diff)) {
return null;
}
}
return diff * rowDiff;
} else if (type == Long.class) {
Long diff = calculateLongDiff(cells[1][columnArrayIndex], cells[0][columnArrayIndex]);
if (diff == null) {
return null;
}
Long temp = diff;
for (int i = 1; i < cells.length; i++) {
temp = calculateLongDiff(cells[i][columnArrayIndex], cells[i - 1][columnArrayIndex]);
if (temp == null || !temp.equals(diff)) {
return null;
}
}
return diff * rowDiff;
} else if (type == Float.class) {
Float diff = calculateFloatDiff(cells[1][columnArrayIndex], cells[0][columnArrayIndex]);
if (diff == null) {
return null;
}
Float temp = diff;
for (int i = 1; i < cells.length; i++) {
temp = calculateFloatDiff(cells[i][columnArrayIndex], cells[i - 1][columnArrayIndex]);
if (temp == null || !temp.equals(diff)) {
return null;
}
}
return BigDecimal.valueOf(diff).multiply(BigDecimal.valueOf(rowDiff)).floatValue();
} else if (type == Double.class) {
Double diff = calculateDoubleDiff(cells[1][columnArrayIndex], cells[0][columnArrayIndex]);
if (diff == null) {
return null;
}
Double temp = diff;
for (int i = 1; i < cells.length; i++) {
temp = calculateDoubleDiff(cells[i][columnArrayIndex], cells[i - 1][columnArrayIndex]);
if (temp == null || !temp.equals(diff)) {
return null;
}
}
return BigDecimal.valueOf(diff).multiply(BigDecimal.valueOf(rowDiff)).doubleValue();
} else if (type == BigInteger.class) {
BigInteger diff = calculateBigIntegerDiff(cells[1][columnArrayIndex], cells[0][columnArrayIndex]);
if (diff == null) {
return null;
}
BigInteger temp = diff;
for (int i = 1; i < cells.length; i++) {
temp = calculateBigIntegerDiff(cells[i][columnArrayIndex], cells[i - 1][columnArrayIndex]);
if (temp == null || !temp.equals(diff)) {
return null;
}
}
return diff.multiply(BigInteger.valueOf(rowDiff));
} else if (type == BigDecimal.class) {
BigDecimal diff = calculateBigDecimalDiff(cells[1][columnArrayIndex], cells[0][columnArrayIndex]);
if (diff == null) {
return null;
}
BigDecimal temp = diff;
for (int i = 1; i < cells.length; i++) {
temp = calculateBigDecimalDiff(cells[i][columnArrayIndex], cells[i - 1][columnArrayIndex]);
if (temp == null || !temp.equals(diff)) {
return null;
}
}
return diff.multiply(BigDecimal.valueOf(rowDiff));
} else if (type == Date.class) {
Integer diff = calculateDateDiff(cells[1][columnArrayIndex], cells[0][columnArrayIndex], configRegistry);
if (diff == null) {
return null;
}
Integer temp = diff;
for (int i = 1; i < cells.length; i++) {
temp = calculateDateDiff(cells[i][columnArrayIndex], cells[i - 1][columnArrayIndex], configRegistry);
if (temp == null || !temp.equals(diff)) {
return null;
}
}
return diff * rowDiff;
}
}
return null;
}
protected Number calculateVerticalDiff(ILayerCell cell, int toColumn, IConfigRegistry configRegistry) {
Class<?> type = cell.getDataValue() != null ? cell.getDataValue().getClass() : null;
ILayerCell[][] cells = this.clipboard.getCopiedCells();
int columnDiff = getColumnDiff(cell, toColumn);
int rowArrayIndex = cell.getRowIndex() - this.clipboard.getCopiedCells()[0][0].getRowIndex();
if (cells[rowArrayIndex].length == 1) {
return getCastValue(columnDiff, type);
} else if (type != null) {
if (type == Byte.class) {
Byte diff = calculateByteDiff(cells[rowArrayIndex][1], cells[rowArrayIndex][0]);
if (diff == null) {
return null;
}
Byte temp = diff;
for (int i = 1; i < cells.length; i++) {
temp = calculateByteDiff(cells[rowArrayIndex][i], cells[rowArrayIndex][i - 1]);
if (temp == null || !temp.equals(diff)) {
return null;
}
}
return (byte) (diff * columnDiff);
} else if (type == Short.class) {
Short diff = calculateShortDiff(cells[rowArrayIndex][1], cells[rowArrayIndex][0]);
if (diff == null) {
return null;
}
Short temp = diff;
for (int i = 1; i < cells.length; i++) {
temp = calculateShortDiff(cells[rowArrayIndex][i], cells[rowArrayIndex][i - 1]);
if (temp == null || !temp.equals(diff)) {
return null;
}
}
return (short) (diff * columnDiff);
} else if (type == Integer.class) {
Integer diff = calculateIntDiff(cells[rowArrayIndex][1], cells[rowArrayIndex][0]);
if (diff == null) {
return null;
}
Integer temp = diff;
for (int i = 1; i < cells.length; i++) {
temp = calculateIntDiff(cells[rowArrayIndex][i], cells[rowArrayIndex][i - 1]);
if (temp == null || !temp.equals(diff)) {
return null;
}
}
return diff * columnDiff;
} else if (type == Long.class) {
Long diff = calculateLongDiff(cells[rowArrayIndex][1], cells[rowArrayIndex][0]);
if (diff == null) {
return null;
}
Long temp = diff;
for (int i = 1; i < cells.length; i++) {
temp = calculateLongDiff(cells[rowArrayIndex][i], cells[rowArrayIndex][i - 1]);
if (temp == null || !temp.equals(diff)) {
return null;
}
}
return diff * columnDiff;
} else if (type == Float.class) {
Float diff = calculateFloatDiff(cells[rowArrayIndex][1], cells[rowArrayIndex][0]);
if (diff == null) {
return null;
}
Float temp = diff;
for (int i = 1; i < cells.length; i++) {
temp = calculateFloatDiff(cells[rowArrayIndex][i], cells[rowArrayIndex][i - 1]);
if (temp == null || !temp.equals(diff)) {
return null;
}
}
return BigDecimal.valueOf(diff).multiply(BigDecimal.valueOf(columnDiff)).floatValue();
} else if (type == Double.class) {
Double diff = calculateDoubleDiff(cells[rowArrayIndex][1], cells[rowArrayIndex][0]);
if (diff == null) {
return null;
}
Double temp = diff;
for (int i = 1; i < cells.length; i++) {
temp = calculateDoubleDiff(cells[rowArrayIndex][i], cells[rowArrayIndex][i - 1]);
if (temp == null || !temp.equals(diff)) {
return null;
}
}
return BigDecimal.valueOf(diff).multiply(BigDecimal.valueOf(columnDiff)).doubleValue();
} else if (type == BigInteger.class) {
BigInteger diff = calculateBigIntegerDiff(cells[rowArrayIndex][1], cells[rowArrayIndex][0]);
if (diff == null) {
return null;
}
BigInteger temp = diff;
for (int i = 1; i < cells.length; i++) {
temp = calculateBigIntegerDiff(cells[rowArrayIndex][i], cells[rowArrayIndex][i - 1]);
if (temp == null || !temp.equals(diff)) {
return null;
}
}
return diff.multiply(BigInteger.valueOf(columnDiff));
} else if (type == BigDecimal.class) {
BigDecimal diff = calculateBigDecimalDiff(cells[rowArrayIndex][1], cells[rowArrayIndex][0]);
if (diff == null) {
return null;
}
BigDecimal temp = diff;
for (int i = 1; i < cells.length; i++) {
temp = calculateBigDecimalDiff(cells[rowArrayIndex][i], cells[rowArrayIndex][i - 1]);
if (temp == null || !temp.equals(diff)) {
return null;
}
}
return diff.multiply(BigDecimal.valueOf(columnDiff));
} else if (type == Date.class) {
Integer diff = calculateDateDiff(cells[rowArrayIndex][1], cells[rowArrayIndex][0], configRegistry);
if (diff == null) {
return null;
}
Integer temp = diff;
for (int i = 1; i < cells.length; i++) {
temp = calculateDateDiff(cells[rowArrayIndex][i], cells[rowArrayIndex][i - 1], configRegistry);
if (temp == null || !temp.equals(diff)) {
return null;
}
}
return diff * columnDiff;
}
}
return null;
}
/**
* Calculate the row difference between the cell row index and the row index
* of the cell to copy to.
*
* @param currentCell
* The current cell to manage.
* @param toRow
* The row index of the cell to copy to.
* @return The difference as integer.
*/
protected int getRowDiff(final ILayerCell currentCell, final int toRow) {
return toRow - currentCell.getRowIndex();
}
/**
* Calculate the column difference between the cell column index and the
* column index of the cell to copy to.
*
* @param currentCell
* The current cell to manage.
* @param toColumn
* The column index of the cell to copy to.
* @return The difference as integer.
*/
protected int getColumnDiff(final ILayerCell currentCell, final int toColumn) {
return toColumn - currentCell.getColumnIndex();
}
protected Number getCastValue(int diff, Class<?> type) {
if (type != null) {
if (type == Byte.class) {
return (byte) diff;
} else if (type == Short.class) {
return (short) diff;
} else if (type == Long.class) {
return (long) diff;
} else if (type == Float.class) {
return (float) diff;
} else if (type == Double.class) {
return (double) diff;
} else if (type == BigInteger.class) {
return BigInteger.valueOf(diff);
} else if (type == BigDecimal.class) {
return BigDecimal.valueOf(diff);
}
}
return diff;
}
protected Byte calculateByteDiff(ILayerCell c1, ILayerCell c2) {
if (c1 == null || c2 == null) {
return null;
}
return (c1.getDataValue() == null || c2.getDataValue() == null
|| !(c1.getDataValue() instanceof Byte) || !(c2.getDataValue() instanceof Byte))
? null
: (byte) (((Byte) c1.getDataValue()) - ((Byte) c2.getDataValue()));
}
protected Short calculateShortDiff(ILayerCell c1, ILayerCell c2) {
if (c1 == null || c2 == null) {
return null;
}
return (c1.getDataValue() == null || c2.getDataValue() == null
|| !(c1.getDataValue() instanceof Short) || !(c2.getDataValue() instanceof Short))
? null
: (short) (((Short) c1.getDataValue()) - ((Short) c2.getDataValue()));
}
protected Integer calculateIntDiff(ILayerCell c1, ILayerCell c2) {
if (c1 == null || c2 == null) {
return null;
}
return (c1.getDataValue() == null || c2.getDataValue() == null
|| !(c1.getDataValue() instanceof Integer) || !(c2.getDataValue() instanceof Integer))
? null
: (Integer) (c1.getDataValue()) - (Integer) (c2.getDataValue());
}
protected Long calculateLongDiff(ILayerCell c1, ILayerCell c2) {
if (c1 == null || c2 == null) {
return null;
}
return (c1.getDataValue() == null || c2.getDataValue() == null
|| !(c1.getDataValue() instanceof Long) || !(c2.getDataValue() instanceof Long))
? null
: ((Long) c1.getDataValue()) - ((Long) c2.getDataValue());
}
protected Float calculateFloatDiff(ILayerCell c1, ILayerCell c2) {
if (c1 == null || c2 == null) {
return null;
}
if (c1.getDataValue() == null || c2.getDataValue() == null
|| !(c1.getDataValue() instanceof Float) || !(c2.getDataValue() instanceof Float)) {
return null;
}
// Use BigDecimal for exact calculations because of floating point
// arithmetic issues. Because of that we also use the String constructor
// of BigDecimal.
BigDecimal v1 = new BigDecimal(c1.getDataValue().toString());
BigDecimal v2 = new BigDecimal(c2.getDataValue().toString());
return v1.subtract(v2).floatValue();
}
protected Double calculateDoubleDiff(ILayerCell c1, ILayerCell c2) {
if (c1 == null || c2 == null) {
return null;
}
if (c1.getDataValue() == null || c2.getDataValue() == null
|| !(c1.getDataValue() instanceof Double) || !(c2.getDataValue() instanceof Double)) {
return null;
}
// Use BigDecimal for exact calculations because of floating point
// arithmetic issues. Because of that we also use the String constructor
// of BigDecimal.
BigDecimal v1 = new BigDecimal(c1.getDataValue().toString());
BigDecimal v2 = new BigDecimal(c2.getDataValue().toString());
return v1.subtract(v2).doubleValue();
}
protected BigInteger calculateBigIntegerDiff(ILayerCell c1, ILayerCell c2) {
if (c1 == null || c2 == null) {
return null;
}
return (c1.getDataValue() == null || c2.getDataValue() == null
|| !(c1.getDataValue() instanceof BigInteger) || !(c2.getDataValue() instanceof BigInteger))
? null
: ((BigInteger) c1.getDataValue()).subtract((BigInteger) c2.getDataValue());
}
protected BigDecimal calculateBigDecimalDiff(ILayerCell c1, ILayerCell c2) {
if (c1 == null || c2 == null) {
return null;
}
return (c1.getDataValue() == null || c2.getDataValue() == null
|| !(c1.getDataValue() instanceof BigDecimal) || !(c2.getDataValue() instanceof BigDecimal))
? null
: ((BigDecimal) c1.getDataValue()).subtract((BigDecimal) c2.getDataValue());
}
protected Integer calculateDateDiff(ILayerCell c1, ILayerCell c2, IConfigRegistry configRegistry) {
if (c1 == null || c2 == null) {
return null;
}
if (c1.getDataValue() == null || c2.getDataValue() == null
|| !(c1.getDataValue() instanceof Date) || !(c2.getDataValue() instanceof Date)) {
return null;
}
int dateField = getIncrementDateField(c1, configRegistry);
Calendar cal1 = Calendar.getInstance();
cal1.setTime((Date) c1.getDataValue());
int fieldValue1 = cal1.get(dateField);
Calendar cal2 = Calendar.getInstance();
cal2.setTime((Date) c2.getDataValue());
int fieldValue2 = cal2.get(dateField);
return fieldValue1 - fieldValue2;
}
protected int getIncrementDateField(ILayerCell cell, IConfigRegistry configRegistry) {
Integer dateField = configRegistry.getConfigAttribute(
FillHandleConfigAttributes.INCREMENT_DATE_FIELD,
DisplayMode.NORMAL,
cell.getConfigLabels());
if (dateField == null) {
dateField = Calendar.DATE;
}
return dateField;
}
@Override
public Class<FillHandlePasteCommand> getCommandClass() {
return FillHandlePasteCommand.class;
}
}