GUI -- added getAsString() to TableModel.Row.
- Moved cell-related classes to new package.
- Created base classes for cell comparator and formatter.
- Created IntComparator and HexFormatter.

Change-Id: I3861bf3e0ca738a2d6eab9c70174db53f22960f8
diff --git a/core/api/src/main/java/org/onosproject/ui/table/CellFormatter.java b/core/api/src/main/java/org/onosproject/ui/table/CellFormatter.java
index 49a316d..854ac27 100644
--- a/core/api/src/main/java/org/onosproject/ui/table/CellFormatter.java
+++ b/core/api/src/main/java/org/onosproject/ui/table/CellFormatter.java
@@ -24,7 +24,8 @@
 
     /**
      * Formats the specified value into a string appropriate for displaying
-     * in a table cell.
+     * in a table cell. Note that null values are acceptable, and will result
+     * in the empty string.
      *
      * @param value the value
      * @return the formatted string
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 7fb336e..2c3ffc5 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
@@ -17,6 +17,8 @@
 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;
@@ -254,6 +256,17 @@
         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
+         */
+        public String getAsString(String columnId) {
+            return getFormatter(columnId).format(get(columnId));
+        }
     }
 
     private static final String DESC = "desc";
diff --git a/core/api/src/main/java/org/onosproject/ui/table/cell/AbstractCellComparator.java b/core/api/src/main/java/org/onosproject/ui/table/cell/AbstractCellComparator.java
new file mode 100644
index 0000000..6113fc3
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/ui/table/cell/AbstractCellComparator.java
@@ -0,0 +1,61 @@
+/*
+ * 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.cell;
+
+import org.onosproject.ui.table.CellComparator;
+
+/**
+ * Base implementation of a {@link CellComparator}. This class takes care
+ * of dealing with null inputs; subclasses should implement their comparison
+ * knowing that both inputs are guaranteed to be non-null.
+ */
+public abstract class AbstractCellComparator implements CellComparator {
+
+    @Override
+    public int compare(Object o1, Object o2) {
+        if (o1 == null && o2 == null) {
+            return 0;       // o1 == o2
+        }
+        if (o1 == null) {
+            return -1;      // o1 < o2
+        }
+        if (o2 == null) {
+            return 1;       // o1 > o2
+        }
+        return nonNullCompare(o1, o2);
+    }
+
+    /**
+     * Compares its two arguments for order.  Returns a negative integer,
+     * zero, or a positive integer as the first argument is less than, equal
+     * to, or greater than the second.<p>
+     *
+     * Note that both objects are guaranteed to be non-null.
+     *
+     * @see java.util.Comparator#compare(Object, Object)
+     *
+     * @param o1 the first object to be compared.
+     * @param o2 the second object to be compared.
+     * @return a negative integer, zero, or a positive integer as the
+     *         first argument is less than, equal to, or greater than the
+     *         second.
+     * @throws ClassCastException if the arguments' types prevent them from
+     *         being compared by this comparator.
+     */
+    protected abstract int nonNullCompare(Object o1, Object o2);
+}
diff --git a/core/api/src/main/java/org/onosproject/ui/table/cell/AbstractCellFormatter.java b/core/api/src/main/java/org/onosproject/ui/table/cell/AbstractCellFormatter.java
new file mode 100644
index 0000000..33ce2ab
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/ui/table/cell/AbstractCellFormatter.java
@@ -0,0 +1,42 @@
+/*
+ * 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.cell;
+
+import org.onosproject.ui.table.CellFormatter;
+
+/**
+ * Base implementation of a {@link CellFormatter}. This class takes care of
+ * dealing with null inputs; subclasses should implement their format method
+ * knowing that the input is guaranteed to be non-null.
+ */
+public abstract class AbstractCellFormatter implements CellFormatter {
+
+    @Override
+    public String format(Object value) {
+        return value == null ? "" : nonNullFormat(value);
+    }
+
+    /**
+     * Formats the specified value into a string appropriate for displaying
+     * in a table cell. Note that value is guaranteed to be non-null.
+     *
+     * @param value the value
+     * @return the formatted string
+     */
+    protected abstract String nonNullFormat(Object value);
+}
diff --git a/core/api/src/main/java/org/onosproject/ui/table/DefaultCellComparator.java b/core/api/src/main/java/org/onosproject/ui/table/cell/DefaultCellComparator.java
similarity index 71%
rename from core/api/src/main/java/org/onosproject/ui/table/DefaultCellComparator.java
rename to core/api/src/main/java/org/onosproject/ui/table/cell/DefaultCellComparator.java
index c27be61..7846f89 100644
--- a/core/api/src/main/java/org/onosproject/ui/table/DefaultCellComparator.java
+++ b/core/api/src/main/java/org/onosproject/ui/table/cell/DefaultCellComparator.java
@@ -15,7 +15,7 @@
  *
  */
 
