/* * Copyright 2015 Open Networking Laboratory * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.onosproject.ui.table; import com.google.common.collect.Sets; import org.onosproject.ui.table.cell.DefaultCellComparator; import org.onosproject.ui.table.cell.DefaultCellFormatter; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; /** * A simple model of table data. *

* Note that this is not a full MVC type model; the expected usage pattern * is to create an empty table, add rows (by consulting the business model), * sort rows (based on client request parameters), and finally produce the * sorted list of rows. *

* The table also provides a mechanism for defining how cell values for a * particular column should be formatted into strings, to help facilitate * the encoding of the table data into a JSON structure. *

* Note that it is expected that all values for a particular column will * be the same class. */ public class TableModel { private static final CellComparator DEF_CMP = DefaultCellComparator.INSTANCE; private static final CellFormatter DEF_FMT = DefaultCellFormatter.INSTANCE; private final String[] columnIds; private final Set idSet; private final Map comparators = new HashMap<>(); private final Map formatters = new HashMap<>(); private final List rows = new ArrayList<>(); /** * Constructs a table (devoid of data) with the given column IDs. * * @param columnIds column identifiers */ public TableModel(String... columnIds) { checkNotNull(columnIds, "columnIds cannot be null"); checkArgument(columnIds.length > 0, "must be at least one column"); idSet = Sets.newHashSet(columnIds); if (idSet.size() != columnIds.length) { throw new IllegalArgumentException("duplicate column ID(s) detected"); } this.columnIds = Arrays.copyOf(columnIds, columnIds.length); } private void checkId(String id) { checkNotNull(id, "must provide a column ID"); if (!idSet.contains(id)) { throw new IllegalArgumentException("unknown column id: " + id); } } /** * Returns the number of rows in this table model. * * @return number of rows */ public int rowCount() { return rows.size(); } /** * Returns the number of columns in this table model. * * @return number of columns */ public int columnCount() { return columnIds.length; } /** * Returns the array of column IDs for this table model. *

* Implementation note: we are knowingly passing you a reference to * our internal array to avoid copying. Don't mess with it. It's your * table you'll break if you do! * * @return the column identifiers */ public String[] getColumnIds() { return columnIds; } /** * Returns the raw {@link Row} representation of the rows in this table. * * @return raw table rows */ public Row[] getRows() { return rows.toArray(new Row[rows.size()]); } /** * Sets a cell comparator for the specified column. * * @param columnId column identifier * @param comparator comparator to use */ public void setComparator(String columnId, CellComparator comparator) { checkNotNull(comparator, "must provide a comparator"); checkId(columnId); comparators.put(columnId, comparator); } /** * Returns the cell comparator to use on values in the specified column. * * @param columnId column identifier * @return an appropriate cell comparator */ private CellComparator getComparator(String columnId) { checkId(columnId); CellComparator cmp = comparators.get(columnId); return cmp == null ? DEF_CMP : cmp; } /** * Sets a cell formatter for the specified column. * * @param columnId column identifier * @param formatter formatter to use */ public void setFormatter(String columnId, CellFormatter formatter) { checkNotNull(formatter, "must provide a formatter"); checkId(columnId); formatters.put(columnId, formatter); } /** * Returns the cell formatter to use on values in the specified column. * * @param columnId column identifier * @return an appropriate cell formatter */ public CellFormatter getFormatter(String columnId) { checkId(columnId); CellFormatter fmt = formatters.get(columnId); return fmt == null ? DEF_FMT : fmt; } /** * Adds a row to the table model. * * @return the row, for chaining */ public Row addRow() { Row r = new Row(); rows.add(r); return r; } /** * Sorts the table rows based on the specified column, in the * specified direction. * * @param columnId column identifier * @param dir sort direction */ public void sort(String columnId, SortDir dir) { Collections.sort(rows, new RowComparator(columnId, dir)); } /** Designates sorting direction. */ public enum SortDir { /** Designates an ascending sort. */ ASC, /** Designates a descending sort. */ DESC } /** * Row comparator. */ private class RowComparator implements Comparator { private final String columnId; private final SortDir dir; private final CellComparator cellComparator; /** * Constructs a row comparator based on the specified * column identifier and sort direction. * * @param columnId column identifier * @param dir sort direction */ public RowComparator(String columnId, SortDir dir) { this.columnId = columnId; this.dir = dir; cellComparator = getComparator(columnId); } @Override public int compare(Row a, Row b) { Object cellA = a.get(columnId); Object cellB = b.get(columnId); int result = cellComparator.compare(cellA, cellB); return dir == SortDir.ASC ? result : -result; } } /** * Model of a row. */ public class Row { private final Map cells = new HashMap<>(); /** * Sets the cell value for the given column of this row. * * @param columnId column identifier * @param value value to set * @return self, for chaining */ public Row cell(String columnId, Object value) { checkId(columnId); cells.put(columnId, value); return this; } /** * Returns the value of the cell in the given column for this row. * * @param columnId column identifier * @return cell value */ public Object get(String columnId) { return cells.get(columnId); } /** * Returns the value of the cell as a string, using the * formatter appropriate for the column. * * @param columnId column identifier * @return formatted cell value */ String getAsString(String columnId) { return getFormatter(columnId).format(get(columnId)); } /** * Returns the row as an array of formatted strings. * * @return the formatted row data */ public String[] getAsFormattedStrings() { List formatted = new ArrayList<>(columnCount()); for (String c : columnIds) { formatted.add(getAsString(c)); } return formatted.toArray(new String[formatted.size()]); } } private static final String DESC = "desc"; /** * Returns the appropriate sort direction for the given string. *

* The expected strings are "asc" for {@link SortDir#ASC ascending} and * "desc" for {@link SortDir#DESC descending}. Any other value will * default to ascending. * * @param s sort direction string encoding * @return sort direction */ public static SortDir sortDir(String s) { return !DESC.equals(s) ? SortDir.ASC : SortDir.DESC; } }