Minor changes to PI runtime classes

- New class for action runtime parameter and its identifier
- PiAction builder
- Various indentifier builders

Change-Id: I265f71c868c21dbbbe633622b0c4330712f5a5ad
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiAction.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiAction.java
index 24a7fbe..ef01b9d 100644
--- a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiAction.java
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiAction.java
@@ -19,11 +19,11 @@
 import com.google.common.annotations.Beta;
 import com.google.common.base.MoreObjects;
 import com.google.common.base.Objects;
-import com.google.common.collect.ImmutableList;
-import org.onlab.util.ImmutableByteSequence;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
 
-import java.util.Collections;
-import java.util.List;
+import java.util.Collection;
+import java.util.Map;
 import java.util.StringJoiner;
 
 import static com.google.common.base.Preconditions.checkNotNull;
@@ -36,7 +36,7 @@
 public final class PiAction implements PiTableAction {
 
     private final PiActionId actionId;
-    private final List<ImmutableByteSequence> runtimeParams;
+    private final Map<PiActionParamId, PiActionParam> runtimeParams;
 
     /**
      * Creates a new action instance for the given action identifier and runtime parameters.
@@ -44,18 +44,9 @@
      * @param actionId      action identifier
      * @param runtimeParams list of runtime parameters
      */
-    public PiAction(PiActionId actionId, List<ImmutableByteSequence> runtimeParams) {
-        this.actionId = checkNotNull(actionId);
-        this.runtimeParams = ImmutableList.copyOf(checkNotNull(runtimeParams));
-    }
-
-    /**
-     * Creates a new action instance for the given action identifier, with no runtime parameters.
-     *
-     * @param actionId action identifier
-     */
-    public PiAction(PiActionId actionId) {
-        this(actionId, Collections.emptyList());
+    private PiAction(PiActionId actionId, Map<PiActionParamId, PiActionParam> runtimeParams) {
+        this.actionId = actionId;
+        this.runtimeParams = ImmutableMap.copyOf(runtimeParams);
     }
 
     @Override
@@ -73,13 +64,13 @@
     }
 
     /**
-     * Returns an immutable view of the list of parameters of this action.
-     * Return an empty list if the action doesn't take any runtime parameters.
+     * Returns all runtime parameters of this action.
+     * Return an empty collection if the action doesn't take any runtime parameters.
      *
      * @return list of byte sequences
      */
-    public List<ImmutableByteSequence> parameters() {
-        return runtimeParams;
+    public Collection<PiActionParam> parameters() {
+        return runtimeParams.values();
     }
 
     @Override
@@ -108,4 +99,71 @@
                 .addValue(this.id().toString() + stringParams.toString())
                 .toString();
     }
+
+    /**
+     * Returns an action builder.
+     *
+     * @return a new builder
+     */
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    /**
+     * Builder of protocol-independent actions.
+     */
+    public static final class Builder {
+
+        private PiActionId actionId;
+        private Map<PiActionParamId, PiActionParam> runtimeParams = Maps.newHashMap();
+
+        private Builder() {
+            // hides constructor.
+        }
+
+        /**
+         * Sets the identifier of this action.
+         *
+         * @param actionId action identifier
+         * @return this
+         */
+        public Builder withId(PiActionId actionId) {
+            this.actionId = actionId;
+            return this;
+        }
+
+        /**
+         * Adds a runtime parameter.
+         *
+         * @param param action parameter
+         * @return this
+         */
+        public Builder withParameter(PiActionParam param) {
+            checkNotNull(param);
+            runtimeParams.put(param.id(), param);
+            return this;
+        }
+
+        /**
+         * Adds many runtime parameters.
+         *
+         * @param params collection of action parameters
+         * @return this
+         */
+        public Builder withParameters(Collection<PiActionParam> params) {
+            checkNotNull(params);
+            params.forEach(this::withParameter);
+            return this;
+        }
+
+        /**
+         * Returns a new action instance.
+         *
+         * @return action
+         */
+        public PiAction build() {
+            checkNotNull(actionId);
+            return new PiAction(actionId, runtimeParams);
+        }
+    }
 }
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionId.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionId.java
index ae17071..d0655da 100644
--- a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionId.java
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionId.java
@@ -19,6 +19,9 @@
 import com.google.common.annotations.Beta;
 import org.onlab.util.Identifier;
 
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
 /**
  * Identifier of an action of a match+action table in a protocol-independent pipeline.
  */
