[ONOS-7602]Implement support for P4Data in PI framework and P4Runtime southbound

Change-Id: I9c0c76fb29b8c3aa36f39bde62e825f0f0094253
diff --git a/core/api/src/main/java/org/onosproject/net/pi/model/PiData.java b/core/api/src/main/java/org/onosproject/net/pi/model/PiData.java
new file mode 100644
index 0000000..97d5290
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/pi/model/PiData.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * 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.model;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * Representation of data that can be used for runtime operations of a protocol-independent pipeline.
+ */
+@Beta
+public interface PiData {
+    /**
+     * Types of data in a protocol-independent pipeline.
+     */
+    enum Type {
+        /**
+         * Bit String.
+         */
+        BITSTRING,
+
+        /**
+         * Bool.
+         */
+        BOOL,
+
+        /**
+         * Tuple.
+         */
+        TUPLE,
+
+        /**
+         * Struct.
+         */
+        STRUCT,
+
+        /**
+         * Header.
+         */
+        HEADER,
+
+        /**
+         * Header Stack.
+         */
+        HEADERSTACK,
+
+        /**
+         * Header Union.
+         */
+        HEADERUNION,
+
+        /**
+         * Header Union Stack.
+         */
+        HEADERUNIONSTACK,
+
+        /**
+         * Enum String.
+         */
+        ENUMSTRING,
+
+        /**
+         * Error String.
+         */
+        ERRORSTRING
+    }
+
+    /**
+     * Returns the type of this protocol-independent data.
+     * @return the type of this instance
+     */
+    Type type();
+}
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/data/PiBitString.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/data/PiBitString.java
new file mode 100644
index 0000000..544f5df
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/data/PiBitString.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * 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.data;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Objects;
+import org.onlab.util.ImmutableByteSequence;
+import org.onosproject.net.pi.model.PiData;
+
+/**
+ * BitString entity in a protocol-independent pipeline.
+ */
+@Beta
+public final class PiBitString implements PiData {
+    private final ImmutableByteSequence bitString;
+
+    /**
+     * Creates a new protocol-independent bit string instance.
+     *
+     * @param bitString bitString
+     */
+    private PiBitString(ImmutableByteSequence bitString) {
+        this.bitString = bitString;
+    }
+
+    /**
+     * Returns a new protocol-independent BitString.
+     * @param bitString bitString
+     * @return BitString
+     */
+    public static PiBitString of(ImmutableByteSequence bitString) {
+        return new PiBitString(bitString);
+    }
+
+    /**
+     * Return protocol-independent bitString instance.
+     *
+     * @return bitString
+     */
+    public ImmutableByteSequence bitString() {
+        return this.bitString;
+    }
+
+    @Override
+    public Type type() {
+        return Type.BITSTRING;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        PiBitString bitStr = (PiBitString) o;
+        return Objects.equal(bitString, bitStr.bitString);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(bitString);
+    }
+
+    @Override
+    public String toString() {
+        return bitString.toString();
+    }
+}
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/data/PiBool.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/data/PiBool.java
new file mode 100644
index 0000000..e100f03
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/data/PiBool.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * 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.data;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Objects;
+import org.onosproject.net.pi.model.PiData;
+
+/**
+ * Boolean entity in a protocol-independent pipeline.
+ */
+@Beta
+public final class PiBool implements PiData {
+    private final Boolean bool;
+
+    /**
+     * Creates a new protocol-independent Bool instance.
+     *
+     * @param bool boolean
+     */
+    private PiBool(Boolean bool) {
+        this.bool = bool;
+    }
+
+    /**
+     * Returns a new protocol-independent Bool.
+     * @param bool boolean
+     * @return protocol-independent Bool
+     */
+    public static PiBool of(Boolean bool) {
+        return new PiBool(bool);
+    }
+
+    /**
+     * Return protocol-independent Boolean instance.
+     *
+     * @return bool
+     */
+    public Boolean bool() {
+        return this.bool;
+    }
+
+    @Override
+    public Type type() {
+        return Type.BOOL;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        PiBool boolValue = (PiBool) o;
+        return Objects.equal(bool, boolValue.bool);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(bool);
+    }
+
+    @Override
+    public String toString() {
+        return bool.toString();
+    }
+}
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/data/PiEnumString.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/data/PiEnumString.java
new file mode 100644
index 0000000..74481ea
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/data/PiEnumString.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * 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.data;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Objects;
+import org.onosproject.net.pi.model.PiData;
+
+/**
+ * EnumString entity in a protocol-independent pipeline.
+ */
+@Beta
+public final class PiEnumString implements PiData {
+    private final String enumString;
+
+    /**
+     * Creates a new protocol-independent enum string instance.
+     *
+     * @param enumString enum string
+     */
+    private PiEnumString(String enumString) {
+        this.enumString = enumString;
+    }
+
+    /**
+     * Returns a new protocol-independent enum string.
+     * @param enumString enum string
+     * @return enum string
+     */
+    public static PiEnumString of(String enumString) {
+        return new PiEnumString(enumString);
+    }
+
+    /**
+     * Return protocol-independent enum string instance.
+     *
+     * @return enum string
+     */
+    public String enumString() {
+        return this.enumString;
+    }
+
+    @Override
+    public Type type() {
+        return Type.ENUMSTRING;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        PiEnumString enumStr = (PiEnumString) o;
+        return Objects.equal(enumString, enumStr.enumString);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(enumString);
+    }
+
+    @Override
+    public String toString() {
+        return enumString;
+    }
+}
\ No newline at end of file
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/data/PiErrorString.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/data/PiErrorString.java
new file mode 100644
index 0000000..ff4e172
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/data/PiErrorString.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * 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.data;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Objects;
+import org.onosproject.net.pi.model.PiData;
+
+/**
+ * ErrorString entity in a protocol-independent pipeline.
+ */
+@Beta
+public final class PiErrorString implements PiData {
+    private final String errorString;
+
+    /**
+     * Creates a new protocol-independent error string instance.
+     *
+     * @param errorString error string
+     */
+    private PiErrorString(String errorString) {
+        this.errorString = errorString;
+    }
+
+    /**
+     * Returns a new protocol-independent error string.
+     * @param errorString error string
+     * @return error string
+     */
+    public static PiErrorString of(String errorString) {
+        return new PiErrorString(errorString);
+    }
+
+    /**
+     * Return protocol-independent error string instance.
+     *
+     * @return error string
+     */
+    public String errorString() {
+        return this.errorString;
+    }
+
+    @Override
+    public Type type() {
+        return Type.ERRORSTRING;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        PiErrorString errorStr = (PiErrorString) o;
+        return Objects.equal(errorString, errorStr.errorString);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(errorString);
+    }
+
+    @Override
+    public String toString() {
+        return errorString;
+    }
+}
\ No newline at end of file
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/data/PiHeader.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/data/PiHeader.java
new file mode 100644
index 0000000..bbc0f8e
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/data/PiHeader.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * 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.data;
+
+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 org.onosproject.net.pi.model.PiData;
+
+import java.util.List;
+import java.util.StringJoiner;
+
+/**
+ * Instance of a PiHeader in a protocol-independent pipeline.
+ */
+@Beta
+public final class PiHeader implements PiData {
+    private final Boolean isValid;
+    private final ImmutableList<ImmutableByteSequence> bitStrings;
+
+    /**
+     * Creates a new protocol-independent header instance.
+     *
+     * @param isValid whether the header is valid
+     * @param bitStrings bitstrings
+     */
+    private PiHeader(Boolean isValid, List<ImmutableByteSequence> bitStrings) {
+        this.isValid = isValid;
+        this.bitStrings = ImmutableList.copyOf(bitStrings);
+    }
+
+    /**
+     * Returns a new protocol-independent header.
+     * @param isValid whether the header is valid
+     * @param bitStrings bitstrings
+     * @return header
+     */
+    public static PiHeader of(Boolean isValid, List<ImmutableByteSequence> bitStrings) {
+        return new PiHeader(isValid, bitStrings);
+    }
+
+    /**
+     * Return whether the header is valid.
+     *
+     * @return bool
+     */
+    public Boolean isValid() {
+        return this.isValid;
+    }
+
+    /**
+     * Return the header bit strings.
+     *
+     * @return the list of bit string
+     */
+    public List<ImmutableByteSequence> bitStrings() {
+        return this.bitStrings;
+    }
+
+    @Override
+    public Type type() {
+        return Type.HEADER;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        PiHeader header = (PiHeader) o;
+        return Objects.equal(isValid, header.isValid) &&
+                Objects.equal(bitStrings, header.bitStrings);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(isValid, bitStrings);
+    }
+
+    @Override
+    public String toString() {
+        StringJoiner stringParams = new StringJoiner(", ", "(", ")");
+        this.bitStrings().forEach(p -> stringParams.add(p.toString()));
+        return MoreObjects.toStringHelper(getClass())
+                .add("bitString", stringParams)
+                .add("isValid", isValid)
+                .toString();
+    }
+}
\ No newline at end of file
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/data/PiHeaderStack.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/data/PiHeaderStack.java
new file mode 100644
index 0000000..ccf8af8
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/data/PiHeaderStack.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * 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.data;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableList;
+import org.onosproject.net.pi.model.PiData;
+
+import java.util.List;
+import java.util.StringJoiner;
+
+/**
+ * Instance of a PiHeaderStack in a protocol-independent pipeline.
+ */
+@Beta
+public final class PiHeaderStack implements PiData {
+    private final ImmutableList<PiHeader> headers;
+
+    /**
+     * Creates a new protocol-independent header stack instance for the given collection of Header.
+     *
+     * @param headers the collection of header
+     */
+    private PiHeaderStack(List<PiHeader> headers) {
+        this.headers = ImmutableList.copyOf(headers);
+    }
+
+    /**
+     * Returns a new protocol-independent header stack.
+     * @param headers the list of header
+     * @return header stack
+     */
+    public static PiHeaderStack of(List<PiHeader> headers) {
+        return new PiHeaderStack(headers);
+    }
+
+    /**
+     * Return the list of header.
+     *
+     * @return the list of header
+     */
+    public List<PiHeader> headers() {
+        return this.headers;
+    }
+
+    @Override
+    public Type type() {
+        return Type.HEADERSTACK;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        PiHeaderStack stack = (PiHeaderStack) o;
+        return Objects.equal(headers, stack.headers);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(headers);
+    }
+
+    @Override
+    public String toString() {
+        StringJoiner stringParams = new StringJoiner(", ", "(", ")");
+        this.headers().forEach(p -> stringParams.add(p.toString()));
+        return stringParams.toString();
+    }
+}
\ No newline at end of file
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/data/PiHeaderUnion.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/data/PiHeaderUnion.java
new file mode 100644
index 0000000..189318c
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/data/PiHeaderUnion.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * 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.data;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Objects;
+import org.onosproject.net.pi.model.PiData;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * PiHeaderUnion entity in a protocol-independent pipeline.
+ */
+@Beta
+public final class PiHeaderUnion implements PiData {
+    private final String validHeaderName;
+    private final PiHeader header;
+    private final boolean isValid;
+
+    /**
+     * Creates a new protocol-independent header union instance for the given header name and header.
+     *
+     * @param isValid indicates whether this header union is valid
+     * @param validHeaderName header name
+     * @param header the header
+     */
+    private PiHeaderUnion(boolean isValid, String validHeaderName, PiHeader header) {
+        this.isValid = isValid;
+        this.validHeaderName = validHeaderName;
+        this.header = header;
+    }
+
+    /**
+     * Returns a new invalid protocol-independent header union.
+     *
+     * @return header union
+     */
+    public static PiHeaderUnion ofInvalid() {
+        return new PiHeaderUnion(false, null, null);
+    }
+
+    /**
+     * Returns a new valid protocol-independent header union.
+     * @param validHeaderName header name
+     * @param header the header
+     * @return header union
+     */
+    public static PiHeaderUnion of(String validHeaderName, PiHeader header) {
+        checkNotNull(validHeaderName);
+        checkArgument(!validHeaderName.isEmpty(), "The header name must not be empty");
+        checkNotNull(header);
+        return new PiHeaderUnion(true, validHeaderName, header);
+    }
+
+    /**
+     * Returns true if this header is valid, false otherwise.
+     *
+     * @return a boolean value
+     */
+    public boolean isValid() {
+        return this.isValid;
+    }
+
+    /**
+     * Return the header name.
+     *
+     * @return header name, return null if the header union invalid
+     */
+    public String headerName() {
+        return this.validHeaderName;
+    }
+
+    /**
+     * Return the header.
+     *
+     * @return header, return null if the header union invalid
+     */
+    public PiHeader header() {
+        return this.header;
+    }
+
+    @Override
+    public Type type() {
+        return Type.HEADERUNION;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        PiHeaderUnion headerUnion = (PiHeaderUnion) o;
+        return Objects.equal(validHeaderName, headerUnion.validHeaderName) &&
+                Objects.equal(header, headerUnion.header);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(validHeaderName, header);
+    }
+
+    @Override
+    public String toString() {
+        return !isValid ? "INVALID" : validHeaderName + ":" + header.toString();
+    }
+}
\ No newline at end of file
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/data/PiHeaderUnionStack.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/data/PiHeaderUnionStack.java
new file mode 100644
index 0000000..a1e39b8
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/data/PiHeaderUnionStack.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * 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.data;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableList;
+import org.onosproject.net.pi.model.PiData;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.StringJoiner;
+
+/**
+ * PiHeaderUnionStack entity in a protocol-independent pipeline.
+ */
+@Beta
+public final class PiHeaderUnionStack implements PiData {
+    private final ImmutableList<PiHeaderUnion> headerUnions;
+
+    /**
+     * Creates a new protocol-independent header union stack instance for the given collection of HeaderUnion.
+     *
+     * @param headerUnions the collection of headerUnion
+     */
+    private PiHeaderUnionStack(List<PiHeaderUnion> headerUnions) {
+        this.headerUnions = ImmutableList.copyOf(headerUnions);
+    }
+
+    /**
+     * Returns a new protocol-independent header union stack.
+     * @param headerUnions the list of headerUnion
+     * @return header union stack
+     */
+    public static PiHeaderUnionStack of(List<PiHeaderUnion> headerUnions) {
+        return new PiHeaderUnionStack(headerUnions);
+    }
+
+    /**
+     * Return the collection of header union.
+     *
+     * @return the collection header union
+     */
+    public Collection<PiHeaderUnion> headerUnions() {
+        return this.headerUnions;
+    }
+
+    @Override
+    public Type type() {
+        return Type.HEADERUNIONSTACK;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        PiHeaderUnionStack headerUnionStack = (PiHeaderUnionStack) o;
+        return Objects.equal(headerUnions, headerUnionStack.headerUnions);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(headerUnions);
+    }
+
+    @Override
+    public String toString() {
+        StringJoiner stringParams = new StringJoiner(", ", "(", ")");
+        this.headerUnions().forEach(p -> stringParams.add(p.toString()));
+        return stringParams.toString();
+    }
+}
\ No newline at end of file
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/data/PiStruct.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/data/PiStruct.java
new file mode 100644
index 0000000..7c60c16
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/data/PiStruct.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * 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.data;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableList;
+import org.onosproject.net.pi.model.PiData;
+
+import java.util.List;
+import java.util.StringJoiner;
+
+/**
+ * Struct entity in a protocol-independent pipeline.
+ */
+@Beta
+public final class PiStruct implements PiData {
+    private final ImmutableList<PiData> struct;
+
+    /**
+     * Creates a new protocol-independent struct instance for the given collection of PiData.
+     *
+     * @param struct the collection of PiData
+     */
+    private PiStruct(List<PiData> struct) {
+        this.struct = ImmutableList.copyOf(struct);
+    }
+
+    /**
+     * Returns a new protocol-independent struct.
+     * @param struct the list of PiData
+     * @return struct
+     */
+    public static PiStruct of(List<PiData> struct) {
+        return new PiStruct(struct);
+    }
+
+    /**
+     * Return protocol-independent struct instance.
+     *
+     * @return the list of PiData
+     */
+    public List<PiData> struct() {
+        return this.struct;
+    }
+
+    @Override
+    public Type type() {
+        return Type.STRUCT;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        PiStruct st = (PiStruct) o;
+        return Objects.equal(struct, st.struct);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(struct);
+    }
+
+    @Override
+    public String toString() {
+        StringJoiner stringParams = new StringJoiner(", ", "(", ")");
+        this.struct().forEach(p -> stringParams.add(p.toString()));
+        return stringParams.toString();
+    }
+}
\ No newline at end of file
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/data/PiTuple.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/data/PiTuple.java
new file mode 100644
index 0000000..e88fede
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/data/PiTuple.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * 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.data;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableList;
+import org.onosproject.net.pi.model.PiData;
+
+import java.util.List;
+import java.util.StringJoiner;
+
+/**
+ * Tuple entity in a protocol-independent pipeline.
+ */
+@Beta
+public final class PiTuple implements PiData {
+    private final ImmutableList<PiData> tuple;
+
+    /**
+     * Creates a new protocol-independent tuple instance for the given collection of PiData.
+     *
+     * @param tuple the collection of PiData
+     */
+    private PiTuple(List<PiData> tuple) {
+        this.tuple = ImmutableList.copyOf(tuple);
+    }
+
+    /**
+     * Returns a new protocol-independent tuple.
+     * @param tuple the list of PiData
+     * @return tuple
+     */
+    public static PiTuple of(List<PiData> tuple) {
+        return new PiTuple(tuple);
+    }
+
+    /**
+     * Return protocol-independent tuple instance.
+     *
+     * @return the list of PiData
+     */
+    public List<PiData> tuple() {
+        return this.tuple;
+    }
+
+    @Override
+    public Type type() {
+        return Type.TUPLE;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        PiTuple tp = (PiTuple) o;
+        return Objects.equal(tuple, tp.tuple);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(tuple);
+    }
+
+    @Override
+    public String toString() {
+        StringJoiner stringParams = new StringJoiner(", ", "(", ")");
+        this.tuple().forEach(p -> stringParams.add(p.toString()));
+        return stringParams.toString();
+    }
+}
\ No newline at end of file
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/data/package-info.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/data/package-info.java
new file mode 100644
index 0000000..86fa76b
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/data/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * 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.
+ */
+
+/**
+ * Classes abstracting data that can be used for runtime operations of a protocol-independent pipeline.
+ */
+
+package org.onosproject.net.pi.runtime.data;
\ No newline at end of file
diff --git a/core/api/src/test/java/org/onosproject/net/pi/runtime/PiDataTest.java b/core/api/src/test/java/org/onosproject/net/pi/runtime/PiDataTest.java
new file mode 100644
index 0000000..2915156
--- /dev/null
+++ b/core/api/src/test/java/org/onosproject/net/pi/runtime/PiDataTest.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * 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.collect.Lists;
+import com.google.common.testing.EqualsTester;
+import org.junit.Test;
+import org.onlab.util.ImmutableByteSequence;
+import org.onosproject.net.pi.model.PiData;
+import org.onosproject.net.pi.runtime.data.PiBitString;
+import org.onosproject.net.pi.runtime.data.PiBool;
+import org.onosproject.net.pi.runtime.data.PiEnumString;
+import org.onosproject.net.pi.runtime.data.PiErrorString;
+import org.onosproject.net.pi.runtime.data.PiHeader;
+import org.onosproject.net.pi.runtime.data.PiHeaderStack;
+import org.onosproject.net.pi.runtime.data.PiHeaderUnion;
+import org.onosproject.net.pi.runtime.data.PiHeaderUnionStack;
+import org.onosproject.net.pi.runtime.data.PiStruct;
+import org.onosproject.net.pi.runtime.data.PiTuple;
+
+import static org.junit.Assert.assertEquals;
+import static org.onlab.junit.ImmutableClassChecker.assertThatClassIsImmutable;
+
+/**
+ * Unit tests for PiData class.
+ */
+public class PiDataTest {
+    private final PiData bitString1 = PiBitString.of(ImmutableByteSequence.copyFrom(10));
+    private final PiData sameAsBitString1 = PiBitString.of(ImmutableByteSequence.copyFrom(10));
+    private final PiData bitString2 = PiBitString.of(ImmutableByteSequence.copyFrom(20));
+
+    private final PiData bool1 = PiBool.of(true);
+    private final PiData sameAsBool1 = PiBool.of(true);
+    private final PiData bool2 = PiBool.of(false);
+
+    private final PiData tuple1 = PiTuple.of(Lists.newArrayList(bool1));
+    private final PiData sameAsTuple1 = PiTuple.of(Lists.newArrayList(bool1));
+    private final PiData tuple2 = PiTuple.of(Lists.newArrayList(bitString1));
+
+    private final PiData struct1 = PiStruct.of(Lists.newArrayList(bool1));
+    private final PiData sameAsStruct1 = PiStruct.of(Lists.newArrayList(bool1));
+    private final PiData struct2 = PiStruct.of(Lists.newArrayList(bitString1));
+
+    private final PiData header1 = PiHeader.of(true,
+                                               Lists.newArrayList(ImmutableByteSequence.copyFrom(10)));
+    private final PiData sameAsHeader1 = PiHeader.of(true,
+                                                     Lists.newArrayList(ImmutableByteSequence.copyFrom(10)));
+    private final PiData header2 = PiHeader.of(true,
+                                               Lists.newArrayList(ImmutableByteSequence.copyFrom(20)));
+
+    private final PiData headerUnion1 = PiHeaderUnion.of("port", (PiHeader) header1);
+    private final PiData sameAsHeaderUnion1 = PiHeaderUnion.of("port", (PiHeader) header1);
+    private final PiData headerUnion2 =  PiHeaderUnion.of("port", (PiHeader) header2);
+
+    private final PiData inValidHeaderUnion1 = PiHeaderUnion.ofInvalid();
+    private final PiData sameAsInvalidHeaderUnion1 = PiHeaderUnion.ofInvalid();
+
+    private final PiData headerStack1 = PiHeaderStack.of(Lists.newArrayList((PiHeader) header1));
+    private final PiData sameAsHeaderStack1 = PiHeaderStack.of(Lists.newArrayList((PiHeader) header1));
+    private final PiData headerStack2 = PiHeaderStack.of(Lists.newArrayList((PiHeader) header2));
+
+    private final PiData headerUnionStack1 = PiHeaderUnionStack.of(Lists.newArrayList(
+            (PiHeaderUnion) headerUnion1));
+    private final PiData sameAsHeaderUnionStack1 = PiHeaderUnionStack.of(Lists.newArrayList(
+            (PiHeaderUnion) headerUnion1));
+    private final PiData headerUnionStack2 = PiHeaderUnionStack.of(Lists.newArrayList(
+            (PiHeaderUnion) headerUnion2));
+
+    private final PiData enumString1 = PiEnumString.of("test");
+    private final PiData sameAsEnumString1 = PiEnumString.of("test");
+    private final PiData enumString2 = PiEnumString.of("test1");
+
+    private final PiData errorString1 = PiErrorString.of("failed");
+    private final PiData sameAsErrorString1 = PiErrorString.of("failed");
+    private final PiData errorString2 = PiErrorString.of("success");
+
+    /**
+     * Checks that the PiData classes is immutable.
+     */
+    @Test
+    public void testImmutability() {
+        assertThatClassIsImmutable(PiBitString.class);
+        assertThatClassIsImmutable(PiBool.class);
+        assertThatClassIsImmutable(PiTuple.class);
+        assertThatClassIsImmutable(PiStruct.class);
+        assertThatClassIsImmutable(PiHeader.class);
+        assertThatClassIsImmutable(PiHeaderStack.class);
+        assertThatClassIsImmutable(PiHeaderUnion.class);
+        assertThatClassIsImmutable(PiHeaderUnionStack.class);
+        assertThatClassIsImmutable(PiEnumString.class);
+        assertThatClassIsImmutable(PiErrorString.class);
+    }
+
+    /**
+     * Checks the PiData type.
+     */
+    @Test
+    public void testPiDataType() {
+        assertEquals(bitString1.type(), PiData.Type.BITSTRING);
+        assertEquals(bool1.type(), PiData.Type.BOOL);
+        assertEquals(tuple1.type(), PiData.Type.TUPLE);
+        assertEquals(struct1.type(), PiData.Type.STRUCT);
+        assertEquals(header1.type(), PiData.Type.HEADER);
+        assertEquals(headerUnion1.type(), PiData.Type.HEADERUNION);
+        assertEquals(headerStack1.type(), PiData.Type.HEADERSTACK);
+        assertEquals(headerUnionStack1.type(), PiData.Type.HEADERUNIONSTACK);
+        assertEquals(enumString1.type(), PiData.Type.ENUMSTRING);
+        assertEquals(errorString1.type(), PiData.Type.ERRORSTRING);
+    }
+
+    /**
+     * Checks the operation of equals(), hashCode() and toString() methods.
+     */
+    @Test
+    public void testEquals() {
+        new EqualsTester()
+                .addEqualityGroup(bitString1, sameAsBitString1)
+                .addEqualityGroup(bitString2)
+                .testEquals();
+
+        new EqualsTester()
+                .addEqualityGroup(bool1, sameAsBool1)
+                .addEqualityGroup(bool2)
+                .testEquals();
+
+        new EqualsTester()
+                .addEqualityGroup(tuple1, sameAsTuple1)
+                .addEqualityGroup(tuple2)
+                .testEquals();
+
+        new EqualsTester()
+                .addEqualityGroup(struct1, sameAsStruct1)
+                .addEqualityGroup(struct2)
+                .testEquals();
+
+        new EqualsTester()
+                .addEqualityGroup(header1, sameAsHeader1)
+                .addEqualityGroup(header2)
+                .testEquals();
+
+        new EqualsTester()
+                .addEqualityGroup(headerUnion1, sameAsHeaderUnion1)
+                .addEqualityGroup(headerUnion2)
+                .testEquals();
+
+        new EqualsTester()
+                .addEqualityGroup(inValidHeaderUnion1, sameAsInvalidHeaderUnion1)
+                .testEquals();
+
+        new EqualsTester()
+                .addEqualityGroup(headerStack1, sameAsHeaderStack1)
+                .addEqualityGroup(headerStack2)
+                .testEquals();
+
+        new EqualsTester()
+                .addEqualityGroup(headerUnionStack1, sameAsHeaderUnionStack1)
+                .addEqualityGroup(headerUnionStack2)
+                .testEquals();
+
+        new EqualsTester()
+                .addEqualityGroup(enumString1, sameAsEnumString1)
+                .addEqualityGroup(enumString2)
+                .testEquals();
+
+        new EqualsTester()
+                .addEqualityGroup(errorString1, sameAsErrorString1)
+                .addEqualityGroup(errorString2)
+                .testEquals();
+    }
+
+}
diff --git a/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/P4DataCodec.java b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/P4DataCodec.java
new file mode 100644
index 0000000..5b0a2c5
--- /dev/null
+++ b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/P4DataCodec.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * 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.p4runtime.ctl;
+
+import com.google.protobuf.ByteString;
+import org.onlab.util.ImmutableByteSequence;
+import org.onosproject.net.pi.runtime.data.PiBitString;
+import org.onosproject.net.pi.runtime.data.PiBool;
+import org.onosproject.net.pi.model.PiData;
+import org.onosproject.net.pi.runtime.data.PiEnumString;
+import org.onosproject.net.pi.runtime.data.PiErrorString;
+import org.onosproject.net.pi.runtime.data.PiHeader;
+import org.onosproject.net.pi.runtime.data.PiHeaderStack;
+import org.onosproject.net.pi.runtime.data.PiHeaderUnion;
+import org.onosproject.net.pi.runtime.data.PiHeaderUnionStack;
+import org.onosproject.net.pi.runtime.data.PiStruct;
+import org.onosproject.net.pi.runtime.data.PiTuple;
+import org.slf4j.Logger;
+import p4.P4Types;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Encoder/decoder of PI Data entry to P4 Data entry protobuf
+ * messages, and vice versa.
+ */
+final class P4DataCodec {
+
+    private static final Logger log = getLogger(P4DataCodec.class);
+
+    private P4DataCodec() {
+        // Hides constructor.
+    }
+
+    private static P4Types.P4Header encodeHeader(PiHeader piHeader) {
+        P4Types.P4Header.Builder builder = P4Types.P4Header.newBuilder();
+        int i = 0;
+        for (ImmutableByteSequence bitString : piHeader.bitStrings()) {
+            builder.setBitstrings(i, ByteString.copyFrom(bitString.asArray()));
+            i++;
+        }
+        return builder.setIsValid(piHeader.isValid()).build();
+    }
+
+    private static PiHeader decodeHeader(P4Types.P4Header p4Header) {
+        List<ImmutableByteSequence> bitStrings = p4Header.getBitstringsList().stream()
+                .map(bit -> ImmutableByteSequence.copyFrom(bit.asReadOnlyByteBuffer()))
+                .collect(Collectors.toList());
+
+        return PiHeader.of(p4Header.getIsValid(), bitStrings);
+    }
+
+    private static P4Types.P4HeaderUnion encodeHeaderUnion(PiHeaderUnion headerUnion) {
+
+        P4Types.P4HeaderUnion.Builder builder = P4Types.P4HeaderUnion.newBuilder();
+        if (headerUnion.isValid()) {
+            builder.setValidHeader(encodeHeader(headerUnion.header()));
+            builder.setValidHeaderName(headerUnion.headerName());
+        } else {
+            // An empty string indicates that none of the union members are valid and
+            // valid_header must therefore be unset.
+            builder.setValidHeaderName("");
+        }
+
+        return builder.build();
+    }
+
+    private static PiHeaderUnion decodeHeaderUnion(P4Types.P4HeaderUnion p4HeaderUnion) {
+
+        return PiHeaderUnion.of(p4HeaderUnion.getValidHeaderName(),
+                                decodeHeader(p4HeaderUnion.getValidHeader()));
+    }
+
+    private static P4Types.P4StructLike encodeStruct(PiStruct piStruct) {
+        P4Types.P4StructLike.Builder builder = P4Types.P4StructLike.newBuilder();
+        builder.addAllMembers(piStruct.struct().stream()
+                                      .map(piData -> encodeP4Data(piData))
+                                      .collect(Collectors.toList()));
+        return builder.build();
+    }
+
+    private static PiStruct decodeStruct(P4Types.P4StructLike p4StructLike) {
+
+        return PiStruct.of(p4StructLike.getMembersList().stream()
+                                   .map(p4Data -> decodeP4Data(p4Data))
+                                   .collect(Collectors.toList()));
+    }
+
+    private static P4Types.P4StructLike encodeTuple(PiTuple piTuple) {
+        P4Types.P4StructLike.Builder builder = P4Types.P4StructLike.newBuilder();
+        builder.addAllMembers(piTuple.tuple().stream()
+                                      .map(piData -> encodeP4Data(piData))
+                                      .collect(Collectors.toList()));
+        return builder.build();
+    }
+
+    private static PiTuple decodeTuple(P4Types.P4StructLike p4StructLike) {
+
+        return PiTuple.of(p4StructLike.getMembersList().stream()
+                                  .map(p4Data -> decodeP4Data(p4Data))
+                                  .collect(Collectors.toList()));
+    }
+
+    static P4Types.P4Data encodeP4Data(PiData piData) {
+
+        P4Types.P4Data.Builder builder = P4Types.P4Data.newBuilder();
+        switch (piData.type()) {
+            case BITSTRING:
+                builder.setBitstring(ByteString.copyFrom(((PiBitString) piData).bitString().asArray()));
+                break;
+            case ENUMSTRING:
+                builder.setEnum(((PiEnumString) piData).enumString());
+                break;
+            case ERRORSTRING:
+                builder.setError(((PiErrorString) piData).errorString());
+                break;
+            case BOOL:
+                builder.setBool(((PiBool) piData).bool());
+                break;
+            case STRUCT:
+                builder.setStruct(encodeStruct((PiStruct) piData));
+                break;
+            case TUPLE:
+                builder.setTuple(encodeTuple((PiTuple) piData));
+                break;
+            case HEADER:
+                builder.setHeader(encodeHeader((PiHeader) piData));
+                break;
+            case HEADERSTACK:
+                P4Types.P4HeaderStack.Builder headerStack =  P4Types.P4HeaderStack.newBuilder();
+                int i = 0;
+                for (PiHeader header : ((PiHeaderStack) piData).headers()) {
+                    headerStack.setEntries(i, encodeHeader(header));
+                    i++;
+                }
+                builder.setHeaderStack(headerStack.build());
+                break;
+            case HEADERUNION:
+                builder.setHeaderUnion(encodeHeaderUnion((PiHeaderUnion) piData));
+                break;
+            case HEADERUNIONSTACK:
+                P4Types.P4HeaderUnionStack.Builder headerUnionStack =  P4Types.P4HeaderUnionStack.newBuilder();
+                int j = 0;
+                for (PiHeaderUnion headerUnion : ((PiHeaderUnionStack) piData).headerUnions()) {
+                    headerUnionStack.setEntries(j, encodeHeaderUnion(headerUnion));
+                    j++;
+                }
+                builder.setHeaderUnionStack(headerUnionStack.build());
+                break;
+            default:
+                break;
+        }
+
+        return builder.build();
+    }
+
+    static PiData decodeP4Data(P4Types.P4Data p4Data) {
+        PiData piData = null;
+
+        switch (p4Data.getDataCase()) {
+            case BITSTRING:
+                piData = PiBitString.of(ImmutableByteSequence.copyFrom(p4Data.getBitstring().asReadOnlyByteBuffer()));
+                break;
+            case BOOL:
+                piData = PiBool.of(p4Data.getBool());
+                break;
+            case TUPLE:
+                piData = decodeTuple(p4Data.getTuple());
+                break;
+            case STRUCT:
+                piData = decodeStruct(p4Data.getStruct());
+                break;
+            case HEADER:
+                piData = decodeHeader(p4Data.getHeader());
+                break;
+            case HEADER_UNION:
+                piData = decodeHeaderUnion(p4Data.getHeaderUnion());
+                break;
+            case HEADER_STACK:
+                piData = PiHeaderStack.of(p4Data.getHeaderStack().getEntriesList().stream()
+                                                  .map(p4header -> decodeHeader(p4header))
+                                                  .collect(Collectors.toList()));
+                break;
+            case HEADER_UNION_STACK:
+                piData = PiHeaderUnionStack.of(p4Data.getHeaderUnionStack()
+                                                       .getEntriesList().stream()
+                                                       .map(p4HeaderUnion -> decodeHeaderUnion(p4HeaderUnion))
+                                                       .collect(Collectors.toList()));
+                break;
+            case ENUM:
+                piData = PiEnumString.of(p4Data.getEnum());
+                break;
+            case ERROR:
+                piData = PiErrorString.of(p4Data.getError());
+                break;
+            case DATA_NOT_SET:
+                break;
+            default:
+                break;
+        }
+
+        return piData;
+    }
+}
\ No newline at end of file