ONOS-3755: use thousand separator for packet and byte counts, etc. Fix alignment (numbers right justified).

Change-Id: Idb407fb16a82d5e3fb6fd10a6599b263a777deb2
diff --git a/core/api/src/main/java/org/onosproject/ui/table/cell/NumberFormatter.java b/core/api/src/main/java/org/onosproject/ui/table/cell/NumberFormatter.java
index 76f4246..d1f9fab 100644
--- a/core/api/src/main/java/org/onosproject/ui/table/cell/NumberFormatter.java
+++ b/core/api/src/main/java/org/onosproject/ui/table/cell/NumberFormatter.java
@@ -16,6 +16,8 @@
 
 package org.onosproject.ui.table.cell;
 
+import org.onosproject.ui.table.CellFormatter;
+
 import java.text.DecimalFormat;
 import java.text.NumberFormat;
 
@@ -24,17 +26,35 @@
  */
 public final class NumberFormatter extends AbstractCellFormatter {
 
+    private static final String FMT_INTEGER = "#,##0";
+    private static final String FMT_5DP = "#,##0.00000";
+
+
     private final NumberFormat format;
 
     /**
-     * Creates a formatter using a default decimal format.
+     * Creates a formatter using the default format (no decimal places).
+     * For example
+     * <pre>
+     *     12345 formatted as "12,345"
+     * </pre>
      */
     public NumberFormatter() {
-        this(new DecimalFormat("#,##0.00000"));
+        this(FMT_INTEGER);
     }
 
     /**
-     * Creates a formatter using the specified format.
+     * Creates a formatter using a {@link DecimalFormat} configured with the
+     * given format string.
+     *
+     * @param decimalFormat the format string
+     */
+    public NumberFormatter(String decimalFormat) {
+        this(new DecimalFormat(decimalFormat));
+    }
+
+    /**
+     * Creates a formatter using the specified {@link NumberFormat}.
      *
      * @param format number format
      */
@@ -47,4 +67,23 @@
         return format.format(value);
     }
 
+    /**
+     * An instance of this class that formats as integers (no decimal places).
+     * For example
+     * <pre>
+     *     12345 formatted as "12,345"
+     * </pre>
+     */
+    public static final CellFormatter INTEGER = new NumberFormatter();
+
+    /**
+     * An instance of this class that formats to 5 decimal places.
+     * For example
+     * <pre>
+     *     12.3 formatted as "12.30000"
+     *     1234 formatted as "1,234.00000"
+     * </pre>
+     */
+    public static final CellFormatter TO_5DP = new NumberFormatter(FMT_5DP);
+
 }
diff --git a/core/api/src/test/java/org/onosproject/ui/table/cell/NumberFormatterTest.java b/core/api/src/test/java/org/onosproject/ui/table/cell/NumberFormatterTest.java
new file mode 100644
index 0000000..2432977
--- /dev/null
+++ b/core/api/src/test/java/org/onosproject/ui/table/cell/NumberFormatterTest.java
@@ -0,0 +1,78 @@
+/*
+ *  Copyright 2016 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.CellFormatter;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Unit tests for {@link NumberFormatter}.
+ */
+public class NumberFormatterTest {
+
+
+    private CellFormatter f5dp = NumberFormatter.TO_5DP;
+    private CellFormatter fInt = NumberFormatter.INTEGER;
+
+    @Test
+    public void defaultNullValue() {
+        assertEquals("default null value", "", f5dp.format(null));
+    }
+
+    @Test
+    public void defaultZero() {
+        assertEquals("default zero", "0.00000", f5dp.format(0));
+    }
+
+    @Test
+    public void defaultFifty() {
+        assertEquals("default fifty", "50.00000", f5dp.format(50));
+    }
+
+    @Test
+    public void default2G() {
+        assertEquals("default 2G", "2,000.00000", f5dp.format(2000));
+    }
+
+    @Test
+    public void integerNullValue() {
+        assertEquals("integer null value", "", fInt.format(null));
+    }
+
+    @Test
+    public void integerZero() {
+        assertEquals("integer zero", "0", fInt.format(0));
+    }
+
+    @Test
+    public void integerFifty() {
+        assertEquals("integer fifty", "50", fInt.format(50));
+    }
+
+    @Test
+    public void integer2G() {
+        assertEquals("integer 2G", "2,000", fInt.format(2000));
+    }
+
+    @Test
+    public void integer5M() {
+        assertEquals("integer 5M", "5,000,042", fInt.format(5000042));
+    }
+
+}
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/FlowViewMessageHandler.java b/web/gui/src/main/java/org/onosproject/ui/impl/FlowViewMessageHandler.java
index 44bd78d..e7a29dd 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/FlowViewMessageHandler.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/FlowViewMessageHandler.java
@@ -32,6 +32,7 @@
 import org.onosproject.ui.table.cell.EnumFormatter;
 import org.onosproject.ui.table.cell.HexFormatter;
 import org.onosproject.ui.table.cell.HexLongFormatter;
