Enhance CLI command 'tablestats' to support P4 switch

Change-Id: Icb7bae93840bac1ad8d49240f61a4f5bf08dac4c
diff --git a/cli/src/main/java/org/onosproject/cli/net/TableStatisticsCommand.java b/cli/src/main/java/org/onosproject/cli/net/TableStatisticsCommand.java
index aedbaee..2037800e 100644
--- a/cli/src/main/java/org/onosproject/cli/net/TableStatisticsCommand.java
+++ b/cli/src/main/java/org/onosproject/cli/net/TableStatisticsCommand.java
@@ -55,13 +55,13 @@
     String uri = null;
 
     private static final String FORMAT =
-            "   table=%s, active=%s, lookedup=%s, matched=%s";
+            "   table=%s, active=%s, lookedup=%s, matched=%s, maxsize=%s";
+    private static final String NA = "N/A";
 
     @Override
     protected void execute() {
         FlowRuleService flowService = get(FlowRuleService.class);
         DeviceService deviceService = get(DeviceService.class);
-
         SortedMap<Device, List<TableStatisticsEntry>> deviceTableStats =
                 getSortedTableStats(deviceService, flowService);
 
@@ -115,8 +115,9 @@
         print("deviceId=%s, tableCount=%d", d.id(), empty ? 0 : tableStats.size());
         if (!empty) {
             for (TableStatisticsEntry t : tableStats) {
-                print(FORMAT, t.tableId(), t.activeFlowEntries(),
-                      t.packetsLookedup(), t.packetsMatched());
+                print(FORMAT, t.table(), t.activeFlowEntries(),
+                        t.hasPacketsLookedup() ? t.packetsLookedup() : NA, t.packetsMatched(),
+                        t.hasMaxSize() ? t.maxSize() : NA);
             }
         }
     }
@@ -129,7 +130,7 @@
      * @return sorted table statistics list
      */
     protected SortedMap<Device, List<TableStatisticsEntry>> getSortedTableStats(DeviceService deviceService,
-                                                          FlowRuleService flowService) {
+                                                                                FlowRuleService flowService) {
         SortedMap<Device, List<TableStatisticsEntry>> deviceTableStats = new TreeMap<>(Comparators.ELEMENT_COMPARATOR);
         List<TableStatisticsEntry> tableStatsList;
         Iterable<Device> devices = uri == null ? deviceService.getDevices() :
@@ -142,4 +143,4 @@
         return deviceTableStats;
     }
 
