Allows to specify matches, action parameters as strings in PI

Some PI elements can encode in their value a string (e.g., when
a P4Runtime translation is used), for this reason we allow users
to specify matches and action parameters as strings.
From southbound, during decode, we interpret the elements as
string if the P4 model suggests that.

Change-Id: I5884de1500437ab647abc200d65de442e23bd1a8
diff --git a/utils/misc/src/main/java/org/onlab/util/ImmutableByteSequence.java b/utils/misc/src/main/java/org/onlab/util/ImmutableByteSequence.java
index 013f3c5..1710d29 100644
--- a/utils/misc/src/main/java/org/onlab/util/ImmutableByteSequence.java
+++ b/utils/misc/src/main/java/org/onlab/util/ImmutableByteSequence.java
@@ -51,6 +51,7 @@
     The order of a newly-created byte buffer is always BIG_ENDIAN.
      */
     private ByteBuffer value;
+    private boolean isAscii = false;
 
     /**
      * Private constructor. Creates a new byte sequence object backed by the
@@ -65,6 +66,11 @@
         this.value.rewind();
     }
 
+    private ImmutableByteSequence(ByteBuffer value, boolean isAscii) {
+        this(value);
+        this.isAscii = isAscii;
+    }
+
     /**
      * Creates a new immutable byte sequence with the same content and order of
      * the passed byte array.
@@ -126,6 +132,20 @@
     }
 
     /**
+     * Creates a new immutable byte sequence from the given string.
+     *
+     * @param original a string
+     * @return a new byte buffer object
+     */
+    public static ImmutableByteSequence copyFrom(String original) {
+        checkArgument(original != null && original.length() > 0,
+                      "Cannot copy from an empty or null string");
+        return new ImmutableByteSequence(ByteBuffer.allocate(original.length())
+                                                 .put(original.getBytes()),
+                                         true);
+    }
+
+    /**
      * Creates a new byte sequence of 8 bytes containing the given long value.
      *
      * @param original a long value
@@ -394,19 +414,34 @@
     }
 
     /**
-     * Returns a hexadecimal representation of this byte sequence, e.g.
-     * 0xbeef. The length of the returned string is not representative of the
-     * length of the byte sequence, as all padding zeros are removed.
+     * Returns the ASCII representation of the byte sequence if the content can
+     * be interpreted as an ASCII string, otherwise returns the hexadecimal
+     * representation of this byte sequence, e.g.0xbeef. The length of the
+     * returned string is not representative of the length of the byte sequence,
+     * as all padding zeros are removed.
      *
      * @return hexadecimal representation
      */
     @Override
     public String toString() {
-        final String hexValue = HexString
-                .toHexString(value.array(), "")
-                // Remove leading zeros, but leave one if string is all zeros.
-                .replaceFirst("^0+(?!$)", "");
-        return "0x" + hexValue;
+        if (this.isAscii()) {
+            return new String(value.array());
+        } else {
+            return "0x" + HexString
+                    .toHexString(value.array(), "")
+                    // Remove leading zeros, but leave one if string is all zeros.
+                    .replaceFirst("^0+(?!$)", "");
+        }
+    }
+
+    /**
+     * Checks if the content can be interpreted as an ASCII printable string.
+     *
+     * @return True if the content can be interpreted as an ASCII printable
+     *  string, false otherwise
+     */
+    public boolean isAscii() {
+        return isAscii;
     }
 
     /**