@@ -30,7 +33,19 @@
      *
      * @param name action name
      */
-    public PiActionId(String name) {
+    private PiActionId(String name) {
         super(name);
     }
+
+    /**
+     * Returns an action identifier with the given name.
+     *
+     * @param name action name
+     * @return action identifier
+     */
+    public static PiActionId of(String name) {
+        checkNotNull(name);
+        checkArgument(!name.isEmpty(), "Name can't be empty");
+        return new PiActionId(name);
+    }
 }
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionParam.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionParam.java
new file mode 100644
index 0000000..29b9a26
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionParam.java
@@ -0,0 +1,87 @@
+/*
+ * 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.pi.runtime;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Objects;
+import org.onlab.util.ImmutableByteSequence;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Runtime parameter of an action in a match+action table of a protocol-independent pipeline.
+ */
+@Beta
+public final class PiActionParam {
+
+    private final PiActionParamId id;
+    private final ImmutableByteSequence value;
+
+    /**
+     * Creates an action's runtime parameter.
+     *
+     * @param id    parameter identifier
+     * @param value value
+     */
+    public PiActionParam(PiActionParamId id, ImmutableByteSequence value) {
+        this.id = checkNotNull(id);
+        this.value = checkNotNull(value);
+        checkArgument(value.size() > 0, "Value can't have size 0");
+    }
+
+    /**
+     * Returns the identifier of this parameter.
+     *
+     * @return parameter identifier
+     */
+    public PiActionParamId id() {
+        return id;
+    }
+
+    /**
+     * Returns the value of this parameter.
+     *
+     * @return parameter value
+     */
+    public ImmutableByteSequence value() {
+        return value;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        PiActionParam that = (PiActionParam) o;
+        return Objects.equal(id, that.id) &&
+                Objects.equal(value, that.value);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(id, value);
+    }
+
+    @Override
+    public String toString() {
+        return this.id.toString() + "=" + this.value().toString();
+    }
+}
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionParamId.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionParamId.java
new file mode 100644
index 0000000..e941e10
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionParamId.java
@@ -0,0 +1,61 @@
+/*
+ * 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.pi.runtime;
+
+import com.google.common.annotations.Beta;
+import org.onlab.util.Identifier;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Identifier of an action's runtime parameter in a match+action table of a protocol-independent pipeline.
+ */
+@Beta
+public final class PiActionParamId extends Identifier<String> {
+
+    private final String name;
+
+    // TODO: if needed, we might add here support for positional parameters.
+    // E.g. add a second constructor that takes the "position" of a parameter, vs. its name.
+
+    private PiActionParamId(String name) {
+        super(name);
+        this.name = name;
+    }
+
+    /**
+     * Returns the name of this parameter.
+     *
+     * @return parameter name
+     */
+    public String name() {
+        return name;
+    }
+
+    /**
+     * Returns a parameter identifier with the given name.
+     *
+     * @param name parameter name
+     * @return parameter identifier
+     */
+    public static PiActionParamId of(String name) {
+        checkNotNull(name);
+        checkArgument(!name.isEmpty(), "Name can't be empty");
+        return new PiActionParamId(name);
+    }
+}
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiHeaderFieldId.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiHeaderFieldId.java
index 5566202..bf27b48 100644
--- a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiHeaderFieldId.java
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiHeaderFieldId.java
@@ -18,6 +18,9 @@
 
 import org.onlab.util.Identifier;
 
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
 /**
  * Identifier of a packet's header field.
  */
@@ -27,18 +30,7 @@
     private final String fieldName;
     private final int index;
 
