blob: bf5d95d65c89dbef8fa8ce223466eca94a48d7be [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;
Jian Li69f66632016-01-15 12:27:42 -080025import java.util.Collection;
Simon Hunt933b1a82015-05-04 19:07:24 -070026import java.util.Collections;
27import java.util.Comparator;
Simon Hunte9828152015-05-01 17:54:25 -070028import java.util.HashMap;
29import java.util.List;
30import java.util.Map;
31import java.util.Set;
32
33import static com.google.common.base.Preconditions.checkArgument;
34import static com.google.common.base.Preconditions.checkNotNull;
35
36/**
Simon Hunt933b1a82015-05-04 19:07:24 -070037 * A simple model of table data.
38 * <p>
39 * Note that this is not a full MVC type model; the expected usage pattern
40 * is to create an empty table, add rows (by consulting the business model),
41 * sort rows (based on client request parameters), and finally produce the
42 * sorted list of rows.
43 * <p>
44 * The table also provides a mechanism for defining how cell values for a
45 * particular column should be formatted into strings, to help facilitate
46 * the encoding of the table data into a JSON structure.
Simon Hunt27bf0792015-05-07 10:50:29 -070047 * <p>
48 * Note that it is expected that all values for a particular column will
49 * be the same class.
Simon Hunte9828152015-05-01 17:54:25 -070050 */
51public class TableModel {
52
Simon Hunt3d1b0652015-05-05 17:27:24 -070053 private static final CellComparator DEF_CMP = DefaultCellComparator.INSTANCE;
54 private static final CellFormatter DEF_FMT = DefaultCellFormatter.INSTANCE;
Simon Hunte9828152015-05-01 17:54:25 -070055
56 private final String[] columnIds;
57 private final Set<String> idSet;
Simon Hunt933b1a82015-05-04 19:07:24 -070058 private final Map<String, CellComparator> comparators = new HashMap<>();
Simon Hunte9828152015-05-01 17:54:25 -070059 private final Map<String, CellFormatter> formatters = new HashMap<>();
60 private final List<Row> rows = new ArrayList<>();
Jian Li69f66632016-01-15 12:27:42 -080061 private final Map<String, Annot> annotations = new HashMap<>();
Simon Hunte9828152015-05-01 17:54:25 -070062
63 /**
64 * Constructs a table (devoid of data) with the given column IDs.
65 *
66 * @param columnIds column identifiers
67 */
68 public TableModel(String... columnIds) {
69 checkNotNull(columnIds, "columnIds cannot be null");
70 checkArgument(columnIds.length > 0, "must be at least one column");
71
72 idSet = Sets.newHashSet(columnIds);
73 if (idSet.size() != columnIds.length) {
74 throw new IllegalArgumentException("duplicate column ID(s) detected");
75 }
76
77 this.columnIds = Arrays.copyOf(columnIds, columnIds.length);
78 }
79
80 private void checkId(String id) {
81 checkNotNull(id, "must provide a column ID");
82 if (!idSet.contains(id)) {
83 throw new IllegalArgumentException("unknown column id: " + id);
84 }
85 }
86
87 /**
88 * Returns the number of rows in this table model.
89 *
90 * @return number of rows
91 */
92 public int rowCount() {
93 return rows.size();
94 }
95
96 /**
97 * Returns the number of columns in this table model.
98 *
99 * @return number of columns
100 */
101 public int columnCount() {
102 return columnIds.length;
103 }
104
105 /**
Simon Hunt3d1b0652015-05-05 17:27:24 -0700106 * Returns the array of column IDs for this table model.
107 * <p>
108 * Implementation note: we are knowingly passing you a reference to
109 * our internal array to avoid copying. Don't mess with it. It's your
110 * table you'll break if you do!
Simon Hunte9828152015-05-01 17:54:25 -0700111 *
Simon Hunt3d1b0652015-05-05 17:27:24 -0700112 * @return the column identifiers
Simon Hunte9828152015-05-01 17:54:25 -0700113 */
Simon Hunt3d1b0652015-05-05 17:27:24 -0700114 public String[] getColumnIds() {
115 return columnIds;
Simon Hunte9828152015-05-01 17:54:25 -0700116 }
117
118 /**
119 * Returns the raw {@link Row} representation of the rows in this table.
120 *
121 * @return raw table rows
122 */
123 public Row[] getRows() {
124 return rows.toArray(new Row[rows.size()]);
125 }
126
127 /**
Jian Li69f66632016-01-15 12:27:42 -0800128 * Inserts a new annotation.
129 *
130 * @param key key of annotation
131 * @param value value of annotation
132 */
133 public void addAnnotation(String key, Object value) {
134 Annot annot = new Annot(key, value);
135 annotations.put(key, annot);
136 }
137
138 /**
139 * Returns the annotations in this table.
140 *
141 * @return annotations
142 */
143 public Collection<Annot> getAnnotations() {
144 Collection<Annot> annots = new ArrayList<>(annotations.size());
145 annotations.forEach((k, v) -> annots.add(v));
146 return annots;
147 }
148
149 /**
Simon Hunt933b1a82015-05-04 19:07:24 -0700150 * Sets a cell comparator for the specified column.
151 *
152 * @param columnId column identifier
153 * @param comparator comparator to use
154 */
155 public void setComparator(String columnId, CellComparator comparator) {
156 checkNotNull(comparator, "must provide a comparator");
157 checkId(columnId);
158 comparators.put(columnId, comparator);
159 }
160
161 /**
162 * Returns the cell comparator to use on values in the specified column.
163 *
164 * @param columnId column identifier
165 * @return an appropriate cell comparator
166 */
167 private CellComparator getComparator(String columnId) {
168 checkId(columnId);
169 CellComparator cmp = comparators.get(columnId);
170 return cmp == null ? DEF_CMP : cmp;
171 }
172
173 /**
Simon Hunte9828152015-05-01 17:54:25 -0700174 * Sets a cell formatter for the specified column.
175 *
176 * @param columnId column identifier
177 * @param formatter formatter to use
178 */
179 public void setFormatter(String columnId, CellFormatter formatter) {
180 checkNotNull(formatter, "must provide a formatter");
181 checkId(columnId);
182 formatters.put(columnId, formatter);
183 }
184
185 /**
186 * Returns the cell formatter to use on values in the specified column.
187 *
188 * @param columnId column identifier
189 * @return an appropriate cell formatter
190 */
191 public CellFormatter getFormatter(String columnId) {
192 checkId(columnId);
193 CellFormatter fmt = formatters.get(columnId);
194 return fmt == null ? DEF_FMT : fmt;
195 }
196
197 /**
198 * Adds a row to the table model.
199 *
200 * @return the row, for chaining
201 */
202 public Row addRow() {
203 Row r = new Row();
204 rows.add(r);
205 return r;
206 }
207
208 /**
Simon Hunt933b1a82015-05-04 19:07:24 -0700209 * Sorts the table rows based on the specified column, in the
210 * specified direction.
211 *
212 * @param columnId column identifier
213 * @param dir sort direction
214 */
215 public void sort(String columnId, SortDir dir) {
216 Collections.sort(rows, new RowComparator(columnId, dir));
217 }
218
219
220 /** Designates sorting direction. */
221 public enum SortDir {
222 /** Designates an ascending sort. */
223 ASC,
224 /** Designates a descending sort. */
225 DESC
226 }
227
228 /**
229 * Row comparator.
230 */
231 private class RowComparator implements Comparator<Row> {
232 private final String columnId;
233 private final SortDir dir;
234 private final CellComparator cellComparator;
235
236 /**
237 * Constructs a row comparator based on the specified
238 * column identifier and sort direction.
239 *
240 * @param columnId column identifier
241 * @param dir sort direction
242 */
243 public RowComparator(String columnId, SortDir dir) {
244 this.columnId = columnId;
245 this.dir = dir;
246 cellComparator = getComparator(columnId);
247 }
248
249 @Override
250 public int compare(Row a, Row b) {
251 Object cellA = a.get(columnId);
252 Object cellB = b.get(columnId);
253 int result = cellComparator.compare(cellA, cellB);
254 return dir == SortDir.ASC ? result : -result;
255 }
256 }
257
258 /**
Jian Li69f66632016-01-15 12:27:42 -0800259 * Model of an annotation.
260 */
261 public class Annot {
262 private final String key;
263 private final Object value;
264
265 /**
266 * Constructs an annotation with the given key and value.
267 *
268 * @param key the key
269 * @param value the value
270 */
271 public Annot(String key, Object value) {
272 this.key = key;
273 this.value = value;
274 }
275
276 /**
277 * Returns the annotation's key.
278 *
279 * @return key
280 */
281 public String key() {
282 return key;
283 }
284
285 /**
286 * Returns the annotation's value.
287 *
288 * @return value
289 */
290 public Object value() {
291 return value;
292 }
293
294 /**
295 * Returns the value as a string.
296 * This default implementation uses the value's toString() method.
297 *
298 * @return the value as a string
299 */
300 public String valueAsString() {
301 return value.toString();
302 }
303 }
304
305 /**
Simon Hunte9828152015-05-01 17:54:25 -0700306 * Model of a row.
307 */
308 public class Row {
309 private final Map<String, Object> cells = new HashMap<>();
310
311 /**
312 * Sets the cell value for the given column of this row.
313 *
314 * @param columnId column identifier
315 * @param value value to set
316 * @return self, for chaining
317 */
318 public Row cell(String columnId, Object value) {
Simon Hunte9828152015-05-01 17:54:25 -0700319 checkId(columnId);
320 cells.put(columnId, value);
321 return this;
322 }
323
324 /**
325 * Returns the value of the cell in the given column for this row.
326 *
327 * @param columnId column identifier
328 * @return cell value
329 */
330 public Object get(String columnId) {
331 return cells.get(columnId);
332 }
Simon Hunt3ee7f742015-05-05 10:18:20 -0700333
334 /**
335 * Returns the value of the cell as a string, using the
336 * formatter appropriate for the column.
337 *
338 * @param columnId column identifier
339 * @return formatted cell value
340 */
Simon Hunt3d1b0652015-05-05 17:27:24 -0700341 String getAsString(String columnId) {
Simon Hunt3ee7f742015-05-05 10:18:20 -0700342 return getFormatter(columnId).format(get(columnId));
343 }
Simon Hunt3d1b0652015-05-05 17:27:24 -0700344
345 /**
346 * Returns the row as an array of formatted strings.
347 *
348 * @return the formatted row data
349 */
350 public String[] getAsFormattedStrings() {
351 List<String> formatted = new ArrayList<>(columnCount());
352 for (String c : columnIds) {
353 formatted.add(getAsString(c));
354 }
355 return formatted.toArray(new String[formatted.size()]);
356 }
Simon Hunte9828152015-05-01 17:54:25 -0700357 }
Simon Hunt933b1a82015-05-04 19:07:24 -0700358
359 private static final String DESC = "desc";
360
361 /**
362 * Returns the appropriate sort direction for the given string.
363 * <p>
364 * The expected strings are "asc" for {@link SortDir#ASC ascending} and
365 * "desc" for {@link SortDir#DESC descending}. Any other value will
366 * default to ascending.
367 *
368 * @param s sort direction string encoding
369 * @return sort direction
370 */
371 public static SortDir sortDir(String s) {
372 return !DESC.equals(s) ? SortDir.ASC : SortDir.DESC;
373 }
Simon Hunte9828152015-05-01 17:54:25 -0700374}