Add LinkTuple

- Object to identify a link between ports.
-- Switch -> Dpid, Port -> SwitchPort, Link -> LinkTuple

Implicit assumption here is that even if we allow multiple Links departing from a Port,
their destination will be different ports.

Change-Id: Iad3fb068e5dab0a1b1bea63f257bce2a8e773fb3
diff --git a/src/main/java/net/onrc/onos/core/util/LinkTuple.java b/src/main/java/net/onrc/onos/core/util/LinkTuple.java
new file mode 100644
index 0000000..4e80826
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/util/LinkTuple.java
@@ -0,0 +1,99 @@
+package net.onrc.onos.core.util;
+
+import java.util.Objects;
+
+import org.apache.commons.lang.Validate;
+
+
+/**
+ * Immutable class to identify a Link between 2 ports.
+ */
+public final class LinkTuple {
+
+    private final SwitchPort src;
+    private final SwitchPort dst;
+
+    /**
+     * Default constructor for Serializer.
+     */
+    protected LinkTuple() {
+        src = null;
+        dst = null;
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param src source port
+     * @param dst destination port
+     */
+    public LinkTuple(SwitchPort src, SwitchPort dst) {
+        Validate.notNull(src);
+        Validate.notNull(dst);
+
+        this.src = src;
+        this.dst = dst;
+    }
+
+    /**
+     * Creates the Link object.
+     *
+     * @param srcDpid source switch DPID
+     * @param srcPortNo source port number
+     * @param dstDpid destination switch DPID
+     * @param dstPortNo destination port number
+     */
+    public LinkTuple(Dpid srcDpid, PortNumber srcPortNo,
+                     Dpid dstDpid, PortNumber dstPortNo) {
+        this(new SwitchPort(srcDpid, srcPortNo),
+             new SwitchPort(dstDpid, dstPortNo));
+    }
+
+    /**
+     * Gets the source port.
+     *
+     * @return source port
+     */
+    public SwitchPort getSrc() {
+        return src;
+    }
+
+    /**
+     * Gets the destination port.
+     *
+     * @return destination port
+     */
+    public SwitchPort getDst() {
+        return dst;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((src == null) ? 0 : src.hashCode());
+        result = prime * result + ((dst == null) ? 0 : dst.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        LinkTuple other = (LinkTuple) obj;
+        return Objects.equals(src, other.src) &&
+                Objects.equals(dst, other.dst);
+    }
+
+    @Override
+    public String toString() {
+        return "(" + src + "=>" + dst + ")";
+    }
+}
diff --git a/src/main/java/net/onrc/onos/core/util/SwitchPort.java b/src/main/java/net/onrc/onos/core/util/SwitchPort.java
index b9e1964..cbe4bcb 100644
--- a/src/main/java/net/onrc/onos/core/util/SwitchPort.java
+++ b/src/main/java/net/onrc/onos/core/util/SwitchPort.java
@@ -2,6 +2,7 @@
 
 import net.onrc.onos.core.util.serializers.SwitchPortSerializer;
 
+import org.apache.commons.lang.Validate;
 import org.codehaus.jackson.map.annotate.JsonSerialize;
 
 /**
@@ -28,6 +29,8 @@
      * @param port the port to use.
      */
     public SwitchPort(Dpid dpid, PortNumber port) {
+        Validate.notNull(dpid);
+        Validate.notNull(port);
         this.dpid = dpid;
         this.port = port;
     }
diff --git a/src/test/java/net/onrc/onos/core/util/LinkTupleTest.java b/src/test/java/net/onrc/onos/core/util/LinkTupleTest.java
new file mode 100644
index 0000000..88c2ca9
--- /dev/null
+++ b/src/test/java/net/onrc/onos/core/util/LinkTupleTest.java
@@ -0,0 +1,151 @@
+package net.onrc.onos.core.util;
+
+import static net.onrc.onos.core.util.ImmutableClassChecker.assertThatClassIsImmutable;
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+/**
+ * Basic LinkTupleTest.
+ */
+public class LinkTupleTest {
+
+    private static final Dpid SRC_DPID = new Dpid(9);
+    private static final PortNumber SRC_PORT_NUM = new PortNumber((short) 56);
+    private static final Dpid DST_DPID = new Dpid(12);
+    private static final PortNumber DST_PORT_NUM = new PortNumber((short) 81);
+
+    private static final SwitchPort SRC = new SwitchPort(SRC_DPID, SRC_PORT_NUM);
+    private static final SwitchPort DST = new SwitchPort(DST_DPID, DST_PORT_NUM);
+
+
+    private static final LinkTuple L1 = new LinkTuple(SRC, DST);
+    private static final LinkTuple L2 = new LinkTuple(
+            new SwitchPort(new Dpid(1), new PortNumber((short) 65535)),
+            new SwitchPort(new Dpid(2), new PortNumber((short) 65534)));
+
+    /**
+     * Test to confirm class definition is immutable.
+     */
+    @Test
+    public void testImmutable() {
+        assertThatClassIsImmutable(LinkTuple.class);
+    }
+
+    /**
+     * Tests to confirm 2-arg constructor.
+     */
+    @Test
+    public void testLinkTupleSwitchPortSwitchPort() {
+        LinkTuple link = new LinkTuple(SRC, DST);
+        assertEquals(SRC, link.getSrc());
+        assertEquals(SRC_DPID, link.getSrc().getDpid());
+        assertEquals(SRC_PORT_NUM, link.getSrc().getPortNumber());
+        assertEquals(DST, link.getDst());
+        assertEquals(DST_DPID, link.getDst().getDpid());
+        assertEquals(DST_PORT_NUM, link.getDst().getPortNumber());
+    }
+
+    /**
+     * Tests to confirm constructors input validation.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testLinkTupleSwitchPortSwitchPortFailsOnNull1() {
+        new LinkTuple(null, DST);
+    }
+
+    /**
+     * Tests to confirm constructors input validation.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testLinkTupleSwitchPortSwitchPortFailsOnNull2() {
+        new LinkTuple(SRC, null);
+    }
+
+    /**
+     * Tests to confirm 4-arg constructor.
+     */
+    @Test
+    public void testLinkTupleDpidPortNumberDpidPortNumber() {
+        LinkTuple link = new LinkTuple(SRC_DPID, SRC_PORT_NUM, DST_DPID, DST_PORT_NUM);
+        assertEquals(SRC, link.getSrc());
+        assertEquals(SRC_DPID, link.getSrc().getDpid());
+        assertEquals(SRC_PORT_NUM, link.getSrc().getPortNumber());
+        assertEquals(DST, link.getDst());
+        assertEquals(DST_DPID, link.getDst().getDpid());
+        assertEquals(DST_PORT_NUM, link.getDst().getPortNumber());
+    }
+
+    /**
+     * Tests to confirm constructors input validation.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testLinkTupleDpidPortNumberDpidPortNumberFailOnNull1() {
+        new LinkTuple(null, SRC_PORT_NUM, DST_DPID, DST_PORT_NUM);
+    }
+
+    /**
+     * Tests to confirm constructors input validation.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testLinkTupleDpidPortNumberDpidPortNumberFailOnNull2() {
+        new LinkTuple(SRC_DPID, null, DST_DPID, DST_PORT_NUM);
+    }
+
+    /**
+     * Tests to confirm constructors input validation.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testLinkTupleDpidPortNumberDpidPortNumberFailOnNull3() {
+        new LinkTuple(SRC_DPID, SRC_PORT_NUM, null, DST_PORT_NUM);
+    }
+
+    /**
+     * Tests to confirm constructors input validation.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testLinkTupleDpidPortNumberDpidPortNumberFailOnNull4() {
+        new LinkTuple(SRC_DPID, SRC_PORT_NUM, DST_DPID, null);
+    }
+
+    /**
+     * Tests confirming equals returning true.
+     */
+    @Test
+    public void testEqualsTrue() {
+        LinkTuple link = new LinkTuple(SRC, DST);
+
+        assertTrue(L1.equals(link));
+        assertTrue(link.equals(L1));
+        assertEquals(L1.hashCode(), link.hashCode());
+
+        assertTrue(link.equals(link));
+    }
+
+    /**
+     * Tests confirming equals returning false.
+     */
+    @Test
+    public void testEqualsFalse() {
+        LinkTuple link = new LinkTuple(SRC, DST);
+
+        assertFalse(L2.equals(link));
+        assertFalse(link.equals(L2));
+        assertNotEquals(L2.hashCode(), link.hashCode());
+
+        assertFalse(link.equals(null));
+
+        assertFalse(link.equals(DST));
+    }
+
+    /**
+     * Test to detect string representation change.
+     */
+    @Test
+    public void testToString() {
+        // FIXME when we start handling unsigned integer properly
+        assertEquals("(00:00:00:00:00:00:00:01/-1=>00:00:00:00:00:00:00:02/-2)",
+                     L2.toString());
+    }
+
+}