Merge into master from pull request #277:
java_gen.PrimitiveSinkUtils - add putNullableTo (https://github.com/floodlight/loxigen/pull/277)
diff --git a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/util/PrimitiveSinkUtils.java b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/util/PrimitiveSinkUtils.java
index 9a1401d..0f7400e 100644
--- a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/util/PrimitiveSinkUtils.java
+++ b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/util/PrimitiveSinkUtils.java
@@ -3,6 +3,8 @@
 import java.util.List;
 import java.util.SortedSet;
 
+import javax.annotation.Nullable;
+
 import org.projectfloodlight.openflow.types.PrimitiveSinkable;
 
 import com.google.common.hash.PrimitiveSink;
@@ -14,6 +16,28 @@
 public class PrimitiveSinkUtils {
     private PrimitiveSinkUtils() {}
 
+    /** puts a nullable element into a primitive sink. The entry is terminated by a null byte
+     *  to disambiguate null elements.
+     *
+     * @param sink the sink to put the object
+     * @param nullableObj the nullable object
+     */
+    public static void putNullableTo(PrimitiveSink sink,
+            @Nullable PrimitiveSinkable nullableObj) {
+        if(nullableObj != null)
+            nullableObj.putTo(sink);
+
+        // terminate this object representation by a null byte. this ensures that we get
+        // unique digests even if some values are null
+        sink.putByte((byte) 0);
+    }
+
+    /** puts the elements of a sorted set into the {@link PrimitiveSink}. Does not support null
+     *  elements. The elements are assumed to be self-delimitating.
+     *
+     * @param sink
+     * @param set
+     */
     public static void putSortedSetTo(PrimitiveSink sink,
             SortedSet<? extends PrimitiveSinkable> set) {
         for(PrimitiveSinkable e: set) {
@@ -21,6 +45,12 @@
         }
     }
 
+    /** puts the elements of a list into the {@link PrimitiveSink}. Does not support null
+     *  elements. The elements are assumed to be self-delimitating.
+     *
+     * @param sink
+     * @param set
+     */
     public static void putListTo(PrimitiveSink sink,
             List<? extends PrimitiveSinkable> set) {
         for(PrimitiveSinkable e: set) {
diff --git a/java_gen/pre-written/src/test/java/org/projectfloodlight/openflow/util/PrimitiveSinkUtilsTest.java b/java_gen/pre-written/src/test/java/org/projectfloodlight/openflow/util/PrimitiveSinkUtilsTest.java
new file mode 100644
index 0000000..5c2f9e6
--- /dev/null
+++ b/java_gen/pre-written/src/test/java/org/projectfloodlight/openflow/util/PrimitiveSinkUtilsTest.java
@@ -0,0 +1,105 @@
+package org.projectfloodlight.openflow.util;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.not;
+import static org.junit.Assert.assertThat;
+
+import java.util.Arrays;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.projectfloodlight.openflow.types.OFPort;
+import org.projectfloodlight.openflow.types.PrimitiveSinkable;
+
+import com.google.common.collect.ImmutableSortedSet;
+import com.google.common.hash.HashCode;
+import com.google.common.hash.HashFunction;
+import com.google.common.hash.Hasher;
+import com.google.common.hash.Hashing;
+
+public class PrimitiveSinkUtilsTest {
+
+    private HashFunction hash;
+
+    @Before
+    public void setup() {
+        hash = Hashing.murmur3_128();
+    }
+
+    @Test
+    public void testPutNullable() {
+        // test that these different invocations of putNullable
+        // differ pairwise
+        HashCode[] hs = new HashCode[] {
+                calcPutNullables(),
+                calcPutNullables(OFPort.of(1)),
+                calcPutNullables(OFPort.of(1), null),
+                calcPutNullables(OFPort.of(1), null, null),
+                calcPutNullables(null, OFPort.of(1), null),
+                calcPutNullables(null, null, OFPort.of(1))
+        };
+
+        checkPairwiseDifferent(hs);
+    }
+
+    private void checkPairwiseDifferent(HashCode[] hs) {
+        for(int i=0;i<hs.length;i++) {
+            for(int j=i+1; j<hs.length;j++) {
+                assertThat(hs[i], not(hs[j]));
+            }
+        }
+    }
+
+    @Test
+    public void testPutList() {
+        HashCode[] hs = new HashCode[] {
+                calcPutList(),
+                calcPutList(OFPort.of(1)),
+                calcPutList(OFPort.of(2)),
+                calcPutList(OFPort.of(1), OFPort.of(2)),
+                calcPutList(OFPort.of(2), OFPort.of(1)),
+                calcPutList(OFPort.of(1), OFPort.of(3)),
+                calcPutList(OFPort.of(1), OFPort.of(2), OFPort.of(3)),
+        };
+
+        checkPairwiseDifferent(hs);
+    }
+
+    @Test
+    public void testPutSortedSet() {
+        HashCode[] hs = new HashCode[] {
+                calcPutSortedSet(),
+                calcPutSortedSet(OFPort.of(1)),
+                calcPutSortedSet(OFPort.of(2)),
+                calcPutSortedSet(OFPort.of(1), OFPort.of(2)),
+                calcPutSortedSet(OFPort.of(1), OFPort.of(3)),
+                calcPutSortedSet(OFPort.of(1), OFPort.of(2), OFPort.of(3)),
+        };
+
+        checkPairwiseDifferent(hs);
+
+        assertThat(calcPutSortedSet(OFPort.of(1), OFPort.of(2)),
+                equalTo(calcPutSortedSet(OFPort.of(2), OFPort.of(1))));
+}
+
+    private HashCode calcPutSortedSet(OFPort... ports) {
+        Hasher h = hash.newHasher();
+        PrimitiveSinkUtils.putSortedSetTo(h, ImmutableSortedSet.copyOf(ports));
+        return h.hash();
+    }
+
+    private HashCode calcPutList(OFPort... ports) {
+        Hasher h = hash.newHasher();
+        PrimitiveSinkUtils.putListTo(h, Arrays.asList(ports));
+        return h.hash();
+    }
+
+
+    private HashCode calcPutNullables(PrimitiveSinkable... ps) {
+        Hasher h = hash.newHasher();
+        for(PrimitiveSinkable p : ps) {
+            PrimitiveSinkUtils.putNullableTo(h, p);
+        }
+        return h.hash();
+    }
+}