+import org.onosproject.ui.table.cell.NumberFormatter;
 
 import java.util.Collection;
 import java.util.List;
@@ -88,9 +89,11 @@
             TableModel tm = super.createTableModel();
             tm.setFormatter(ID, HexLongFormatter.INSTANCE);
             tm.setFormatter(GROUP_ID, HexFormatter.INSTANCE);
+            tm.setFormatter(STATE, EnumFormatter.INSTANCE);
+            tm.setFormatter(PACKETS, NumberFormatter.INTEGER);
+            tm.setFormatter(BYTES, NumberFormatter.INTEGER);
             tm.setFormatter(SELECTOR, new SelectorFormatter());
             tm.setFormatter(TREATMENT, new TreatmentFormatter());
-            tm.setFormatter(STATE, EnumFormatter.INSTANCE);
             return tm;
         }
 
@@ -112,13 +115,13 @@
                 .cell(GROUP_ID, flow.groupId().id())
                 .cell(TABLE_ID, flow.tableId())
                 .cell(PRIORITY, flow.priority())
-                .cell(SELECTOR, flow)
-                .cell(TREATMENT, flow)
                 .cell(TIMEOUT, flow.timeout())
                 .cell(PERMANENT, flow.isPermanent())
                 .cell(STATE, flow.state())
                 .cell(PACKETS, flow.packets())
-                .cell(BYTES, flow.bytes());
+                .cell(BYTES, flow.bytes())
+                .cell(SELECTOR, flow)
+                .cell(TREATMENT, flow);
         }
 
         private final class SelectorFormatter implements CellFormatter {
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/GroupViewMessageHandler.java b/web/gui/src/main/java/org/onosproject/ui/impl/GroupViewMessageHandler.java
index c78d32f..5780e9f 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/GroupViewMessageHandler.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/GroupViewMessageHandler.java
@@ -30,6 +30,7 @@
 import org.onosproject.ui.table.TableRequestHandler;
 import org.onosproject.ui.table.cell.EnumFormatter;
 import org.onosproject.ui.table.cell.HexFormatter;
+import org.onosproject.ui.table.cell.NumberFormatter;
 
 import java.util.Collection;
 import java.util.List;
@@ -78,6 +79,8 @@
             TableModel tm = super.createTableModel();
             tm.setFormatter(ID, HexFormatter.INSTANCE);
             tm.setFormatter(TYPE, EnumFormatter.INSTANCE);
+            tm.setFormatter(PACKETS, NumberFormatter.INTEGER);
+            tm.setFormatter(BYTES, NumberFormatter.INTEGER);
             tm.setFormatter(BUCKETS, new BucketFormatter());
             return tm;
         }
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/MeterViewMessageHandler.java b/web/gui/src/main/java/org/onosproject/ui/impl/MeterViewMessageHandler.java
index 204fe8f..f48fc81 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/MeterViewMessageHandler.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/MeterViewMessageHandler.java
@@ -28,6 +28,8 @@
 import org.onosproject.ui.table.CellFormatter;
 import org.onosproject.ui.table.TableModel;
 import org.onosproject.ui.table.TableRequestHandler;