-}
+}
\ No newline at end of file
diff --git a/core/api/src/main/java/org/onosproject/net/flow/DefaultTableStatisticsEntry.java b/core/api/src/main/java/org/onosproject/net/flow/DefaultTableStatisticsEntry.java
index 53423d3..bf34437 100644
--- a/core/api/src/main/java/org/onosproject/net/flow/DefaultTableStatisticsEntry.java
+++ b/core/api/src/main/java/org/onosproject/net/flow/DefaultTableStatisticsEntry.java
@@ -24,10 +24,36 @@
 public final class DefaultTableStatisticsEntry implements TableStatisticsEntry {
 
     private final DeviceId deviceId;
-    private final int tableId;
+    private final TableId tableId;
     private final long activeFlowEntries;
     private final long packetsLookedupCount;
     private final long packetsMatchedCount;
+    private final long maxSize;
+    private static final Long NOT_PRESENT = (long) -1;
+
+    /**
+     * Default table statistics constructor.
+     *
+     * @param deviceId device identifier
+     * @param tableId index table identifier
+     * @param activeFlowEntries number of active flow entries in the table
+     * @param packetsLookedupCount number of packets looked up in table
+     * @param packetsMatchedCount number of packets that hit table
+     * @deprecated since 1.15, suggest using the Builder class.
+     */
+    @Deprecated
+    public DefaultTableStatisticsEntry(DeviceId deviceId,
+                                       int  tableId,
+                                       long activeFlowEntries,
+                                       long packetsLookedupCount,
+                                       long packetsMatchedCount) {
+        this.deviceId = checkNotNull(deviceId);
+        this.tableId = IndexTableId.of(tableId);
+        this.activeFlowEntries = activeFlowEntries;
+        this.packetsLookedupCount = packetsLookedupCount;
+        this.packetsMatchedCount = packetsMatchedCount;
+        this.maxSize = NOT_PRESENT;
+    }
 
     /**
      * Default table statistics constructor.
@@ -37,17 +63,20 @@
      * @param activeFlowEntries number of active flow entries in the table
      * @param packetsLookedupCount number of packets looked up in table
      * @param packetsMatchedCount number of packets that hit table
+     * @param maxSize maximum size of this table
      */
-    public DefaultTableStatisticsEntry(DeviceId deviceId,
-                                  int  tableId,
-                                  long activeFlowEntries,
-                                  long packetsLookedupCount,
-                                  long packetsMatchedCount) {
+    private DefaultTableStatisticsEntry(DeviceId deviceId,
+                                       TableId  tableId,
+                                       long activeFlowEntries,
+                                       long packetsLookedupCount,
+                                       long packetsMatchedCount,
+                                       long maxSize) {
         this.deviceId = checkNotNull(deviceId);
         this.tableId = tableId;
         this.activeFlowEntries = activeFlowEntries;
         this.packetsLookedupCount = packetsLookedupCount;
         this.packetsMatchedCount = packetsMatchedCount;
+        this.maxSize = maxSize;
     }
 
     @Override
@@ -61,6 +90,12 @@
 
     @Override
     public int tableId() {
+        return tableId.type() == TableId.Type.INDEX ? ((IndexTableId) tableId).id() : tableId.hashCode();
+    }
+
+    @Override
+    public TableId table() {
+        //TODO: this is a temporary method, should implement tableId() like this method.
         return tableId;
     }
 
@@ -83,4 +118,73 @@
     public DeviceId deviceId() {
         return deviceId;
     }
-}
+
+    @Override
+    public long maxSize() {
+        return maxSize;
+    }
+
+    @Override
+    public boolean hasPacketsLookedup() {
+        return packetsLookedupCount == NOT_PRESENT ? false : true;
+    }
+
+    @Override
+    public boolean hasMaxSize() {
+        return maxSize == NOT_PRESENT ? false : true;
+    }
+
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    public static final class Builder {
+        private DeviceId deviceId;
+        private TableId tableId;
+        private Long activeFlowEntries;
+        private Long packetsMatchedCount;
+        private Long packetsLookedUpCount = NOT_PRESENT;
+        private Long maxSize = NOT_PRESENT;
+
+        public Builder withDeviceId(DeviceId deviceId) {
+            this.deviceId = deviceId;
+            return this;
+        }
+
+        public Builder withTableId(TableId tableId) {
+            this.tableId = tableId;
+            return this;
+        }
+
+        public Builder withActiveFlowEntries(long activeFlowEntries) {
+            this.activeFlowEntries = activeFlowEntries;
+            return this;
+        }
+
+        public Builder withPacketsLookedUpCount(long packetsLookedUpCount) {
+            this.packetsLookedUpCount = packetsLookedUpCount;
+            return this;
+        }
+
+        public Builder withPacketsMatchedCount(long packetsMatchedCount) {
+            this.packetsMatchedCount = packetsMatchedCount;
+            return this;
+        }
+
+        public Builder withMaxSize(long maxSize) {
+            this.maxSize = maxSize;
+            return this;
+        }
+
+        public TableStatisticsEntry build() {
+            checkNotNull(deviceId, "DeviceId cannot be null");
+            checkNotNull(tableId, "TableId cannot be null");
+            checkNotNull(activeFlowEntries, "ActiveFlowEntries cannot be null");
+            checkNotNull(packetsMatchedCount, "PacketsMatchedCount cannot be null");
+
+            return new DefaultTableStatisticsEntry(deviceId, tableId, activeFlowEntries, packetsLookedUpCount,
+                    packetsMatchedCount, maxSize);
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/core/api/src/main/java/org/onosproject/net/flow/TableStatisticsEntry.java b/core/api/src/main/java/org/onosproject/net/flow/TableStatisticsEntry.java
index 505d535..1b39146 100644
--- a/core/api/src/main/java/org/onosproject/net/flow/TableStatisticsEntry.java
+++ b/core/api/src/main/java/org/onosproject/net/flow/TableStatisticsEntry.java
@@ -30,13 +30,21 @@
     DeviceId  deviceId();
 
     /**
-     * Returns the table number.
+     * Returns the integer table id.
      *
-     * @return table number
+     * @return integer table id
      */
+    @Deprecated
     int  tableId();
 
     /**
+     * Returns the table id.
+     *
+     * @return table id
+     */
+    TableId table();
+
+    /**
      * Returns the number of active flow entries in this table.
      *
      * @return the number of active flow entries
@@ -56,4 +64,26 @@
      * @return the number of packets that successfully matched in the table
      */
     long packetsMatched();
-}
+
+    /**
+     * Returns the maximum size of this table.
+     *
+     * @return the maximum size of this table
+     */
+    long maxSize();
+
+    /**
+     * To check whether packetLookedUp is present in this TableStatisticsEntry.
+     *
+     * @return true if packetLookedUp is present, otherwise false;
+     */
+    boolean hasPacketsLookedup();
+
+    /**
+     * To check whether maxSize is present in this TableStatisticsEntry.
+     *
+     * @return true if maxSize is present, otherwise false;
+     */
+    boolean hasMaxSize();
+
+}
\ No newline at end of file
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/TableStatisticsEntryCodec.java b/core/common/src/main/java/org/onosproject/codec/impl/TableStatisticsEntryCodec.java
index efd1d2c..f1af62b 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/TableStatisticsEntryCodec.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/TableStatisticsEntryCodec.java
@@ -33,7 +33,7 @@
         checkNotNull(entry, "Table Statistics entry cannot be null");
 
         final ObjectNode result = context.mapper().createObjectNode()
-                .put("tableId", entry.tableId())
+                .put("tableId", entry.table().toString())
                 .put("deviceId", entry.deviceId().toString())
                 .put("activeEntries", entry.activeFlowEntries())
                 .put("packetsLookedUp", entry.packetsLookedup())
@@ -43,4 +43,3 @@
     }
 
 }
