More readable toString for BMv2 extension selectors and treatments

Also, added a test for serialization

Change-Id: I77e80fa7597b552c71e80c9d39d03549e0325778
diff --git a/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2ExtensionSelector.java b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2ExtensionSelector.java
index d9d9d0e..1e357c4 100644
--- a/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2ExtensionSelector.java
+++ b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2ExtensionSelector.java
@@ -29,9 +29,10 @@
 import org.onosproject.net.flow.AbstractExtension;
 import org.onosproject.net.flow.criteria.ExtensionSelector;
 import org.onosproject.net.flow.criteria.ExtensionSelectorType;
+import org.onosproject.store.serializers.KryoNamespaces;
 
 import java.nio.ByteBuffer;
-import java.util.HashMap;
+import java.util.Collections;
 import java.util.Map;
 
 import static com.google.common.base.Preconditions.*;
@@ -46,9 +47,8 @@
 @Beta
 public final class Bmv2ExtensionSelector extends AbstractExtension implements ExtensionSelector {
 
-    private final KryoNamespace appKryo = new KryoNamespace.Builder()
-            .register(HashMap.class)
-            .register(Bmv2MatchParam.class)
+    private static final KryoNamespace APP_KRYO = new KryoNamespace.Builder()
+            .register(KryoNamespaces.API)
             .register(Bmv2ExactMatchParam.class)
             .register(Bmv2TernaryMatchParam.class)
             .register(Bmv2LpmMatchParam.class)
@@ -83,12 +83,12 @@
 
     @Override
     public byte[] serialize() {
-        return appKryo.serialize(parameterMap);
+        return APP_KRYO.serialize(parameterMap);
     }
 
     @Override
     public void deserialize(byte[] data) {
-        this.parameterMap = appKryo.deserialize(data);
+        this.parameterMap = APP_KRYO.deserialize(data);
     }
 
     @Override
@@ -110,9 +110,31 @@
 
     @Override
     public String toString() {
-        return MoreObjects.toStringHelper(this)
-                .add("parameterMap", parameterMap)
-                .toString();
+        MoreObjects.ToStringHelper helper = MoreObjects.toStringHelper(this);
+        parameterMap.forEach((name, param) -> {
+            switch (param.type()) {
+                case EXACT:
+                    Bmv2ExactMatchParam e = (Bmv2ExactMatchParam) param;
+                    helper.add(name, e.value());
+                    break;
+                case TERNARY:
+                    Bmv2TernaryMatchParam t = (Bmv2TernaryMatchParam) param;
+                    helper.add(name, t.value() + "&&&" + t.mask());
+                    break;
+                case LPM:
+                    Bmv2LpmMatchParam l = (Bmv2LpmMatchParam) param;
+                    helper.add(name, l.value() + "/" + String.valueOf(l.prefixLength()));
+                    break;
+                case VALID:
+                    Bmv2ValidMatchParam v = (Bmv2ValidMatchParam) param;
+                    helper.add(name, v.flag() ? "VALID" : "NOT_VALID");
+                    break;
+                default:
+                    helper.add(name, param);
+                    break;
+            }
+        });
+        return helper.toString();
     }
 
     /**
@@ -121,7 +143,7 @@
      * @return a BMv2 extension treatment
      */
     public static Bmv2ExtensionSelector empty() {
-        return new Bmv2ExtensionSelector(null);
+        return new Bmv2ExtensionSelector(Collections.emptyMap());
     }
 
     /**
diff --git a/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2ExtensionTreatment.java b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2ExtensionTreatment.java
index b2a9ba3..7062f41 100644
--- a/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2ExtensionTreatment.java
+++ b/protocols/bmv2/api/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2ExtensionTreatment.java
@@ -29,11 +29,14 @@
 import org.onosproject.net.flow.AbstractExtension;
 import org.onosproject.net.flow.instructions.ExtensionTreatment;
 import org.onosproject.net.flow.instructions.ExtensionTreatmentType;
+import org.onosproject.store.serializers.KryoNamespaces;
 
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.StringJoiner;
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
@@ -47,16 +50,25 @@
 @Beta
 public final class Bmv2ExtensionTreatment extends AbstractExtension implements ExtensionTreatment {
 
-    private final KryoNamespace appKryo = new KryoNamespace.Builder().build();
+    private static final KryoNamespace APP_KRYO = new KryoNamespace.Builder()
+            .register(KryoNamespaces.API)
+            .register(Bmv2ExtensionTreatment.class)
+            .register(Bmv2Action.class)
+            .build();
+
+    private List<String> parameterNames;
     private Bmv2Action action;
 
     /**
      * Creates a new extension treatment for the given BMv2 action.
+     * The list of action parameters name is also required for visualization purposes (i.e. nicer toString()).
      *
-     * @param action an action
+     * @param action         an action
+     * @param parameterNames a list of strings
      */
-    private Bmv2ExtensionTreatment(Bmv2Action action) {
+    private Bmv2ExtensionTreatment(Bmv2Action action, List<String> parameterNames) {
         this.action = action;
+        this.parameterNames = parameterNames;
     }
 
     /**
@@ -75,12 +87,14 @@
 
     @Override
     public byte[] serialize() {
-        return appKryo.serialize(action);
+        return APP_KRYO.serialize(this);
     }
 
     @Override
     public void deserialize(byte[] data) {
-        action = appKryo.deserialize(data);
+        Bmv2ExtensionTreatment other = APP_KRYO.deserialize(data);
+        action = other.action;
+        parameterNames = other.parameterNames;
     }
 
     @Override
@@ -102,8 +116,12 @@
 
     @Override
     public String toString() {
+        StringJoiner stringJoiner = new StringJoiner(", ", "(", ")");
+        for (int i = 0; i < parameterNames.size(); i++) {
+            stringJoiner.add(parameterNames.get(i) + "=" + action.parameters().get(i));
+        }
         return MoreObjects.toStringHelper(this)
-                .add("action", action)
+                .addValue(action.name() + stringJoiner.toString())
                 .toString();
     }
 
@@ -113,7 +131,7 @@
      * @return a BMv2 extension treatment
      */
     public static Bmv2ExtensionTreatment empty() {
-        return new Bmv2ExtensionTreatment(null);
+        return new Bmv2ExtensionTreatment(null, Collections.emptyList());
     }
 
     /**
@@ -232,6 +250,7 @@
                           "invalid number of parameters", actionName);
 
             List<ImmutableByteSequence> newParameters = new ArrayList<>(parameters.size());
+            List<String> parameterNames = new ArrayList<>(parameters.size());
 
             for (String parameterName : parameters.keySet()) {
                 Bmv2RuntimeDataModel runtimeData = actionModel.runtimeData(parameterName);
@@ -242,13 +261,14 @@
                     ImmutableByteSequence newSequence = fitByteSequence(parameters.get(parameterName), bitWidth);
                     int idx = actionModel.runtimeDatas().indexOf(runtimeData);
                     newParameters.add(idx, newSequence);
+                    parameterNames.add(idx, parameterName);
                 } catch (Bmv2TranslatorUtils.ByteSequenceFitException e) {
                     throw new IllegalArgumentException(e.getMessage() +
                                                                " [" + actionName + "->" + runtimeData.name() + "]");
                 }
             }
 
-            return new Bmv2ExtensionTreatment(new Bmv2Action(actionName, newParameters));
+            return new Bmv2ExtensionTreatment(new Bmv2Action(actionName, newParameters), parameterNames);
         }