+import org.onosproject.ui.table.cell.HexLongFormatter;
+import org.onosproject.ui.table.cell.NumberFormatter;
 
 import java.util.Collection;
 
@@ -71,6 +73,9 @@
         @Override
         protected TableModel createTableModel() {
             TableModel tm = super.createTableModel();
+            tm.setFormatter(ID, HexLongFormatter.INSTANCE);
+            tm.setFormatter(PACKETS, NumberFormatter.INTEGER);
+            tm.setFormatter(BYTES, NumberFormatter.INTEGER);
             tm.setFormatter(BANDS, new BandFormatter());
             return tm;
         }
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/PortViewMessageHandler.java b/web/gui/src/main/java/org/onosproject/ui/impl/PortViewMessageHandler.java
index 92ebcf7..9c2f211 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/PortViewMessageHandler.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/PortViewMessageHandler.java
@@ -26,6 +26,7 @@
 import org.onosproject.ui.UiMessageHandler;
 import org.onosproject.ui.table.TableModel;
 import org.onosproject.ui.table.TableRequestHandler;
+import org.onosproject.ui.table.cell.NumberFormatter;
 
 import java.util.Collection;
 
@@ -71,6 +72,18 @@
         }
 
         @Override
+        protected TableModel createTableModel() {
+            TableModel tm = super.createTableModel();
+            tm.setFormatter(PKT_RX, NumberFormatter.INTEGER);
+            tm.setFormatter(PKT_TX, NumberFormatter.INTEGER);
+            tm.setFormatter(BYTES_RX, NumberFormatter.INTEGER);
+            tm.setFormatter(BYTES_TX, NumberFormatter.INTEGER);
+            tm.setFormatter(PKT_RX_DRP, NumberFormatter.INTEGER);
+            tm.setFormatter(PKT_TX_DRP, NumberFormatter.INTEGER);
+            return tm;
+        }
+
+        @Override
         protected void populateTable(TableModel tm, ObjectNode payload) {
             String uri = string(payload, "devId");
             if (!Strings.isNullOrEmpty(uri)) {
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/ProcessorViewMessageHandler.java b/web/gui/src/main/java/org/onosproject/ui/impl/ProcessorViewMessageHandler.java
index 5d97504..00b221b 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/ProcessorViewMessageHandler.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/ProcessorViewMessageHandler.java
@@ -76,7 +76,7 @@
         @Override
         protected TableModel createTableModel() {
             TableModel tm = super.createTableModel();
-            tm.setFormatter(AVG_MS, new NumberFormatter());
+            tm.setFormatter(AVG_MS, NumberFormatter.TO_5DP);
             return tm;
         }
 
diff --git a/web/gui/src/main/webapp/app/view/flow/flow.css b/web/gui/src/main/webapp/app/view/flow/flow.css
index 3022622..a7867d4 100644
--- a/web/gui/src/main/webapp/app/view/flow/flow.css
+++ b/web/gui/src/main/webapp/app/view/flow/flow.css
@@ -78,8 +78,15 @@
     background-color: #5A5600;
 }
 
+#ov-flow td {
+    text-align: center;
+}
+#ov-flow td.right {
+    text-align: right;
+}
 #ov-flow td.selector,
 #ov-flow td.treatment {
+    text-align: left;
     padding-left: 36px;
     opacity: 0.65;
 }
\ No newline at end of file
diff --git a/web/gui/src/main/webapp/app/view/flow/flow.html b/web/gui/src/main/webapp/app/view/flow/flow.html
index 374da0b..796a563 100644
--- a/web/gui/src/main/webapp/app/view/flow/flow.html
+++ b/web/gui/src/main/webapp/app/view/flow/flow.html
@@ -50,8 +50,8 @@
                     <td colId="timeout" sortable>Timeout </td>
                     <td colId="permanent" sortable>Permanent </td>
                     <td colId="state" sortable>State </td>