-
diff --git a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/OpenFlowRuleProvider.java b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/OpenFlowRuleProvider.java
index a72c082..c6d0769 100644
--- a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/OpenFlowRuleProvider.java
+++ b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/OpenFlowRuleProvider.java
@@ -52,6 +52,7 @@
 import org.onosproject.net.flow.FlowRuleProviderRegistry;
 import org.onosproject.net.flow.FlowRuleProviderService;
 import org.onosproject.net.flow.TableStatisticsEntry;
+import org.onosproject.net.flow.IndexTableId;
 import org.onosproject.net.provider.AbstractProvider;
 import org.onosproject.net.provider.ProviderId;
 import org.onosproject.net.statistic.DefaultLoad;
@@ -709,11 +710,25 @@
                                                           OFTableStatsEntry ofEntry) {
             TableStatisticsEntry entry = null;
             if (ofEntry != null) {
-                entry = new DefaultTableStatisticsEntry(deviceId,
-                                                        ofEntry.getTableId().getValue(),
-                                                        ofEntry.getActiveCount(),
-                                                        ofEntry.getLookupCount().getValue(),
-                                                        ofEntry.getMatchedCount().getValue());
+                IndexTableId tid = IndexTableId.of(ofEntry.getTableId().getValue());
+
+                try {
+                    entry = DefaultTableStatisticsEntry.builder()
+                            .withDeviceId(deviceId)
+                            .withTableId(tid)
+                            .withActiveFlowEntries(ofEntry.getActiveCount())
+                            .withPacketsLookedUpCount(ofEntry.getLookupCount().getValue())
+                            .withPacketsMatchedCount(ofEntry.getMatchedCount().getValue())
+                            .withMaxSize(ofEntry.getMaxEntries()).build();
+                } catch (UnsupportedOperationException e) {
+                    // The exception "UnsupportedOperationException" is thrown by "getMaxEntries()".
+                    entry = DefaultTableStatisticsEntry.builder()
+                            .withDeviceId(deviceId)
+                            .withTableId(tid)
+                            .withActiveFlowEntries(ofEntry.getActiveCount())
+                            .withPacketsLookedUpCount(ofEntry.getLookupCount().getValue())
+                            .withPacketsMatchedCount(ofEntry.getMatchedCount().getValue()).build();
+                }
             }
 
             return entry;
@@ -775,4 +790,4 @@
         }
     }
 
-}
+}
\ No newline at end of file