All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.googlecode.lanterna.gui2.table.TableModel Maven / Gradle / Ivy

There is a newer version: 3.2.0-alpha1
Show newest version
/*
 * This file is part of lanterna (https://github.com/mabe02/lanterna).
 *
 * lanterna is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see .
 *
 * Copyright (C) 2010-2020 Martin Berglund
 */
package com.googlecode.lanterna.gui2.table;

import java.util.*;

/**
 * A {@code TableModel} contains the data model behind a table, here is where all the action cell values and header
 * labels are stored.
 *
 * @author Martin
 */
public class TableModel {

    /**
     * Listener interface for the {@link TableModel} class which can be attached to a {@link TableModel} to be notified
     * of changes to the table model.
     * @param  Value type stored in the table
     */
    public interface Listener {
        /**
         * Called when a new row has been added to the model
         * @param model Model the row was added to
         * @param index Index of the new row
         */
        void onRowAdded(TableModel model, int index);
        /**
         * Called when a row has been removed from the model
         * @param model Model the row was removed from
         * @param index Index of the removed row
         * @param oldRow Content of the row that was removed
         */
        void onRowRemoved(TableModel model, int index, List oldRow);
        /**
         * Called when a new column has been added to the model
         * @param model Model the column was added to
         * @param index Index of the new column
         */
        void onColumnAdded(TableModel model, int index);

        /**
         * Called when a column has been removed from the model
         * @param model Model the column was removed from
         * @param index Index of the removed column
         * @param oldHeader Header the removed column had
         * @param oldColumn Values in the removed column
         */
        void onColumnRemoved(TableModel model, int index, String oldHeader, List oldColumn);

        /**
         * Called when an existing cell had its content updated
         * @param model Model that was modified
         * @param row Row index of the modified cell
         * @param column Column index of the modified cell
         * @param oldValue Previous value of the cell
         * @param newValue New value of the cell
         */
        void onCellChanged(TableModel model, int row, int column, V oldValue, V newValue);
    }

    private final List columns;
    private final List> rows;
    private final List> listeners;

    /**
     * Default constructor, creates a new model with same number of columns as labels supplied
     * @param columnLabels Labels for the column headers
     */
    public TableModel(String... columnLabels) {
        this.columns = new ArrayList<>(Arrays.asList(columnLabels));
        this.rows = new ArrayList<>();
        this.listeners = new ArrayList<>();
    }

    /**
     * Returns the number of columns in the model
     * @return Number of columns in the model
     */
    public synchronized int getColumnCount() {
        return columns.size();
    }

    /**
     * Returns number of rows in the model
     * @return Number of rows in the model
     */
    public synchronized int getRowCount() {
        return rows.size();
    }

    /**
     * Returns all rows in the model as a list of lists containing the data as elements
     * @return All rows in the model as a list of lists containing the data as elements
     */
    public synchronized List> getRows() {
        List> copy = new ArrayList<>();
        for(List row: rows) {
            copy.add(new ArrayList<>(row));
        }
        return copy;
    }

    /**
     * Returns all column header label as a list of strings
     * @return All column header label as a list of strings
     */
    public synchronized List getColumnLabels() {
        return new ArrayList<>(columns);
    }

    /**
     * Returns a row from the table as a list of the cell data
     * @param index Index of the row to return
     * @return Row from the table as a list of the cell data
     */
    public synchronized List getRow(int index) {
        return new ArrayList<>(rows.get(index));
    }

    /**
     * Adds a new row to the table model at the end. This may update the selection to make sure the same row is selected.
     * @param values Data to associate with the new row, mapped column by column in order
     * @return Itself
     */
    @SafeVarargs
    public final synchronized TableModel addRow(V... values) {
        addRow(Arrays.asList(values));
        return this;
    }

    /**
     * Adds a new row to the table model at the end. This may update the selection to make sure the same row is selected.
     * @param values Data to associate with the new row, mapped column by column in order
     * @return Itself
     */
    public synchronized TableModel addRow(Collection values) {
        insertRow(getRowCount(), values);
        return this;
    }

    /**
     * Inserts a new row to the table model at a particular index. This may update the selection to make sure the same row is selected.
     * @param index Index the new row should have, 0 means the first row and row count will append the row at the
     *              end
     * @param values Data to associate with the new row, mapped column by column in order
     * @return Itself
     */
    public synchronized TableModel insertRow(int index, Collection values) {
        ArrayList list = new ArrayList<>(values);
        rows.add(index, list);
        for(Listener listener: listeners) {
            listener.onRowAdded(this, index);
        }
        return this;
    }

    /**
     * Removes a row at a particular index from the table model. This may update the selection to make sure the same row is selected.
     * @param index Index of the row to remove
     * @return Itself
     */
    public synchronized TableModel removeRow(int index) {
        List removedRow = rows.remove(index);
        for(Listener listener: listeners) {
            listener.onRowRemoved(this, index, removedRow);
        }
        return this;
    }