-                    <td colId="packets" sortable>Packets </td>
-                    <td colId="bytes" sortable>Bytes </td>
+                    <td class="right" colId="packets" sortable>Packets </td>
+                    <td class="right" colId="bytes" sortable>Bytes </td>
                 </tr>
             </table>
         </div>
@@ -74,8 +74,8 @@
                     <td>{{flow.timeout}}</td>
                     <td>{{flow.permanent}}</td>
                     <td>{{flow.state}}</td>
-                    <td>{{flow.packets}}</td>
-                    <td>{{flow.bytes}}</td>
+                    <td class="right">{{flow.packets}}</td>
+                    <td class="right">{{flow.bytes}}</td>
                 </tr>
                 <tr row-id="{{flow.id}}">
                     <td class="selector" colspan="10">{{flow.selector}}</td>
diff --git a/web/gui/src/main/webapp/app/view/group/group.css b/web/gui/src/main/webapp/app/view/group/group.css
index 6773aa8..a09345c 100644
--- a/web/gui/src/main/webapp/app/view/group/group.css
+++ b/web/gui/src/main/webapp/app/view/group/group.css
@@ -70,7 +70,14 @@
     background-color: #5A5600;
 }
 
+#ov-group td {
+    text-align: center;
+}
+#ov-group td.right {
+    text-align: right;
+}
 #ov-group td.buckets {
+    text-align: left;
     padding-left: 36px;
     opacity: 0.65;
 }
diff --git a/web/gui/src/main/webapp/app/view/group/group.html b/web/gui/src/main/webapp/app/view/group/group.html
index 9f4a70e..df5afeb 100644
--- a/web/gui/src/main/webapp/app/view/group/group.html
+++ b/web/gui/src/main/webapp/app/view/group/group.html
@@ -46,8 +46,8 @@
                     <td colId="app_id" sortable>App ID </td>
                     <td colId="state" sortable>State </td>
                     <td colId="type" sortable>Type </td>
-                    <td colId="packets" sortable>Packets </td>
-                    <td colId="bytes" sortable>Bytes </td>
+                    <td class="right" colId="packets" sortable>Packets </td>
+                    <td class="right" colId="bytes" sortable>Bytes </td>
                 </tr>
             </table>
         </div>
@@ -66,8 +66,8 @@
                     <td>{{group.app_id}}</td>
                     <td>{{group.state}}</td>
                     <td>{{group.type}}</td>
-                    <td>{{group.packets}}</td>
-                    <td>{{group.bytes}}</td>
+                    <td class="right">{{group.packets}}</td>
+                    <td class="right">{{group.bytes}}</td>
                 </tr>
                 <tr row-id="{{group.id}}" ng-repeat-end>
                     <td class="buckets" colspan="6"
diff --git a/web/gui/src/main/webapp/app/view/meter/meter.css b/web/gui/src/main/webapp/app/view/meter/meter.css
index b9e72cd..dc06443 100644
--- a/web/gui/src/main/webapp/app/view/meter/meter.css
+++ b/web/gui/src/main/webapp/app/view/meter/meter.css
@@ -70,7 +70,14 @@
     background-color: #5A5600;
 }
 
+#ov-meter td {
+    text-align: center;
+}
+#ov-meter td.right {
+    text-align: right;
+}
 #ov-meter td.bands {
+    text-align: left;
     padding-left: 36px;
     opacity: 0.65;
 }
\ No newline at end of file
diff --git a/web/gui/src/main/webapp/app/view/meter/meter.html b/web/gui/src/main/webapp/app/view/meter/meter.html
index bfa48ca..b068908 100644
--- a/web/gui/src/main/webapp/app/view/meter/meter.html
+++ b/web/gui/src/main/webapp/app/view/meter/meter.html
@@ -45,8 +45,8 @@
                     <td colId="id" sortable>Meter ID </td>
                     <td colId="app_id" sortable>App ID </td>
                     <td colId="state" sortable>State </td>