-    /**
-     * Creates a new header field identifier for the given header name, field name and index.
-     * <p>
-     * Index represents the position of this header in the packet w.r.t. to other headers of the
-     * same type. Index 0 points to the first instance of the header, 1 the second one, etc. Helpful
-     * when dealing with stacked headers, e.g. to match on the second MPLS label.
-     *
-     * @param headerName header name
-     * @param fieldName  field name
-     * @param index      index
-     */
-    public PiHeaderFieldId(String headerName, String fieldName, int index) {
+    private PiHeaderFieldId(String headerName, String fieldName, int index) {
         super(headerName +
                       (index > 0 ? "[" + String.valueOf(index) + "]" : "") +
                       "." + fieldName);
@@ -48,13 +40,36 @@
     }
 
     /**
-     * Creates a new header field identifier for the given header name and field name.
+     * Returns an header field identifier for the given header name, field name and index.
+     * <p>
+     * Index represents the position of this header in the packet w.r.t. to other headers of the
+     * same type. Index 0 points to the first instance of the header, 1 the second one, etc. Helpful
+     * when dealing with stacked headers, e.g. to match on the second MPLS label.
      *
      * @param headerName header name
      * @param fieldName  field name
+     * @param index      index
+     * @return header field identifier
      */
-    public PiHeaderFieldId(String headerName, String fieldName) {
-        this(headerName, fieldName, 0);
+    public static PiHeaderFieldId of(String headerName, String fieldName, int index) {
+        checkNotNull(headerName);
+        checkNotNull(fieldName);
+        checkArgument(!headerName.isEmpty(), "Header name can't be empty");
+        checkArgument(!fieldName.isEmpty(), "Field name can't be empty");
+        checkArgument(index >= 0, "Index must be a positive integer");
+        return new PiHeaderFieldId(headerName, fieldName, index);
+    }
+
+    /**
+     * Returns an header field identifier for the given header name and field name.
+     * Index is set to default value 0.
+     *
+     * @param headerName header name
+     * @param fieldName  field name
+     * @return header field identifier
+     */
+    public static PiHeaderFieldId of(String headerName, String fieldName) {
+        return of(headerName, fieldName, 0);
     }
 
     /**
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiTableEntry.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiTableEntry.java
index 741fc04..c6f6094 100644
--- a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiTableEntry.java
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiTableEntry.java
@@ -143,6 +143,11 @@
                 .toString();
     }
 
+    /**
+     * Returns a table entry builder.
+     *
+     * @return a new builder
+     */
     public static Builder builder() {
         return new Builder();
     }
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 c062500..1740834 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
@@ -21,6 +21,7 @@
 
 import java.util.Optional;
 
+import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
 
 /**
@@ -32,27 +33,37 @@
     private final String scope;
     private final String name;
 
-    /**
-     * Creates a new table identifier for the given scope and table name.
-     *
-     * @param scope table scope
-     * @param name  table name
-     */
-    public PiTableId(String scope, String name) {
-        super(checkNotNull(scope) + '.' + checkNotNull(name));
+    private PiTableId(String scope, String name) {
+        super((scope != null ? scope + "." : "") + name);
         this.scope = scope;
         this.name = name;
     }
 
     /**
-     * Creates a new table identifier for the given table name.
+     * Returns a table identifier for the given table scope and name.
+     *
+     * @param scope table scope
+     * @param name  table name
+     * @return table identifier
+     */
+    public static PiTableId of(String scope, String name) {
+        checkNotNull(name);
+        checkNotNull(scope);
+        checkArgument(!name.isEmpty(), "Name can't be empty");
+        checkArgument(!scope.isEmpty(), "Scope can't be empty");
+        return new PiTableId(scope, name);
+    }
+
+    /**
+     * Returns a table identifier for the given table name.
      *
      * @param name table name
+     * @return table identifier
      */
-    public PiTableId(String name) {
-        super(checkNotNull(name));
-        this.name = name;
-        this.scope = null;
+    public static PiTableId of(String name) {
+        checkNotNull(name);
+        checkArgument(!name.isEmpty(), "Name can't be empty");
+        return new PiTableId(null, name);
     }