001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.data;
003
004import java.util.Arrays;
005import java.util.function.IntSupplier;
006
007import org.openstreetmap.josm.tools.Utils;
008
009/**
010 * Defines a model that can be reordered.
011 * @param <T> item type
012 * @since 15226
013 */
014public interface ReorderableModel<T> {
015
016    /**
017     * Get object value at given index.
018     * @param index index
019     * @return object value at given index
020     */
021    T getValue(int index);
022
023    /**
024     * Set object value at given index.
025     * @param index index
026     * @param value new value
027     * @return the value previously at the specified position
028     */
029    T setValue(int index, T value);
030
031    /**
032     * Checks that a range of rows can be moved by a given number of positions.
033     * @param delta negative or positive delta
034     * @param rowCount row count supplier
035     * @param rows indexes of rows to move
036     * @return {@code true} if rows can be moved
037     */
038    default boolean canMove(int delta, IntSupplier rowCount, int... rows) {
039        if (rows == null || rows.length == 0)
040            return false;
041        int[] sortedRows = Utils.copyArray(rows);
042        Arrays.sort(sortedRows);
043        if (delta < 0)
044            return sortedRows[0] >= -delta;
045        else if (delta > 0)
046            return sortedRows[sortedRows.length-1] <= rowCount.getAsInt()-1 - delta;
047        else
048            return true;
049    }
050
051    /**
052     * Checks that a range of rows can be moved up (by 1 position).
053     * @param rowCount row count supplier
054     * @param rows indexes of rows to move up
055     * @return {@code true} if rows can be moved up
056     */
057    default boolean canMoveUp(IntSupplier rowCount, int... rows) {
058        return canMove(-1, rowCount, rows);
059    }
060
061    /**
062     * Checks that a range of rows can be moved down (by 1 position).
063     * @param rowCount row count supplier
064     * @param rows indexes of rows to move down
065     * @return {@code true} if rows can be moved down
066     */
067    default boolean canMoveDown(IntSupplier rowCount, int... rows) {
068        return canMove(1, rowCount, rows);
069    }
070
071    /**
072     * Performs the move operation, without any check nor selection handling.
073     * @param delta negative or positive delta
074     * @param selectedRows rows to move
075     * @return {@code true} if rows have been moved
076     */
077    default boolean doMove(int delta, int... selectedRows) {
078        if (delta != 0) {
079            if (delta < 0) {
080                for (int row: selectedRows) {
081                    setValue(row + delta, setValue(row, getValue(row + delta)));
082                }
083            } else {
084                for (int i = selectedRows.length - 1; i >= 0; i--) {
085                    int row = selectedRows[i];
086                    setValue(row + delta, setValue(row, getValue(row + delta)));
087                }
088            }
089        }
090        return delta != 0 && selectedRows.length > 0;
091    }
092}