-                    <td colId="packets" sortable>Packets </td>
-                    <td colId="bytes" sortable>Bytes </td>
+                    <td class="right" colId="packets" sortable>Packets </td>
+                    <td class="right" colId="bytes" sortable>Bytes </td>
                 </tr>
             </table>
         </div>
@@ -64,8 +64,8 @@
                     <td>{{meter.id}}</td>
                     <td>{{meter.app_id}}</td>
                     <td>{{meter.state}}</td>
-                    <td>{{meter.packets}}</td>
-                    <td>{{meter.bytes}}</td>
+                    <td class="right">{{meter.packets}}</td>
+                    <td class="right">{{meter.bytes}}</td>
                 </tr>
                 <tr row-id="{{meter.id}}" ng-repeat-end>
                     <td class="bands" colspan="5"
diff --git a/web/gui/src/main/webapp/app/view/port/port.css b/web/gui/src/main/webapp/app/view/port/port.css
index 2aaacfc..0350731 100644
--- a/web/gui/src/main/webapp/app/view/port/port.css
+++ b/web/gui/src/main/webapp/app/view/port/port.css
@@ -40,6 +40,9 @@
 }
 
 #ov-port td {
+    text-align: center;
+}
+#ov-port td.right {
     text-align: right;
 }
 
diff --git a/web/gui/src/main/webapp/app/view/port/port.html b/web/gui/src/main/webapp/app/view/port/port.html
index 33ac24e..7400433 100644
--- a/web/gui/src/main/webapp/app/view/port/port.html
+++ b/web/gui/src/main/webapp/app/view/port/port.html
@@ -43,13 +43,13 @@
             <table>
                 <tr>
                     <td colId="id" col-width="60px" sortable>Port ID </td>
-                    <td colId="pkt_rx" sortable>Pkts Received </td>
-                    <td colId="pkt_tx" sortable>Pkts Sent </td>
-                    <td colId="bytes_rx" sortable>Bytes Received </td>
-                    <td colId="bytes_tx" sortable>Bytes Sent </td>
-                    <td colId="pkt_rx_drp" sortable>Pkts Received Dropped </td>
-                    <td colId="pkt_tx_drp" sortable>Pkts Sent Dropped </td>
-                    <td colId="duration" sortable>Duration (sec) </td>
+                    <td class="right" colId="pkt_rx" sortable>Pkts Received </td>
+                    <td class="right" colId="pkt_tx" sortable>Pkts Sent </td>
+                    <td class="right" colId="bytes_rx" sortable>Bytes Received </td>
+                    <td class="right" colId="bytes_tx" sortable>Bytes Sent </td>
+                    <td class="right" colId="pkt_rx_drp" sortable>Pkts Received Dropped </td>
+                    <td class="right" colId="pkt_tx_drp" sortable>Pkts Sent Dropped </td>
+                    <td class="right" colId="duration" sortable>Duration (sec) </td>
                 </tr>
             </table>
         </div>
@@ -65,13 +65,13 @@
                 <tr ng-repeat="port in tableData track by $index"
                     ng-repeat-complete row-id="{{port.id}}">
                     <td>{{port.id}}</td>
-                    <td>{{port.pkt_rx}}</td>
-                    <td>{{port.pkt_tx}}</td>
-                    <td>{{port.bytes_rx}}</td>
-                    <td>{{port.bytes_tx}}</td>
-                    <td>{{port.pkt_rx_drp}}</td>
-                    <td>{{port.pkt_tx_drp}}</td>
-                    <td>{{port.duration}}</td>
+                    <td class="right">{{port.pkt_rx}}</td>
+                    <td class="right">{{port.pkt_tx}}</td>
+                    <td class="right">{{port.bytes_rx}}</td>
+                    <td class="right">{{port.bytes_tx}}</td>
+                    <td class="right">{{port.pkt_rx_drp}}</td>
+                    <td class="right">{{port.pkt_tx_drp}}</td>
+                    <td class="right">{{port.duration}}</td>
                 </tr>
             </table>
         </div>