| 1 | package com.googlecode.lanterna.gui2.table; |
| 2 | |
| 3 | import java.util.*; |
| 4 | |
| 5 | /** |
| 6 | * A {@code TableModel} contains the data model behind a table, here is where all the action cell values and header |
| 7 | * labels are stored. |
| 8 | * |
| 9 | * @author Martin |
| 10 | */ |
| 11 | public class TableModel<V> { |
| 12 | private final List<String> columns; |
| 13 | private final List<List<V>> rows; |
| 14 | |
| 15 | /** |
| 16 | * Default constructor, creates a new model with same number of columns as labels supplied |
| 17 | * @param columnLabels Labels for the column headers |
| 18 | */ |
| 19 | public TableModel(String... columnLabels) { |
| 20 | this.columns = new ArrayList<String>(Arrays.asList(columnLabels)); |
| 21 | this.rows = new ArrayList<List<V>>(); |
| 22 | } |
| 23 | |
| 24 | /** |
| 25 | * Returns the number of columns in the model |
| 26 | * @return Number of columns in the model |
| 27 | */ |
| 28 | public synchronized int getColumnCount() { |
| 29 | return columns.size(); |
| 30 | } |
| 31 | |
| 32 | /** |
| 33 | * Returns number of rows in the model |
| 34 | * @return Number of rows in the model |
| 35 | */ |
| 36 | public synchronized int getRowCount() { |
| 37 | return rows.size(); |
| 38 | } |
| 39 | |
| 40 | /** |
| 41 | * Returns all rows in the model as a list of lists containing the data as elements |
| 42 | * @return All rows in the model as a list of lists containing the data as elements |
| 43 | */ |
| 44 | public synchronized List<List<V>> getRows() { |
| 45 | List<List<V>> copy = new ArrayList<List<V>>(); |
| 46 | for(List<V> row: rows) { |
| 47 | copy.add(new ArrayList<V>(row)); |
| 48 | } |
| 49 | return copy; |
| 50 | } |
| 51 | |
| 52 | /** |
| 53 | * Returns all column header label as a list of strings |
| 54 | * @return All column header label as a list of strings |
| 55 | */ |
| 56 | public synchronized List<String> getColumnLabels() { |
| 57 | return new ArrayList<String>(columns); |
| 58 | } |
| 59 | |
| 60 | /** |
| 61 | * Returns a row from the table as a list of the cell data |
| 62 | * @param index Index of the row to return |
| 63 | * @return Row from the table as a list of the cell data |
| 64 | */ |
| 65 | public synchronized List<V> getRow(int index) { |
| 66 | return new ArrayList<V>(rows.get(index)); |
| 67 | } |
| 68 | |
| 69 | /** |
| 70 | * Adds a new row to the table model at the end |
| 71 | * @param values Data to associate with the new row, mapped column by column in order |
| 72 | * @return Itself |
| 73 | */ |
| 74 | public synchronized TableModel<V> addRow(V... values) { |
| 75 | addRow(Arrays.asList(values)); |
| 76 | return this; |
| 77 | } |
| 78 | |
| 79 | /** |
| 80 | * Adds a new row to the table model at the end |
| 81 | * @param values Data to associate with the new row, mapped column by column in order |
| 82 | * @return Itself |
| 83 | */ |
| 84 | public synchronized TableModel<V> addRow(Collection<V> values) { |
| 85 | insertRow(getRowCount(), values); |
| 86 | return this; |
| 87 | } |
| 88 | |
| 89 | /** |
| 90 | * Inserts a new row to the table model at a particular index |
| 91 | * @param index Index the new row should have, 0 means the first row and <i>row count</i> will append the row at the |
| 92 | * end |
| 93 | * @param values Data to associate with the new row, mapped column by column in order |
| 94 | * @return Itself |
| 95 | */ |
| 96 | public synchronized TableModel<V> insertRow(int index, Collection<V> values) { |
| 97 | ArrayList<V> list = new ArrayList<V>(values); |
| 98 | rows.add(index, list); |
| 99 | return this; |
| 100 | } |
| 101 | |
| 102 | /** |
| 103 | * Removes a row at a particular index from the table model |
| 104 | * @param index Index of the row to remove |
| 105 | * @return Itself |
| 106 | */ |
| 107 | public synchronized TableModel<V> removeRow(int index) { |
| 108 | rows.remove(index); |
| 109 | return this; |
| 110 | } |
| 111 | |
| 112 | /** |
| 113 | * Returns the label of a column header |
| 114 | * @param index Index of the column to retrieve the header label for |
| 115 | * @return Label of the column selected |
| 116 | */ |
| 117 | public synchronized String getColumnLabel(int index) { |
| 118 | return columns.get(index); |
| 119 | } |
| 120 | |
| 121 | /** |
| 122 | * Updates the label of a column header |
| 123 | * @param index Index of the column to update the header label for |
| 124 | * @param newLabel New label to assign to the column header |
| 125 | * @return Itself |
| 126 | */ |
| 127 | public synchronized TableModel<V> setColumnLabel(int index, String newLabel) { |
| 128 | columns.set(index, newLabel); |
| 129 | return this; |
| 130 | } |
| 131 | |
| 132 | /** |
| 133 | * Adds a new column into the table model as the last column. You can optionally supply values for the existing rows |
| 134 | * through the {@code newColumnValues}. |
| 135 | * @param label Label for the header of the new column |
| 136 | * @param newColumnValues Optional values to assign to the existing rows, where the first element in the array will |
| 137 | * be the value of the first row and so on... |
| 138 | * @return Itself |
| 139 | */ |
| 140 | public synchronized TableModel<V> addColumn(String label, V[] newColumnValues) { |
| 141 | return insertColumn(getColumnCount(), label, newColumnValues); |
| 142 | } |
| 143 | |
| 144 | /** |
| 145 | * Adds a new column into the table model at a specified index. You can optionally supply values for the existing |
| 146 | * rows through the {@code newColumnValues}. |
| 147 | * @param index Index for the new column |
| 148 | * @param label Label for the header of the new column |
| 149 | * @param newColumnValues Optional values to assign to the existing rows, where the first element in the array will |
| 150 | * be the value of the first row and so on... |
| 151 | * @return Itself |
| 152 | */ |
| 153 | public synchronized TableModel<V> insertColumn(int index, String label, V[] newColumnValues) { |
| 154 | columns.add(index, label); |
| 155 | for(int i = 0; i < rows.size(); i++) { |
| 156 | List<V> row = rows.get(i); |
| 157 | |
| 158 | //Pad row with null if necessary |
| 159 | for(int j = row.size(); j < index; j++) { |
| 160 | row.add(null); |
| 161 | } |
| 162 | |
| 163 | if(newColumnValues != null && i < newColumnValues.length && newColumnValues[i] != null) { |
| 164 | row.add(index, newColumnValues[i]); |
| 165 | } |
| 166 | else { |
| 167 | row.add(index, null); |
| 168 | } |
| 169 | } |
| 170 | return this; |
| 171 | } |
| 172 | |
| 173 | /** |
| 174 | * Removes a column from the table model |
| 175 | * @param index Index of the column to remove |
| 176 | * @return Itself |
| 177 | */ |
| 178 | public synchronized TableModel<V> removeColumn(int index) { |
| 179 | columns.remove(index); |
| 180 | for(List<V> row : rows) { |
| 181 | row.remove(index); |
| 182 | } |
| 183 | return this; |
| 184 | } |
| 185 | |
| 186 | /** |
| 187 | * Returns the cell value stored at a specific column/row coordinate. |
| 188 | * @param columnIndex Column index of the cell |
| 189 | * @param rowIndex Row index of the cell |
| 190 | * @return The data value stored in this cell |
| 191 | */ |
| 192 | public synchronized V getCell(int columnIndex, int rowIndex) { |
| 193 | if(rowIndex < 0 || columnIndex < 0) { |
| 194 | throw new IndexOutOfBoundsException("Invalid row or column index: " + rowIndex + " " + columnIndex); |
| 195 | } |
| 196 | else if (rowIndex >= getRowCount()) { |
| 197 | throw new IndexOutOfBoundsException("TableModel has " + getRowCount() + " rows, invalid access at rowIndex " + rowIndex); |
| 198 | } |
| 199 | if(columnIndex >= getColumnCount()) { |
| 200 | throw new IndexOutOfBoundsException("TableModel has " + columnIndex + " columns, invalid access at columnIndex " + columnIndex); |
| 201 | } |
| 202 | return rows.get(rowIndex).get(columnIndex); |
| 203 | } |
| 204 | |
| 205 | /** |
| 206 | * Updates the call value stored at a specific column/row coordinate. |
| 207 | * @param columnIndex Column index of the cell |
| 208 | * @param rowIndex Row index of the cell |
| 209 | * @param value New value to assign to the cell |
| 210 | * @return Itself |
| 211 | */ |
| 212 | public synchronized TableModel<V> setCell(int columnIndex, int rowIndex, V value) { |
| 213 | getCell(columnIndex, rowIndex); |
| 214 | List<V> row = rows.get(rowIndex); |
| 215 | |
| 216 | //Pad row with null if necessary |
| 217 | for(int j = row.size(); j < columnIndex; j++) { |
| 218 | row.add(null); |
| 219 | } |
| 220 | |
| 221 | V existingValue = row.get(columnIndex); |
| 222 | if(existingValue == value) { |
| 223 | return this; |
| 224 | } |
| 225 | row.set(columnIndex, value); |
| 226 | return this; |
| 227 | } |
| 228 | } |