blob: d0fccb65b89f24b968be709ea6702aa440d253d8 [file] [log] [blame]
Simon Hunte9828152015-05-01 17:54:25 -07001/*
2 * Copyright 2015 Open Networking Laboratory
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package org.onosproject.ui.table;
18
19import com.google.common.collect.Sets;
Simon Hunt3ee7f742015-05-05 10:18:20 -070020import org.onosproject.ui.table.cell.DefaultCellComparator;
21import org.onosproject.ui.table.cell.DefaultCellFormatter;
Simon Hunte9828152015-05-01 17:54:25 -070022
23import java.util.ArrayList;
24import java.util.Arrays;
Simon Hunt933b1a82015-05-04 19:07:24 -070025import java.util.Collections;
26import java.util.Comparator;
Simon Hunte9828152015-05-01 17:54:25 -070027import java.util.HashMap;
28import java.util.List;
29import java.util.Map;
30import java.util.Set;
31
32import static com.google.common.base.Preconditions.checkArgument;
33import static com.google.common.base.Preconditions.checkNotNull;
34
35/**
Simon Hunt933b1a82015-05-04 19:07:24 -070036 * A simple model of table data.
37 * <p>
38 * Note that this is not a full MVC type model; the expected usage pattern
39 * is to create an empty table, add rows (by consulting the business model),
40 * sort rows (based on client request parameters), and finally produce the
41 * sorted list of rows.
42 * <p>
43 * The table also provides a mechanism for defining how cell values for a
44 * particular column should be formatted into strings, to help facilitate
45 * the encoding of the table data into a JSON structure.
Simon Hunt27bf0792015-05-07 10:50:29 -070046 * <p>
47 * Note that it is expected that all values for a particular column will
48 * be the same class.
Simon Hunte9828152015-05-01 17:54:25 -070049 */
50public class TableModel {
51
Simon Hunt3d1b0652015-05-05 17:27:24 -070052 private static final CellComparator DEF_CMP = DefaultCellComparator.INSTANCE;
53 private static final CellFormatter DEF_FMT = DefaultCellFormatter.INSTANCE;
Simon Hunte9828152015-05-01 17:54:25 -070054
55 private final String[] columnIds;
56 private final Set<String> idSet;
Simon Hunt933b1a82015-05-04 19:07:24 -070057 private final Map<String, CellComparator> comparators = new HashMap<>();
Simon Hunte9828152015-05-01 17:54:25 -070058 private final Map<String, CellFormatter> formatters = new HashMap<>();
59 private final List<Row> rows = new ArrayList<>();
60
61
62 /**
63 * Constructs a table (devoid of data) with the given column IDs.
64 *
65 * @param columnIds column identifiers
66 */
67 public TableModel(String... columnIds) {
68 checkNotNull(columnIds, "columnIds cannot be null");
69 checkArgument(columnIds.length > 0, "must be at least one column");
70
71 idSet = Sets.newHashSet(columnIds);
72 if (idSet.size() != columnIds.length) {
73 throw new IllegalArgumentException("duplicate column ID(s) detected");
74 }
75
76 this.columnIds = Arrays.copyOf(columnIds, columnIds.length);
77 }
78
79 private void checkId(String id) {
80 checkNotNull(id, "must provide a column ID");
81 if (!idSet.contains(id)) {
82 throw new IllegalArgumentException("unknown column id: " + id);
83 }
84 }
85
86 /**
87 * Returns the number of rows in this table model.
88 *
89 * @return number of rows
90 */
91 public int rowCount() {
92 return rows.size();
93 }
94
95 /**
96 * Returns the number of columns in this table model.
97 *
98 * @return number of columns
99 */
100 public int columnCount() {
101 return columnIds.length;
102 }
103
104 /**
Simon Hunt3d1b0652015-05-05 17:27:24 -0700105 * Returns the array of column IDs for this table model.
106 * <p>
107 * Implementation note: we are knowingly passing you a reference to
108 * our internal array to avoid copying. Don't mess with it. It's your
109 * table you'll break if you do!
Simon Hunte9828152015-05-01 17:54:25 -0700110 *
Simon Hunt3d1b0652015-05-05 17:27:24 -0700111 * @return the column identifiers
Simon Hunte9828152015-05-01 17:54:25 -0700112 */
Simon Hunt3d1b0652015-05-05 17:27:24 -0700113 public String[] getColumnIds() {
114 return columnIds;
Simon Hunte9828152015-05-01 17:54:25 -0700115 }
116
117 /**
118 * Returns the raw {@link Row} representation of the rows in this table.
119 *
120 * @return raw table rows
121 */
122 public Row[] getRows() {
123 return rows.toArray(new Row[rows.size()]);
124 }
125
126 /**
Simon Hunt933b1a82015-05-04 19:07:24 -0700127 * Sets a cell comparator for the specified column.
128 *
129 * @param columnId column identifier
130 * @param comparator comparator to use
131 */
132 public void setComparator(String columnId, CellComparator comparator) {
133 checkNotNull(comparator, "must provide a comparator");
134 checkId(columnId);
135 comparators.put(columnId, comparator);
136 }
137
138 /**
139 * Returns the cell comparator to use on values in the specified column.
140 *
141 * @param columnId column identifier
142 * @return an appropriate cell comparator
143 */
144 private CellComparator getComparator(String columnId) {
145 checkId(columnId);
146 CellComparator cmp = comparators.get(columnId);
147 return cmp == null ? DEF_CMP : cmp;
148 }
149
150 /**
Simon Hunte9828152015-05-01 17:54:25 -0700151 * Sets a cell formatter for the specified column.
152 *
153 * @param columnId column identifier
154 * @param formatter formatter to use
155 */
156 public void setFormatter(String columnId, CellFormatter formatter) {
157 checkNotNull(formatter, "must provide a formatter");
158 checkId(columnId);
159 formatters.put(columnId, formatter);
160 }
161
162 /**
163 * Returns the cell formatter to use on values in the specified column.
164 *
165 * @param columnId column identifier
166 * @return an appropriate cell formatter
167 */
168 public CellFormatter getFormatter(String columnId) {
169 checkId(columnId);
170 CellFormatter fmt = formatters.get(columnId);
171 return fmt == null ? DEF_FMT : fmt;
172 }
173
174 /**
175 * Adds a row to the table model.
176 *
177 * @return the row, for chaining
178 */
179 public Row addRow() {
180 Row r = new Row();
181 rows.add(r);
182 return r;
183 }
184
185 /**
Simon Hunt933b1a82015-05-04 19:07:24 -0700186 * Sorts the table rows based on the specified column, in the
187 * specified direction.
188 *
189 * @param columnId column identifier
190 * @param dir sort direction
191 */
192 public void sort(String columnId, SortDir dir) {
193 Collections.sort(rows, new RowComparator(columnId, dir));
194 }
195
196
197 /** Designates sorting direction. */
198 public enum SortDir {
199 /** Designates an ascending sort. */
200 ASC,
201 /** Designates a descending sort. */
202 DESC
203 }
204
205 /**
206 * Row comparator.
207 */
208 private class RowComparator implements Comparator<Row> {
209 private final String columnId;
210 private final SortDir dir;
211 private final CellComparator cellComparator;
212
213 /**
214 * Constructs a row comparator based on the specified
215 * column identifier and sort direction.
216 *
217 * @param columnId column identifier
218 * @param dir sort direction
219 */
220 public RowComparator(String columnId, SortDir dir) {
221 this.columnId = columnId;
222 this.dir = dir;
223 cellComparator = getComparator(columnId);
224 }
225
226 @Override
227 public int compare(Row a, Row b) {
228 Object cellA = a.get(columnId);
229 Object cellB = b.get(columnId);
230 int result = cellComparator.compare(cellA, cellB);
231 return dir == SortDir.ASC ? result : -result;
232 }
233 }
234
235 /**
Simon Hunte9828152015-05-01 17:54:25 -0700236 * Model of a row.
237 */
238 public class Row {
239 private final Map<String, Object> cells = new HashMap<>();
240
241 /**
242 * Sets the cell value for the given column of this row.
243 *
244 * @param columnId column identifier
245 * @param value value to set
246 * @return self, for chaining
247 */
248 public Row cell(String columnId, Object value) {
Simon Hunte9828152015-05-01 17:54:25 -0700249 checkId(columnId);
250 cells.put(columnId, value);
251 return this;
252 }
253
254 /**
255 * Returns the value of the cell in the given column for this row.
256 *
257 * @param columnId column identifier
258 * @return cell value
259 */
260 public Object get(String columnId) {
261 return cells.get(columnId);
262 }
Simon Hunt3ee7f742015-05-05 10:18:20 -0700263
264 /**
265 * Returns the value of the cell as a string, using the
266 * formatter appropriate for the column.
267 *
268 * @param columnId column identifier
269 * @return formatted cell value
270 */
Simon Hunt3d1b0652015-05-05 17:27:24 -0700271 String getAsString(String columnId) {
Simon Hunt3ee7f742015-05-05 10:18:20 -0700272 return getFormatter(columnId).format(get(columnId));
273 }
Simon Hunt3d1b0652015-05-05 17:27:24 -0700274
275 /**
276 * Returns the row as an array of formatted strings.
277 *
278 * @return the formatted row data
279 */
280 public String[] getAsFormattedStrings() {
281 List<String> formatted = new ArrayList<>(columnCount());
282 for (String c : columnIds) {
283 formatted.add(getAsString(c));
284 }
285 return formatted.toArray(new String[formatted.size()]);
286 }
Simon Hunte9828152015-05-01 17:54:25 -0700287 }
Simon Hunt933b1a82015-05-04 19:07:24 -0700288
289 private static final String DESC = "desc";
290
291 /**
292 * Returns the appropriate sort direction for the given string.
293 * <p>
294 * The expected strings are "asc" for {@link SortDir#ASC ascending} and
295 * "desc" for {@link SortDir#DESC descending}. Any other value will
296 * default to ascending.
297 *
298 * @param s sort direction string encoding
299 * @return sort direction
300 */
301 public static SortDir sortDir(String s) {
302 return !DESC.equals(s) ? SortDir.ASC : SortDir.DESC;
303 }
Simon Hunte9828152015-05-01 17:54:25 -0700304}