Support for generic table IDs in flow rules

This change is needed to let appications specify tables in flow rules
using indentifiers that are not numeric, e.g. using table names as in a
P4 program.

This change deprecates the FlowRule.tableId() method, in favor of
FlowRule.table(), which returns an object of type TableId. However, it
does not deprecate the .forTable(int tableId) builder method, since it
is used extensivelly in the ONOS code base and from external
applications.

Deprecating FlowRule.tableId() does not seem to be such a painful move
since it is used less frequently, e.g. only 22 usages in the ONOS tree.

Change-Id: Idaaaab53d0c1e20a1f9c7de84de09ec3a42648b5
diff --git a/core/api/src/main/java/org/onosproject/net/flow/DefaultFlowRule.java b/core/api/src/main/java/org/onosproject/net/flow/DefaultFlowRule.java
index 0a8ecfb..a31394a 100644
--- a/core/api/src/main/java/org/onosproject/net/flow/DefaultFlowRule.java
+++ b/core/api/src/main/java/org/onosproject/net/flow/DefaultFlowRule.java
@@ -28,6 +28,7 @@
 import static com.google.common.base.MoreObjects.toStringHelper;
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.net.flow.TableId.Type.INDEX;
 
 /**
  * Default flow rule.
@@ -50,7 +51,7 @@
     private final FlowRemoveReason reason;
     private final GroupId groupId;
 
-    private final Integer tableId;
+    private final TableId tableId;
     private final FlowRuleExtPayLoad payLoad;
 
     /**
@@ -71,14 +72,14 @@
         this.reason = rule.reason();
         this.permanent = rule.isPermanent();
         this.created = System.currentTimeMillis();
-        this.tableId = rule.tableId();
+        this.tableId = rule.table();
         this.payLoad = rule.payLoad();
     }
 
     private DefaultFlowRule(DeviceId deviceId, TrafficSelector selector,
                             TrafficTreatment treatment, Integer priority,
                             FlowId flowId, Boolean permanent, Integer timeout, Integer hardTimeout,
-                            FlowRemoveReason reason, Integer tableId) {
+                            FlowRemoveReason reason, TableId tableId) {
 
         this.deviceId = deviceId;
         this.selector = selector;
@@ -158,7 +159,7 @@
         this.reason = FlowRemoveReason.NO_REASON;
         this.hardTimeout = hardTimeout;
         this.permanent = permanent;
-        this.tableId = 0;
+        this.tableId = DEFAULT_TABLE;
         this.created = System.currentTimeMillis();
         this.payLoad = payLoad;
 
@@ -234,7 +235,7 @@
         this.hardTimeout = hardTimeout;
         this.permanent = permanent;
         this.created = System.currentTimeMillis();
-        this.tableId = 0;
+        this.tableId = DEFAULT_TABLE;
         this.payLoad = payLoad;
 
         /*
@@ -364,6 +365,12 @@
 
     @Override
     public int tableId() {
+        // Workaround until we remove this method. Deprecated in Loon.
+        return tableId.type() == INDEX ? ((IndexTableId) tableId).id() : tableId.hashCode();
+    }
+
+    @Override
+    public TableId table() {
         return tableId;
     }
 
@@ -394,7 +401,7 @@
         private ApplicationId appId;
         private Integer priority;
         private DeviceId deviceId;
-        private Integer tableId = 0;
+        private TableId tableId = DEFAULT_TABLE;
         private TrafficSelector selector = DefaultTrafficSelector.builder().build();
         private TrafficTreatment treatment = DefaultTrafficTreatment.builder().build();
         private Integer timeout;
@@ -428,6 +435,12 @@
 
         @Override
         public FlowRule.Builder forTable(int tableId) {
+            this.tableId = IndexTableId.of(tableId);
+            return this;
+        }
+
+        @Override
+        public FlowRule.Builder forTable(TableId tableId) {
             this.tableId = tableId;
             return this;
         }
@@ -475,6 +488,7 @@
         @Override
         public FlowRule build() {
             FlowId localFlowId;
+            checkNotNull(tableId, "Table id cannot be null");
             checkArgument((flowId != null) ^ (appId != null), "Either an application" +
                     " id or a cookie must be supplied");
             checkNotNull(selector, "Traffic selector cannot be null");
@@ -514,7 +528,7 @@
                     .putUnencodedChars(deviceId.toString())
                     .putObject(selector, selectorFunnel)
                     .putInt(priority)
-                    .putInt(tableId)
+                    .putUnencodedChars(tableId.toString())
                     .hash();
 
             return hashCode.asInt();
diff --git a/core/api/src/main/java/org/onosproject/net/flow/FlowRule.java b/core/api/src/main/java/org/onosproject/net/flow/FlowRule.java
index c758c48..7e1f58c 100644
--- a/core/api/src/main/java/org/onosproject/net/flow/FlowRule.java
+++ b/core/api/src/main/java/org/onosproject/net/flow/FlowRule.java
@@ -25,6 +25,7 @@
  */
 public interface FlowRule {
 
+    IndexTableId DEFAULT_TABLE = IndexTableId.of(0);
     int MAX_TIMEOUT = 60;
     int MIN_PRIORITY = 0;
     int MAX_PRIORITY = 65535;
@@ -153,10 +154,19 @@
      * Returns the table id for this rule.
      *
      * @return an integer.
+     * @deprecated in Loon release (version 1.11.0). Use {@link #table()} instead.
      */
+    @Deprecated
     int tableId();
 
     /**
+     * Returns the table identifier for this rule.
+     *
+     * @return a table identifier.
+     */
+    TableId table();
+
+    /**
      * {@inheritDoc}
      *
      * Equality for flow rules only considers 'match equality'. This means that
@@ -227,7 +237,12 @@
         Builder forDevice(DeviceId deviceId);
 
         /**
-         * Sets the table id for this flow rule. Default value is 0.
+         * Sets the table id for this flow rule, when the identifier is of type {@link TableId.Type#INDEX}. Default
+         * value is 0.
+         * <p>
+         * <em>Important:</em> This method is left here for backward compatibility with applications that specifies
+         * table identifiers using integers, e.g. as in OpenFlow. Currently there is no plan to deprecate this method,
+         * however, new applications should favor using {@link #forTable(TableId)}.
          *
          * @param tableId an integer
          * @return this
@@ -235,6 +250,15 @@
         Builder forTable(int tableId);
 
         /**
+         * Sets the table identifier for this flow rule.
+         * Default identifier is of type {@link TableId.Type#INDEX} and value 0.
+         *
+         * @param tableId table identifier
+         * @return this
+         */
+        Builder forTable(TableId tableId);
+
+        /**
          * Sets the selector (or match field) for this flow rule.
          *
          * @param selector a traffic selector
diff --git a/core/api/src/main/java/org/onosproject/net/flow/IndexTableId.java b/core/api/src/main/java/org/onosproject/net/flow/IndexTableId.java
new file mode 100644
index 0000000..04bc560
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/flow/IndexTableId.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2017-present 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.net.flow;
+
+import org.onlab.util.Identifier;
+
+/**
+ * Table identifier representing the position of the table in the pipeline.
+ */
+public final class IndexTableId extends Identifier<Integer> implements TableId {
+
+    private IndexTableId(int index) {
+        super(index);
+    }
+
+    @Override
+    public Type type() {
+        return Type.INDEX;
+    }
+
+    /**
+     * Returns a table identifier for the given index.
+     *
+     * @param index table index
+     * @return table identifier
+     */
+    public static IndexTableId of(int index) {
+        return new IndexTableId(index);
+    }
+}
diff --git a/core/api/src/main/java/org/onosproject/net/flow/TableId.java b/core/api/src/main/java/org/onosproject/net/flow/TableId.java
new file mode 100644
index 0000000..0207f84
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/flow/TableId.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2017-present 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.net.flow;
+
+/**
+ * Match+action table identifier.
+ */
+public interface TableId {
+
+    /**
+     * Types of table identifier.
+     */
+    enum Type {
+        /**
+         * Signifies that the table identifier corresponds to the position of the table in the pipeline.
+         */
+        INDEX,
+
+        /**
+         * Signifies that the table identifier is pipeline-independent.
+         */
+        PIPELINE_INDEPENDENT
+    }
+
+    Type type();
+}
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiTableId.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiTableId.java
index 1740834..add75ec 100644
--- a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiTableId.java
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiTableId.java
@@ -18,6 +18,7 @@
 
 import com.google.common.annotations.Beta;
 import org.onlab.util.Identifier;
+import org.onosproject.net.flow.TableId;
 
 import java.util.Optional;
 
@@ -28,7 +29,7 @@
  * Identifier of a table in a protocol-independent pipeline.
  */
 @Beta
-public final class PiTableId extends Identifier<String> {
+public final class PiTableId extends Identifier<String> implements TableId  {
 
     private final String scope;
     private final String name;
@@ -85,5 +86,8 @@
         return Optional.ofNullable(scope);
     }
 
-
+    @Override
+    public Type type() {
+        return Type.PIPELINE_INDEPENDENT;
+    }
 }
diff --git a/core/api/src/test/java/org/onosproject/net/flow/FlowEntryAdapter.java b/core/api/src/test/java/org/onosproject/net/flow/FlowEntryAdapter.java
index 6937647..cc8ee99 100644
--- a/core/api/src/test/java/org/onosproject/net/flow/FlowEntryAdapter.java
+++ b/core/api/src/test/java/org/onosproject/net/flow/FlowEntryAdapter.java
@@ -134,6 +134,11 @@
     }
 
     @Override
+    public TableId table() {
+        return DEFAULT_TABLE;
+    }
+
+    @Override
     public boolean exactMatch(FlowRule rule) {
         return false;
     }
diff --git a/core/api/src/test/java/org/onosproject/net/intent/IntentTestsMocks.java b/core/api/src/test/java/org/onosproject/net/intent/IntentTestsMocks.java
index fbd134f..43d051c 100644
--- a/core/api/src/test/java/org/onosproject/net/intent/IntentTestsMocks.java
+++ b/core/api/src/test/java/org/onosproject/net/intent/IntentTestsMocks.java
@@ -30,6 +30,8 @@
 import org.onosproject.net.flow.FlowId;
 import org.onosproject.net.flow.FlowRule;
 import org.onosproject.net.flow.FlowRuleExtPayLoad;
+import org.onosproject.net.flow.IndexTableId;
+import org.onosproject.net.flow.TableId;
 import org.onosproject.net.flow.TrafficSelector;
 import org.onosproject.net.flow.TrafficTreatment;
 import org.onosproject.net.flow.criteria.Criterion;
@@ -314,14 +316,14 @@
         static int nextId = 0;
 
         int priority;
-        int tableId;
+        IndexTableId tableId;
         long timestamp;
         int id;
         FlowRuleExtPayLoad payLoad;
 
         public MockFlowRule(int priority) {
             this.priority = priority;
-            this.tableId = 0;
+            this.tableId = DEFAULT_TABLE;
             this.timestamp = System.currentTimeMillis();
             this.id = nextId++;
             this.payLoad = null;
@@ -414,6 +416,11 @@
 
         @Override
         public int tableId() {
+            return tableId.id();
+        }
+
+        @Override
+        public TableId table() {
             return tableId;
         }
 
diff --git a/core/store/serializers/src/main/java/org/onosproject/store/serializers/KryoNamespaces.java b/core/store/serializers/src/main/java/org/onosproject/store/serializers/KryoNamespaces.java
index 64fdba3..39c9828 100644
--- a/core/store/serializers/src/main/java/org/onosproject/store/serializers/KryoNamespaces.java
+++ b/core/store/serializers/src/main/java/org/onosproject/store/serializers/KryoNamespaces.java
@@ -105,7 +105,9 @@
 import org.onosproject.net.flow.FlowRuleBatchRequest;
 import org.onosproject.net.flow.FlowRuleEvent;
 import org.onosproject.net.flow.FlowRuleExtPayLoad;
+import org.onosproject.net.flow.IndexTableId;
 import org.onosproject.net.flow.StoredFlowEntry;
+import org.onosproject.net.flow.TableId;
 import org.onosproject.net.flow.TableStatisticsEntry;
 import org.onosproject.net.flow.criteria.ArpHaCriterion;
 import org.onosproject.net.flow.criteria.ArpOpCriterion;
@@ -194,6 +196,7 @@
 import org.onosproject.net.packet.DefaultOutboundPacket;
 import org.onosproject.net.packet.DefaultPacketRequest;
 import org.onosproject.net.packet.PacketPriority;
+import org.onosproject.net.pi.runtime.PiTableId;
 import org.onosproject.net.provider.ProviderId;
 import org.onosproject.net.region.DefaultRegion;
 import org.onosproject.net.region.Region;
@@ -354,6 +357,9 @@
                     DefaultFlowEntry.class,
                     StoredFlowEntry.class,
                     DefaultFlowRule.class,
+                    TableId.class,
+                    IndexTableId.class,
+                    PiTableId.class,
                     FlowRule.FlowRemoveReason.class,
                     DefaultPacketRequest.class,
                     PacketPriority.class,