-package org.onosproject.ui.table;
+package org.onosproject.ui.table.cell;
 
 /**
  * A default cell comparator. Implements a lexicographical compare function
@@ -23,18 +23,9 @@
  * compares the resulting strings. Note that null values are acceptable and
  * are considered "smaller" than any non-null value.
  */
-public class DefaultCellComparator implements CellComparator {
+public class DefaultCellComparator extends AbstractCellComparator {
     @Override
-    public int compare(Object o1, Object o2) {
-        if (o1 == null && o2 == null) {
-            return 0;       // o1 == o2
-        }
-        if (o1 == null) {
-            return -1;      // o1 < o2
-        }
-        if (o2 == null) {
-            return 1;       // o1 > o2
-        }
+    protected int nonNullCompare(Object o1, Object o2) {
         return o1.toString().compareTo(o2.toString());
     }
 }
diff --git a/core/api/src/main/java/org/onosproject/ui/table/DefaultCellFormatter.java b/core/api/src/main/java/org/onosproject/ui/table/cell/DefaultCellFormatter.java
similarity index 78%
rename from core/api/src/main/java/org/onosproject/ui/table/DefaultCellFormatter.java
rename to core/api/src/main/java/org/onosproject/ui/table/cell/DefaultCellFormatter.java
index 4078673..c030236 100644
--- a/core/api/src/main/java/org/onosproject/ui/table/DefaultCellFormatter.java
+++ b/core/api/src/main/java/org/onosproject/ui/table/cell/DefaultCellFormatter.java
@@ -15,14 +15,14 @@
  *
  */
 
-package org.onosproject.ui.table;
+package org.onosproject.ui.table.cell;
 
 /**
  * A default cell formatter. Uses the object's toString() method.
  */
-public class DefaultCellFormatter implements CellFormatter {
+public class DefaultCellFormatter extends AbstractCellFormatter {
     @Override
-    public String format(Object value) {
-        return value == null ? "" : value.toString();
+    public String nonNullFormat(Object value) {
+        return value.toString();
     }
 }
diff --git a/core/api/src/main/java/org/onosproject/ui/table/DefaultCellFormatter.java b/core/api/src/main/java/org/onosproject/ui/table/cell/HexFormatter.java
similarity index 71%
copy from core/api/src/main/java/org/onosproject/ui/table/DefaultCellFormatter.java
copy to core/api/src/main/java/org/onosproject/ui/table/cell/HexFormatter.java
index 4078673..d4aaade 100644
--- a/core/api/src/main/java/org/onosproject/ui/table/DefaultCellFormatter.java
+++ b/core/api/src/main/java/org/onosproject/ui/table/cell/HexFormatter.java
@@ -15,14 +15,14 @@
  *
  */
 
-package org.onosproject.ui.table;
+package org.onosproject.ui.table.cell;
 
 /**
- * A default cell formatter. Uses the object's toString() method.
+ * Formats integer values as hex strings.
  */
-public class DefaultCellFormatter implements CellFormatter {
+public class HexFormatter extends AbstractCellFormatter {
     @Override
-    public String format(Object value) {
-        return value == null ? "" : value.toString();
+    protected String nonNullFormat(Object value) {
+        return "0x" + Integer.toHexString((Integer) value);
     }
 }
diff --git a/core/api/src/main/java/org/onosproject/ui/table/DefaultCellFormatter.java b/core/api/src/main/java/org/onosproject/ui/table/cell/IntComparator.java
similarity index 65%
copy from core/api/src/main/java/org/onosproject/ui/table/DefaultCellFormatter.java
copy to core/api/src/main/java/org/onosproject/ui/table/cell/IntComparator.java
index 4078673..c399d53 100644
--- a/core/api/src/main/java/org/onosproject/ui/table/DefaultCellFormatter.java
+++ b/core/api/src/main/java/org/onosproject/ui/table/cell/IntComparator.java
@@ -15,14 +15,16 @@
  *
  */
 
-package org.onosproject.ui.table;
+package org.onosproject.ui.table.cell;
 
 /**
- * A default cell formatter. Uses the object's toString() method.
+ * An integer-based cell comparator.
+ * Note that null values are acceptable and are considered "smaller" than
+ * any non-null value.
  */
-public class DefaultCellFormatter implements CellFormatter {
+public class IntComparator extends AbstractCellComparator {
     @Override
-    public String format(Object value) {
-        return value == null ? "" : value.toString();
+    protected int nonNullCompare(Object o1, Object o2) {
+        return ((int) o1) - ((int) o2);
     }
 }
diff --git a/core/api/src/test/java/org/onosproject/ui/table/TableModelTest.java b/core/api/src/test/java/org/onosproject/ui/table/TableModelTest.java
index 33b8fb0..40a9672 100644
--- a/core/api/src/test/java/org/onosproject/ui/table/TableModelTest.java
+++ b/core/api/src/test/java/org/onosproject/ui/table/TableModelTest.java
@@ -18,6 +18,9 @@
 
 import org.junit.Test;
 import org.onosproject.ui.table.TableModel.SortDir;
+import org.onosproject.ui.table.cell.DefaultCellFormatter;
+import org.onosproject.ui.table.cell.HexFormatter;
+import org.onosproject.ui.table.cell.IntComparator;
 
 import static org.junit.Assert.*;
 
@@ -26,22 +29,13 @@
  */
 public class TableModelTest {
 
-    private static final String UNEX_SORT_ORDER = "unexpected sort: index ";
+    private static final String UNEX_SORT = "unexpected sort: index ";
 
     private static final String FOO = "foo";
     private static final String BAR = "bar";
     private static final String ZOO = "zoo";
 
-    private static class TestCmpr implements CellComparator {
-        @Override
-        public int compare(Object o1, Object o2) {
-            int i1 = (int) o1;
-            int i2 = (int) o2;
-            return i1 - i2;
-        }
-    }
-
-    private static class TestFmtr implements CellFormatter {
+    private static class ParenFormatter implements CellFormatter {
         @Override
         public String format(Object value) {
             return "(" + value + ")";
@@ -95,14 +89,14 @@
     @Test
     public void altFormatter() {
         tm = new TableModel(FOO, BAR);
-        tm.setFormatter(BAR, new TestFmtr());
+        tm.setFormatter(BAR, new ParenFormatter());
 
         fmt = tm.getFormatter(FOO);
         assertTrue("Wrong formatter", fmt instanceof DefaultCellFormatter);
         assertEquals("Wrong result", "2", fmt.format(2));
 
         fmt = tm.getFormatter(BAR);
-        assertTrue("Wrong formatter", fmt instanceof TestFmtr);
+        assertTrue("Wrong formatter", fmt instanceof ParenFormatter);
         assertEquals("Wrong result", "(2)", fmt.format(2));
     }
 
@@ -174,6 +168,10 @@
         1, 2, 3, 4, 11, 12, 20, 30
     };
 
+    private static final String[] SORTED_HEX = {
+        "0x1", "0x2", "0x3", "0x4", "0xb", "0xc", "0x14", "0x1e"
+    };
+
     @Test
     public void verifyTestData() {
         // not a unit test per se, but will fail if we don't keep
@@ -206,7 +204,7 @@
         int nr = rows.length;
         assertEquals("row count", NAMES.length, nr);
         for (int i = 0; i < nr; i++) {
-            assertEquals(UNEX_SORT_ORDER + i, SORTED_NAMES[i], rows[i].get(FOO));
+            assertEquals(UNEX_SORT + i, SORTED_NAMES[i], rows[i].get(FOO));
         }
 
         // now the other way
@@ -217,7 +215,7 @@
         nr = rows.length;
         assertEquals("row count", NAMES.length, nr);
         for (int i = 0; i < nr; i++) {
-            assertEquals(UNEX_SORT_ORDER + i,
+            assertEquals(UNEX_SORT + i,
                          SORTED_NAMES[nr - 1 - i], rows[i].get(FOO));
         }
     }
@@ -227,7 +225,7 @@
         initUnsortedTable();
 
         // first, tell the table to use an integer-based comparator
-        tm.setComparator(BAR, new TestCmpr());
+        tm.setComparator(BAR, new IntComparator());
 
         // sort by number
         tm.sort(BAR, SortDir.ASC);
@@ -237,7 +235,7 @@
         int nr = rows.length;
         assertEquals("row count", NUMBERS.length, nr);
         for (int i = 0; i < nr; i++) {
-            assertEquals(UNEX_SORT_ORDER + i, SORTED_NUMBERS[i], rows[i].get(BAR));
+            assertEquals(UNEX_SORT + i, SORTED_NUMBERS[i], rows[i].get(BAR));
         }
 
         // now the other way
@@ -248,12 +246,33 @@
         nr = rows.length;
         assertEquals("row count", NUMBERS.length, nr);
         for (int i = 0; i < nr; i++) {
-            assertEquals(UNEX_SORT_ORDER + i,
+            assertEquals(UNEX_SORT + i,
                          SORTED_NUMBERS[nr - 1 - i], rows[i].get(BAR));
         }
     }
 
     @Test
+    public void sortAndFormat() {
+        initUnsortedTable();
+
+        // set integer-based comparator and hex formatter
+        tm.setComparator(BAR, new IntComparator());
+        tm.setFormatter(BAR, new HexFormatter());
+
+        // sort by number
+        tm.sort(BAR, SortDir.ASC);
+
+        // verify results
+        rows = tm.getRows();
+        int nr = rows.length;
+        assertEquals("row count", SORTED_HEX.length, nr);
+        for (int i = 0; i < nr; i++) {
+            assertEquals(UNEX_SORT + i, SORTED_HEX[i], rows[i].getAsString(BAR));
+        }
+    }
+
+
+    @Test
     public void sortDirAsc() {
         assertEquals("asc sort dir", SortDir.ASC, TableModel.sortDir("asc"));
     }
diff --git a/core/api/src/test/java/org/onosproject/ui/table/DefaultCellComparatorTest.java b/core/api/src/test/java/org/onosproject/ui/table/cell/DefaultCellComparatorTest.java
similarity index 95%
rename from core/api/src/test/java/org/onosproject/ui/table/DefaultCellComparatorTest.java
rename to core/api/src/test/java/org/onosproject/ui/table/cell/DefaultCellComparatorTest.java
index 86add93..d4cd8ed 100644
--- a/core/api/src/test/java/org/onosproject/ui/table/DefaultCellComparatorTest.java
+++ b/core/api/src/test/java/org/onosproject/ui/table/cell/DefaultCellComparatorTest.java
@@ -15,9 +15,10 @@
  *
  */
 
-package org.onosproject.ui.table;
+package org.onosproject.ui.table.cell;
 
 import org.junit.Test;
+import org.onosproject.ui.table.CellComparator;
 
 import static org.junit.Assert.assertTrue;
 
diff --git a/core/api/src/test/java/org/onosproject/ui/table/DefaultCellFormatterTest.java b/core/api/src/test/java/org/onosproject/ui/table/cell/DefaultCellFormatterTest.java
similarity index 95%
rename from core/api/src/test/java/org/onosproject/ui/table/DefaultCellFormatterTest.java
rename to core/api/src/test/java/org/onosproject/ui/table/cell/DefaultCellFormatterTest.java
index bbb0104..8934f48 100644
--- a/core/api/src/test/java/org/onosproject/ui/table/DefaultCellFormatterTest.java
+++ b/core/api/src/test/java/org/onosproject/ui/table/cell/DefaultCellFormatterTest.java
@@ -15,9 +15,10 @@
  *
  */
 
-package org.onosproject.ui.table;
+package org.onosproject.ui.table.cell;
 
 import org.junit.Test;
+import org.onosproject.ui.table.CellFormatter;
 
 import static org.junit.Assert.assertEquals;
 
diff --git a/core/api/src/test/java/org/onosproject/ui/table/cell/HexFormatterTest.java b/core/api/src/test/java/org/onosproject/ui/table/cell/HexFormatterTest.java
new file mode 100644
index 0000000..738fbdd
--- /dev/null
+++ b/core/api/src/test/java/org/onosproject/ui/table/cell/HexFormatterTest.java
@@ -0,0 +1,56 @@
+/*
+ * 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.cell;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Unit tests for {@link HexFormatter}.
+ */
+public class HexFormatterTest {
+
+    private HexFormatter fmt = new HexFormatter();
+
+    @Test
+    public void nullValue() {
+        assertEquals("null value", "", fmt.format(null));
+    }
+
+    @Test
+    public void zero() {
+        assertEquals("zero", "0x0", fmt.format(0));
+    }
+
+    @Test
+    public void one() {
+        assertEquals("one", "0x1", fmt.format(1));
+    }
+
+    @Test
+    public void ten() {
+        assertEquals("ten", "0xa", fmt.format(10));
+    }
+
+    @Test
+    public void twenty() {
+        assertEquals("twenty", "0x14", fmt.format(20));
+    }
+
+}
diff --git a/core/api/src/test/java/org/onosproject/ui/table/cell/IntComparatorTest.java b/core/api/src/test/java/org/onosproject/ui/table/cell/IntComparatorTest.java
new file mode 100644
index 0000000..2b45b3f
--- /dev/null
+++ b/core/api/src/test/java/org/onosproject/ui/table/cell/IntComparatorTest.java
@@ -0,0 +1,72 @@
+/*
+ * 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.cell;
+
+import org.junit.Test;
+import org.onosproject.ui.table.CellComparator;
+
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Unit tests for {@link IntComparator}.
+ */
+public class IntComparatorTest {
+
+    private CellComparator cmp = new IntComparator();
+
+    @Test
+    public void twoNulls() {
+        assertTrue("two nulls", cmp.compare(null, null) == 0);
+    }
+
+    @Test
+    public void nullVsNegValue() {
+        assertTrue("null vs neg value", cmp.compare(null, -5) < 0);
+    }
+
+    @Test
+    public void nullVsPosValue() {
+        assertTrue("null vs pos value", cmp.compare(null, 5) < 0);
+    }
+
+    @Test
+    public void negValueVsNull() {
+        assertTrue("neg value vs null", cmp.compare(-5, null) > 0);
+    }
+
+    @Test
+    public void posValueVsNull() {
+        assertTrue("pos value vs null", cmp.compare(5, null) > 0);
+    }
+
+
+    @Test
+    public void smallVsBig() {
+        assertTrue("small vs big", cmp.compare(25, 75) < 0);
+    }
+
+    @Test
+    public void bigVsSmall() {
+        assertTrue("big vs small", cmp.compare(75, 25) > 0);
+    }
+
+    @Test
+    public void sameValue() {
+        assertTrue("same value", cmp.compare(50, 50) == 0);
+    }
+}