Define a new intent type which supports point-to-point connevtivity

This patch is for ONOS-1655.

- Define PointToPointIntent
- Override equals(), hashCode(), and toString() in PacketMatch
  because of necessity in the unit test for PointToPointIntent

Change-Id: I28467af30626e16808cbc26818fac2386734082e
diff --git a/src/main/java/net/onrc/onos/core/matchaction/match/PacketMatch.java b/src/main/java/net/onrc/onos/core/matchaction/match/PacketMatch.java
index 0a6caff..3584038 100644
--- a/src/main/java/net/onrc/onos/core/matchaction/match/PacketMatch.java
+++ b/src/main/java/net/onrc/onos/core/matchaction/match/PacketMatch.java
@@ -1,5 +1,6 @@
 package net.onrc.onos.core.matchaction.match;
 
+import com.google.common.base.Objects;
 import net.floodlightcontroller.util.MACAddress;
 import net.onrc.onos.core.util.IPv4;
 
@@ -75,4 +76,36 @@
     public IPv4 getDstIpAddress() {
         return dstIpAddress;
     }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(srcMacAddress, dstMacAddress, srcIpAddress, dstIpAddress);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == this) {
+            return true;
+        }
+
+        if (!(obj instanceof PacketMatch)) {
+            return false;
+        }
+
+        PacketMatch that = (PacketMatch) obj;
+        return Objects.equal(this.srcMacAddress, that.srcMacAddress)
+                && Objects.equal(this.dstMacAddress, that.dstMacAddress)
+                && Objects.equal(this.srcIpAddress, that.srcIpAddress)
+                && Objects.equal(this.dstIpAddress, that.dstIpAddress);
+    }
+
+    @Override
+    public String toString() {
+        return Objects.toStringHelper(this)
+                .add("srcMacAddress", srcMacAddress)
+                .add("dstMacAddress", dstMacAddress)
+                .add("srcIpAddress", srcIpAddress)
+                .add("dstIpAddress", dstIpAddress)
+                .toString();
+    }
 }
diff --git a/src/main/java/net/onrc/onos/core/newintent/PointToPointIntent.java b/src/main/java/net/onrc/onos/core/newintent/PointToPointIntent.java
new file mode 100644
index 0000000..e09c08b
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/newintent/PointToPointIntent.java
@@ -0,0 +1,149 @@
+package net.onrc.onos.core.newintent;
+
+import com.google.common.base.Objects;
+import net.onrc.onos.api.intent.Intent;
+import net.onrc.onos.api.intent.IntentId;
+import net.onrc.onos.core.matchaction.match.IMatch;
+import net.onrc.onos.core.util.Pair;
+import net.onrc.onos.core.util.SwitchPort;
+
+import java.util.concurrent.TimeUnit;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * This class represents point-to-point connectivity.
+ */
+public class PointToPointIntent extends Intent {
+    private final SwitchPort ingressPort;
+    private final SwitchPort egressPort;
+    private final IMatch match;
+    private final long idleTimeout;
+    private final TimeUnit unit;
+
+    /**
+     * Constructs an intent representing ingress port to egress port
+     * connectivity with a given match condition.
+     *
+     * @param id ID of this Intent object.
+     * @param ingressPort the ingress port where a path is originated.
+     * @param egressPort the egress port where a path is terminated.
+     * @param match the match condition of a path established by this intent.
+     */
+    public PointToPointIntent(IntentId id,
+                              SwitchPort ingressPort, SwitchPort egressPort,
+                              IMatch match) {
+        this(id, ingressPort, egressPort, match, 0, TimeUnit.SECONDS);
+    }
+
+    /**
+     * Constructs an intent representing ingress port to egress port
+     * connectivity with a given match condition
+     * and idle timeout.
+     *
+     * @param id ID of this Intent object.
+     * @param ingressPort the ingress port where a path is originated.
+     * @param egressPort the egress port where a path is terminated.
+     * @param match the match condition of a path established by this intent.
+     * @param idleTimeout the maximum time to be idle.
+     * @param unit the unit of the idleTimeout argument.
+     */
+    public PointToPointIntent(IntentId id,
+                              SwitchPort ingressPort, SwitchPort egressPort,
+                              IMatch match, long idleTimeout, TimeUnit unit) {
+        super(id);
+
+        checkArgument(idleTimeout >= 0, "idleTimeout should not be negative");
+
+        this.ingressPort = checkNotNull(ingressPort);
+        this.egressPort = checkNotNull(egressPort);
+        this.match = checkNotNull(match);
+        this.idleTimeout = idleTimeout;
+        this.unit = checkNotNull(unit);
+    }
+
+    /**
+     * Returns the ingress port.
+     *
+     * @return the ingress port.
+     */
+    public SwitchPort getIngressPort() {
+        return ingressPort;
+    }
+
+    /**
+     * Returns the egress port.
+     *
+     * @return the egress port.
+     */
+    public SwitchPort getEgressPort() {
+        return egressPort;
+    }
+
+    /**
+     * Returns the match condition.
+     *
+     * @return the match condition.
+     */
+    public IMatch getMatch() {
+        return match;
+    }
+
+    /**
+     * Returns the idle timeout.
+     *
+     * @return the idle timeout.
+     */
+    public Pair<Long, TimeUnit> getIdleTimeout() {
+        return new Pair<>(idleTimeout, unit);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(getId(), ingressPort, egressPort, match, idleTimeout, unit);
+    }
+
+    /**
+     * Compares the specified object with this intent for equality.
+     *
+     * Note: Comparison of idleTimeout value is done in micro-second precision.
+     * Then the value less than a micro second is truncated. In addition, the comparison
+     * is done between long values. It causes overflow if the idleTimeout is large.
+     *
+     * @param obj the object to be compared with this intent for equality.
+     * @return true if the specified object is equal to this intent.
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == this) {
+            return true;
+        }
+
+        if (!(obj instanceof PointToPointIntent)) {
+            return false;
+        }
+
+        PointToPointIntent that = (PointToPointIntent) obj;
+        long thisIdleTimeoutInMicro = this.unit.toMicros(this.idleTimeout);
+        long thatIdleTimeoutInMicro = that.unit.toMicros(that.idleTimeout);
+
+        return Objects.equal(this.getId(), that.getId())
+                && Objects.equal(this.ingressPort, that.ingressPort)
+                && Objects.equal(this.egressPort, that.egressPort)
+                && Objects.equal(this.match, that.match)
+                && Objects.equal(thisIdleTimeoutInMicro, thatIdleTimeoutInMicro);
+    }
+
+    @Override
+    public String toString() {
+        return Objects.toStringHelper(this)
+                .add("id", getId())
+                .add("ingressPort", ingressPort)
+                .add("egressPort", egressPort)
+                .add("match", match)
+                .add("idleTimeout", idleTimeout)
+                .add("unit", unit)
+                .toString();
+    }
+}
diff --git a/src/test/java/net/onrc/onos/core/newintent/PointToPointIntentTest.java b/src/test/java/net/onrc/onos/core/newintent/PointToPointIntentTest.java
new file mode 100644
index 0000000..ea57863
--- /dev/null
+++ b/src/test/java/net/onrc/onos/core/newintent/PointToPointIntentTest.java
@@ -0,0 +1,48 @@
+package net.onrc.onos.core.newintent;
+
+import net.onrc.onos.api.intent.IntentId;
+import net.onrc.onos.core.matchaction.match.PacketMatch;
+import net.onrc.onos.core.util.SwitchPort;
+import org.junit.Test;
+
+import java.util.concurrent.TimeUnit;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+public class PointToPointIntentTest {
+    private final IntentId id = new IntentId(0);
+    private final SwitchPort port1 = new SwitchPort(0L, 0L);
+    private final SwitchPort port2 = new SwitchPort(1L, 1L);
+    private final PacketMatch match = new PacketMatch();
+
+    @Test
+    public void testEqualityWhenIdleTimeoutIsZero() {
+        PointToPointIntent intent1 = new PointToPointIntent(id, port1, port2, match,
+                0, TimeUnit.DAYS);
+        PointToPointIntent intent2 = new PointToPointIntent(id, port1, port2, match,
+                0, TimeUnit.SECONDS);
+
+        assertThat(intent1, is(intent2));
+    }
+
+    @Test
+    public void testEqualityWhenIdleTimeoutIsOneSecond() {
+        PointToPointIntent intent1 = new PointToPointIntent(id, port1, port2, match,
+                1, TimeUnit.SECONDS);
+        PointToPointIntent intent2 = new PointToPointIntent(id, port1, port2, match,
+                1000, TimeUnit.MILLISECONDS);
+
+        assertThat(intent1, is(intent2));
+    }
+
+    @Test
+    public void testEqualityWhenIdleTimeoutIsTruncated() {
+        PointToPointIntent intent1 = new PointToPointIntent(id, port1, port2, match,
+                1, TimeUnit.MICROSECONDS);
+        PointToPointIntent intent2 = new PointToPointIntent(id, port1, port2, match,
+                1001, TimeUnit.NANOSECONDS);
+
+        assertThat(intent1, is(intent2));
+    }
+}