[WIP][ONOS-3722] Augment TableModel with Annotations Mechanism

Change-Id: I815ce0b0fde254dd730153c34905d9454f019d9a
diff --git a/core/api/src/main/java/org/onosproject/ui/table/TableModel.java b/core/api/src/main/java/org/onosproject/ui/table/TableModel.java
index d0fccb6..bf5d95d 100644
--- a/core/api/src/main/java/org/onosproject/ui/table/TableModel.java
+++ b/core/api/src/main/java/org/onosproject/ui/table/TableModel.java
@@ -22,6 +22,7 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
@@ -57,7 +58,7 @@
     private final Map<String, CellComparator> comparators = new HashMap<>();
     private final Map<String, CellFormatter> formatters = new HashMap<>();
     private final List<Row> rows = new ArrayList<>();
-
+    private final Map<String, Annot> annotations = new HashMap<>();
 
     /**
      * Constructs a table (devoid of data) with the given column IDs.
@@ -124,6 +125,28 @@
     }
 
     /**
+     * Inserts a new annotation.
+     *
+     * @param key key of annotation
+     * @param value value of annotation
+     */
+    public void addAnnotation(String key, Object value) {
+        Annot annot = new Annot(key, value);
+        annotations.put(key, annot);
+    }
+
+    /**
+     * Returns the annotations in this table.
+     *
+     * @return annotations
+     */
+    public Collection<Annot> getAnnotations() {
+        Collection<Annot> annots = new ArrayList<>(annotations.size());
+        annotations.forEach((k, v) -> annots.add(v));
+        return annots;
+    }
+
+    /**
      * Sets a cell comparator for the specified column.
      *
      * @param columnId column identifier
@@ -233,6 +256,53 @@
     }
 
     /**
+     * Model of an annotation.
+     */
+    public class Annot {
+        private final String key;
+        private final Object value;
+
+        /**
+         * Constructs an annotation with the given key and value.
+         *
+         * @param key the key
+         * @param value the value
+         */
+        public Annot(String key, Object value) {
+            this.key = key;
+            this.value = value;
+        }
+
+        /**
+         * Returns the annotation's key.
+         *
+         * @return key
+         */
+        public String key() {
+            return key;
+        }
+
+        /**
+         * Returns the annotation's value.
+         *
+         * @return value
+         */
+        public Object value() {
+            return value;
+        }
+
+        /**
+         * Returns the value as a string.
+         * This default implementation uses the value's toString() method.
+         *
+         * @return the value as a string
+         */
+        public String valueAsString() {
+            return value.toString();
+        }
+    }
+
+    /**
      * Model of a row.
      */
     public class Row {
diff --git a/core/api/src/main/java/org/onosproject/ui/table/TableRequestHandler.java b/core/api/src/main/java/org/onosproject/ui/table/TableRequestHandler.java
index b8d4857..43700db 100644
--- a/core/api/src/main/java/org/onosproject/ui/table/TableRequestHandler.java
+++ b/core/api/src/main/java/org/onosproject/ui/table/TableRequestHandler.java
@@ -25,6 +25,8 @@
  */
 public abstract class TableRequestHandler extends RequestHandler {
 
+    private static final String ANNOTS = "annots";
+    private static final String NO_ROWS_MSG_KEY = "no_rows_msg";
     private final String respType;
     private final String nodeName;
 
@@ -53,8 +55,11 @@
         String sortDir = JsonUtils.string(payload, "sortDir", "asc");
         tm.sort(sortCol, TableModel.sortDir(sortDir));
 
+        addTableConfigAnnotations(tm);
+
         ObjectNode rootNode = MAPPER.createObjectNode();
-        rootNode.set(nodeName, TableUtils.generateArrayNode(tm));
+        rootNode.set(nodeName, TableUtils.generateRowArrayNode(tm));
+        rootNode.set(ANNOTS, TableUtils.generateAnnotObjectNode(tm));
         sendMessage(respType, 0, rootNode);
     }
 
@@ -72,6 +77,15 @@
     }
 
     /**
+     * Adds all annotations to table model.
+     *
+     * @param tm a table model
+     */
+    protected void addTableConfigAnnotations(TableModel tm) {
+        tm.addAnnotation(NO_ROWS_MSG_KEY, noRowsMessage());
+    }
+
+    /**
      * Returns the default column ID to be used when one is not supplied in
      * the payload as the column on which to sort.
      * <p>
@@ -92,6 +106,15 @@
     protected abstract String[] getColumnIds();
 
     /**
+     * Subclasses should return the message to display in the table when there
+     * are no rows to display. For example, a host table might return
+     * "No hosts found".
+     *
+     * @return the message
+     */
+    protected abstract String noRowsMessage();
+
+    /**
      * Subclasses should populate the table model by adding
      * {@link TableModel.Row rows}.
      * <pre>
@@ -108,4 +131,4 @@
      * @param payload request payload
      */
     protected abstract void populateTable(TableModel tm, ObjectNode payload);
-}
+}
\ No newline at end of file
diff --git a/core/api/src/main/java/org/onosproject/ui/table/TableUtils.java b/core/api/src/main/java/org/onosproject/ui/table/TableUtils.java
index eb2dff7..14c6743 100644
--- a/core/api/src/main/java/org/onosproject/ui/table/TableUtils.java
+++ b/core/api/src/main/java/org/onosproject/ui/table/TableUtils.java
@@ -32,12 +32,12 @@
     private TableUtils() { }
 
     /**
-     * Generates a JSON array node from a table model.
+     * Generates a JSON array node from the rows of the given table model.
      *
      * @param tm the table model
-     * @return the array node representation
+     * @return the array node representation of rows
      */
-    public static ArrayNode generateArrayNode(TableModel tm) {
+    public static ArrayNode generateRowArrayNode(TableModel tm) {
         ArrayNode array = MAPPER.createArrayNode();
         for (TableModel.Row r : tm.getRows()) {
             array.add(toJsonNode(r, tm));
@@ -45,6 +45,20 @@
         return array;
     }
 
+    /**
+     * Generates a JSON object node from the annotations of the given table model.
+     *
+     * @param tm the table model
+     * @return the object node representation of the annotations
+     */
+    public static ObjectNode generateAnnotObjectNode(TableModel tm) {
+        ObjectNode node = MAPPER.createObjectNode();
+        for (TableModel.Annot a : tm.getAnnotations()) {
+            node.put(a.key(), a.valueAsString());
+        }
+        return node;
+    }
+
     private static JsonNode toJsonNode(TableModel.Row row, TableModel tm) {
         ObjectNode result = MAPPER.createObjectNode();
         String[] keys = tm.getColumnIds();