    /**
     * Removes all rows from the table, this will trigger listeners for each row
     * @return Itself
     */
    public synchronized TableModel clear() {
        while (rows.size() > 0) {
            removeRow(0);
        }
        return this;
    }

    /**
     * Returns the label of a column header
     * @param index Index of the column to retrieve the header label for
     * @return Label of the column selected
     */
    public synchronized String getColumnLabel(int index) {
        return columns.get(index);
    }

    /**
     * Updates the label of a column header
     * @param index Index of the column to update the header label for
     * @param newLabel New label to assign to the column header
     * @return Itself
     */
    public synchronized TableModel setColumnLabel(int index, String newLabel) {
        columns.set(index, newLabel);
        return this;
    }

    /**
     * Adds a new column into the table model as the last column. You can optionally supply values for the existing rows
     * through the {@code newColumnValues}.
     * @param label Label for the header of the new column
     * @param newColumnValues Optional values to assign to the existing rows, where the first element in the array will
     *                        be the value of the first row and so on...
     * @return Itself
     */
    public synchronized TableModel addColumn(String label, V[] newColumnValues) {
        return insertColumn(getColumnCount(), label, newColumnValues);
    }

    /**
     * Adds a new column into the table model at a specified index. You can optionally supply values for the existing
     * rows through the {@code newColumnValues}.
     * @param index Index for the new column
     * @param label Label for the header of the new column
     * @param newColumnValues Optional values to assign to the existing rows, where the first element in the array will
     *                        be the value of the first row and so on...
     * @return Itself
     */
    public synchronized TableModel insertColumn(int index, String label, V[] newColumnValues) {
        columns.add(index, label);
        for(int i = 0; i < rows.size(); i++) {
            List row = rows.get(i);

            //Pad row with null if necessary
            for(int j = row.size(); j < index; j++) {
                row.add(null);
            }

            if(newColumnValues != null && i < newColumnValues.length && newColumnValues[i] != null) {
                row.add(index, newColumnValues[i]);
            }
            else {
                row.add(index, null);
            }
        }

        for(Listener listener: listeners) {
            listener.onColumnAdded(this, index);
        }
        return this;
    }

    /**
     * Removes a column from the table model
     * @param index Index of the column to remove
     * @return Itself
     */
    public synchronized TableModel removeColumn(int index) {
        String removedColumnHeader = columns.remove(index);
        List removedColumn = new ArrayList<>();
        for(List row : rows) {
            removedColumn.add(row.remove(index));
        }
        for(Listener listener: listeners) {
            listener.onColumnRemoved(this, index, removedColumnHeader, removedColumn);
        }
        return this;
    }

    /**
     * Returns the cell value stored at a specific column/row coordinate.
     * @param columnIndex Column index of the cell
     * @param rowIndex Row index of the cell
     * @return The data value stored in this cell
     */
    public synchronized V getCell(int columnIndex, int rowIndex) {
        if(rowIndex < 0 || columnIndex < 0) {
            throw new IndexOutOfBoundsException("Invalid row or column index: " + rowIndex + " " + columnIndex);
        }
        else if (rowIndex >= getRowCount()) {
            throw new IndexOutOfBoundsException("TableModel has " + getRowCount() + " rows, invalid access at rowIndex " + rowIndex);
        }
        if(columnIndex >= getColumnCount()) {
            throw new IndexOutOfBoundsException("TableModel has " + columnIndex + " columns, invalid access at columnIndex " + columnIndex);
        }
        return rows.get(rowIndex).get(columnIndex);
    }

    /**
     * Updates the call value stored at a specific column/row coordinate.
     * @param columnIndex Column index of the cell
     * @param rowIndex Row index of the cell
     * @param value New value to assign to the cell
     * @return Itself
     */
    public synchronized TableModel setCell(int columnIndex, int rowIndex, V value) {
        getCell(columnIndex, rowIndex);
        List row = rows.get(rowIndex);

        //Pad row with null if necessary
        for(int j = row.size(); j < columnIndex; j++) {
            row.add(null);
        }

        V existingValue = row.get(columnIndex);
        if(existingValue == value) {
            return this;
        }
        row.set(columnIndex, value);
        for(Listener listener: listeners) {
            listener.onCellChanged(this, rowIndex, columnIndex, existingValue, value);
        }
        return this;
    }

    /**
     * Adds a listener to this table model that will be notified whenever the model changes
     * @param listener {@link Listener} to register with this model
     * @return Itself
     */
    public TableModel addListener(Listener listener) {
        listeners.add(listener);
        return this;
    }

    /**
     * Removes a listener from this model so that it will no longer receive any notifications when the model changes
     * @param listener {@link Listener} to deregister from this model
     * @return Itself
     */
    public TableModel removeListener(Listener listener) {
        listeners.remove(listener);
        return this;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy