Update Match and Action sub-classes for SDN-IP.

- Added ModifyDstMacAction class to modify destination MAC address on the packet
- Added PacketMatchBuilder class to instantiate PacketMatch class
- Updated PacketMatch class to be able to handle IP prefix, src/dst TCP port and ether type
- Added simple unit tests
- This task is a part of ONOS-1879

Change-Id: Ib26f117076dceac3f58b4911fbb936752e719a4a
diff --git a/src/main/java/net/onrc/onos/core/matchaction/MatchAction.java b/src/main/java/net/onrc/onos/core/matchaction/MatchAction.java
index 2ff3299..b1d224e 100644
--- a/src/main/java/net/onrc/onos/core/matchaction/MatchAction.java
+++ b/src/main/java/net/onrc/onos/core/matchaction/MatchAction.java
@@ -1,68 +1,41 @@
 package net.onrc.onos.core.matchaction;
 
-import java.util.Arrays;
 import java.util.List;
 
-import net.floodlightcontroller.util.MACAddress;
 import net.onrc.onos.api.batchoperation.BatchOperationTarget;
 import net.onrc.onos.core.matchaction.action.Action;
-import net.onrc.onos.core.matchaction.action.OutputAction;
 import net.onrc.onos.core.matchaction.match.Match;
-import net.onrc.onos.core.matchaction.match.PacketMatch;
-import net.onrc.onos.core.util.Dpid;
-import net.onrc.onos.core.util.IPv4;
-import net.onrc.onos.core.util.PortNumber;
 import net.onrc.onos.core.util.SwitchPort;
 
 /**
  * A filter and actions for traffic.
  */
 public class MatchAction implements BatchOperationTarget {
-    protected final MatchActionId id;
-    protected SwitchPort port;
-    protected List<Match> matches;
-    protected List<Action> actions;
+    private final MatchActionId id;
+    private final SwitchPort port;
+    private final Match match;
+    private final List<Action> actions;
 
     /**
      * Constructor.
      *
-     * @param id ID for this MatchAction object.
-     * @param port Switch DPID
-     * @param matches The list of Match objects as match condition on the port.
-     * @param actions The list of Action objects as actions on the switch.
+     * @param id ID for this MatchAction object
+     * @param port switch DPID
+     * @param match the Match object as match condition on the port
+     * @param actions the list of Action objects as actions on the switch
      */
-    public MatchAction(String id, SwitchPort port, List<Match> matches,
-            List<Action> actions) {
+    public MatchAction(String id, SwitchPort port, Match match, List<Action> actions) {
         this.id = new MatchActionId(id);
         this.port = port;
-        this.matches = matches;
+        this.match = match;
         this.actions = actions;
     }
 
     /**
-     * Constructor.
-     * <p>
-     * MEMO: This is a sample constructor to create a packet layer match action.
+     * Gets ID for this object.
      *
-     * @param id ID for this MatchAction object.
-     * @param dpid Switch DPID
-     * @param srcPort Source Port
-     * @param srcMac Source Host MAC Address
-     * @param dstMac Destination Host MAC Address
-     * @param srcIp Source IP Address
-     * @param dstIp Destination IP Address
-     * @param dstPort Destination Port
+     * @return the ID for this object
      */
-    // CHECKSTYLE:OFF suppress the warning about too many parameters
-    public MatchAction(String id, Dpid dpid, PortNumber srcPort,
-            MACAddress srcMac, MACAddress dstMac,
-            IPv4 srcIp, IPv4 dstIp, PortNumber dstPort) {
-        // CHECKSTYLE:ON
-        this(id, new SwitchPort(dpid, srcPort),
-                Arrays.asList((Match) new PacketMatch(srcMac, dstMac, srcIp, dstIp)),
-                Arrays.asList((Action) new OutputAction(dstPort)));
-    }
-
     public MatchActionId getId() {
         return id;
     }
@@ -70,7 +43,7 @@
     /**
      * Gets the switch-port which is the target of this match-action.
      *
-     * @return The target switch-port of this match-action.
+     * @return the target switch-port of this match-action
      */
     public SwitchPort getSwitchPort() {
         return port;
@@ -79,16 +52,16 @@
     /**
      * Gets the traffic filter of the match-action.
      *
-     * @return The traffic filter.
+     * @return the traffic filter
      */
-    public List<Match> getMatches() {
-        return matches;
+    public Match getMatch() {
+        return match;
     }
 
     /**
      * Gets the list of actions of the match-action.
      *
-     * @return The list of actions.
+     * @return the list of actions
      */
     public List<Action> getActions() {
         return actions;
diff --git a/src/main/java/net/onrc/onos/core/matchaction/action/ModifyDstMacAction.java b/src/main/java/net/onrc/onos/core/matchaction/action/ModifyDstMacAction.java
new file mode 100644
index 0000000..856b951
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/matchaction/action/ModifyDstMacAction.java
@@ -0,0 +1,32 @@
+package net.onrc.onos.core.matchaction.action;
+
+import net.floodlightcontroller.util.MACAddress;
+
+/**
+ * An action object to modify destination MAC address.
+ * <p>
+ * This class does not have a switch ID. The switch ID is handled by
+ * MatchAction, IFlow or Intent class.
+ */
+public class ModifyDstMacAction implements Action {
+    private final MACAddress dstMac;
+
+    /**
+     * Constructor.
+     *
+     * @param dstMac destination MAC address after the modification
+     */
+    public ModifyDstMacAction(MACAddress dstMac) {
+        this.dstMac = dstMac;
+    }
+
+    /**
+     * Gets the destination MAC address.
+     *
+     * @return the destination MAC address
+     */
+    public MACAddress getDstMac() {
+        return dstMac;
+    }
+
+}
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 39d3c09..487f7a7 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,85 +1,137 @@
 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;
+import net.onrc.onos.core.util.IPv4Net;
+
+import com.google.common.base.Objects;
 
 /**
  * A match object (traffic specifier) for packet nodes, flow-paths and intents.
  * <p>
- * This class does not have a switch ID and a port number. They are handled by
- * MatchAction, IFlow or Intent class.
+ * This class does not have a switch ID and a in-port number. They are handled
+ * by MatchAction, Flow and Intent classes.
+ * <p>
+ * TODO: This class should be extensible.
  */
 public class PacketMatch implements Match {
 
     // Match fields
-    protected MACAddress srcMacAddress;
-    protected MACAddress dstMacAddress;
-    protected IPv4 srcIpAddress;
-    protected IPv4 dstIpAddress;
+    private final MACAddress srcMac;
+    private final MACAddress dstMac;
+    private final Short etherType;
+    private final IPv4Net srcIp;
+    private final IPv4Net dstIp;
+    private final Byte ipProto;
+    private final Short srcTcpPort;
+    private final Short dstTcpPort;
 
     /**
-     * Constructor.
-     */
-    public PacketMatch() {
-        this(null, null, null, null);
-    }
-
-    /**
-     * Constructor.
+     * Package private constructor.
+     * <p>
+     * This class should be instantiated by the builder.
      *
-     * @param srcMac Source Host MAC Address
-     * @param dstMac Destination Host MAC Address
-     * @param srcIp Source IP Address
-     * @param dstIp Destination IP Address
+     * @param srcMac the source host MAC address
+     * @param dstMac the destination host MAC address
+     * @param etherType the Ether type
+     * @param srcIp the source IP address with IP prefix
+     * @param dstIp the destination IP address with IP prefix
+     * @param ipProto
+     * @param srcTcpPort the source TCP port number
+     * @param dstTcpPort the destination TCP port number
      */
-    public PacketMatch(MACAddress srcMac, MACAddress dstMac,
-            IPv4 srcIp, IPv4 dstIp) {
-        this.srcMacAddress = srcMac;
-        this.dstMacAddress = dstMac;
-        this.srcIpAddress = srcIp;
-        this.dstIpAddress = dstIp;
+    // CHECKSTYLE:OFF suppress the warning about too many parameters
+    PacketMatch(MACAddress srcMac, MACAddress dstMac,
+            Short etherType,
+            IPv4Net srcIp, IPv4Net dstIp, Byte ipProto,
+            Short srcTcpPort, Short dstTcpPort) {
+        // CHECKSTYLE:ON
+        this.srcMac = srcMac;
+        this.dstMac = dstMac;
+        this.etherType = etherType;
+        this.srcIp = srcIp;
+        this.dstIp = dstIp;
+        this.ipProto = ipProto;
+        this.srcTcpPort = srcTcpPort;
+        this.dstTcpPort = dstTcpPort;
     }
 
     /**
      * Gets the source host MAC address.
      *
-     * @return The source host MAC address.
+     * @return the source host MAC address
      */
     public MACAddress getSrcMacAddress() {
-        return srcMacAddress;
+        return srcMac;
     }
 
     /**
      * Gets the destination host MAC address.
      *
-     * @return The destination host MAC address.
+     * @return the destination host MAC address
      */
     public MACAddress getDstMacAddress() {
-        return dstMacAddress;
+        return dstMac;
+    }
+
+    /**
+     * Gets the Ether type.
+     *
+     * @return the Ether type
+     */
+    public Short getEtherType() {
+        return etherType;
     }
 
     /**
      * Gets the source host IP address.
      *
-     * @return The source host IP address.
+     * @return the source host IP address
      */
-    public IPv4 getSrcIpAddress() {
-        return srcIpAddress;
+    public IPv4Net getSrcIpAddress() {
+        return srcIp;
     }
 
     /**
      * Gets the destination host IP address.
      *
-     * @return The destination host IP address.
+     * @return the destination host IP address
      */
-    public IPv4 getDstIpAddress() {
-        return dstIpAddress;
+    public IPv4Net getDstIpAddress() {
+        return dstIp;
+    }
+
+    /**
+     * Gets the IP protocol number.
+     *
+     * @return the IP protocol number
+     */
+    public Byte getIpProtocolNumber() {
+        return ipProto;
+    }
+
+    /**
+     * Gets the source TCP port number.
+     *
+     * @return the source TCP port number
+     */
+    public Short getSrcTcpPortNumber() {
+        return srcTcpPort;
+    }
+
+    /**
+     * Gets the destination TCP port number.
+     *
+     * @return the destination TCP port number
+     */
+    public Short getDstTcpPortNumber() {
+        return dstTcpPort;
     }
 
     @Override
     public int hashCode() {
-        return Objects.hashCode(srcMacAddress, dstMacAddress, srcIpAddress, dstIpAddress);
+        return Objects.hashCode(srcMac, dstMac, etherType,
+                srcIp, dstIp, ipProto,
+                srcTcpPort, dstTcpPort);
     }
 
     @Override
@@ -93,19 +145,35 @@
         }
 
         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);
+        return Objects.equal(this.srcMac, that.srcMac)
+                && Objects.equal(this.dstMac, that.dstMac)
+                && Objects.equal(this.etherType, that.etherType)
+                && Objects.equal(this.srcIp, that.srcIp)
+                && Objects.equal(this.dstIp, that.dstIp)
+                && Objects.equal(this.ipProto, that.ipProto)
+                && Objects.equal(this.srcTcpPort, that.srcTcpPort)
+                && Objects.equal(this.dstTcpPort, that.dstTcpPort);
+    }
+
+    private Integer toUnsignedInt(Byte number) {
+        return number == null ? null : Integer.valueOf(number & 0xFF);
+    }
+
+    private Integer toUnsignedInt(Short number) {
+        return number == null ? null : Integer.valueOf(number & 0xFFFF);
     }
 
     @Override
     public String toString() {
         return Objects.toStringHelper(this)
-                .add("srcMacAddress", srcMacAddress)
-                .add("dstMacAddress", dstMacAddress)
-                .add("srcIpAddress", srcIpAddress)
-                .add("dstIpAddress", dstIpAddress)
+                .add("srcMac", srcMac)
+                .add("dstMac", dstMac)
+                .add("etherType", toUnsignedInt(etherType))
+                .add("srcIp", srcIp)
+                .add("dstIp", dstIp)
+                .add("ipProto", toUnsignedInt(ipProto))
+                .add("srcTcpPort", toUnsignedInt(srcTcpPort))
+                .add("dstTcpPort", toUnsignedInt(dstTcpPort))
                 .toString();
     }
 }
diff --git a/src/main/java/net/onrc/onos/core/matchaction/match/PacketMatchBuilder.java b/src/main/java/net/onrc/onos/core/matchaction/match/PacketMatchBuilder.java
new file mode 100644
index 0000000..1ddc720
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/matchaction/match/PacketMatchBuilder.java
@@ -0,0 +1,143 @@
+package net.onrc.onos.core.matchaction.match;
+
+import net.floodlightcontroller.util.MACAddress;
+import net.onrc.onos.core.util.IPv4;
+import net.onrc.onos.core.util.IPv4Net;
+
+/**
+ * A builder to instantiate PacketMatch class.
+ */
+public class PacketMatchBuilder {
+    private MACAddress srcMac = null;
+    private MACAddress dstMac = null;
+    private Short etherType = null;
+    private IPv4Net srcIp = null;
+    private IPv4Net dstIp = null;
+    private Byte ipProto = null;
+    private Short srcTcpPort = null;
+    private Short dstTcpPort = null;
+
+    /**
+     * Sets source MAC address.
+     *
+     * @param mac the source MAC address
+     * @return this builder
+     */
+    public PacketMatchBuilder setSrcMac(MACAddress mac) {
+        srcMac = mac;
+        return this;
+    }
+
+    /**
+     * Sets destination MAC address.
+     *
+     * @param mac the destination MAC address
+     * @return this builder
+     */
+    public PacketMatchBuilder setDstMac(MACAddress mac) {
+        dstMac = mac;
+        return this;
+    }
+
+    /**
+     * Sets Ethernet type.
+     *
+     * @param etherTypeNo the Ethernet type
+     * @return this builder
+     */
+    public PacketMatchBuilder setEtherType(short etherTypeNo) {
+        etherType = etherTypeNo;
+        return this;
+    }
+
+    /**
+     * Sets source IP address with prefix length.
+     *
+     * @param ip the source IP address
+     * @param prefixLen the prefix length
+     * @return this builder
+     */
+    public PacketMatchBuilder setSrcIp(IPv4 ip, short prefixLen) {
+        srcIp = new IPv4Net(ip, prefixLen);
+        return this;
+    }
+
+    /**
+     * Sets source IP address.
+     *
+     * @param ip the source IP address
+     * @return this builder
+     */
+    public PacketMatchBuilder setSrcIp(IPv4 ip) {
+        return setSrcIp(ip, (short) 32);
+    }
+
+    /**
+     * Sets destination IP address with prefix length.
+     *
+     * @param ip the destination IP address
+     * @param prefixLen the prefix length
+     * @return this builder
+     */
+    public PacketMatchBuilder setDstIp(IPv4 ip, short prefixLen) {
+        dstIp = new IPv4Net(ip, prefixLen);
+        return this;
+    }
+
+    /**
+     * Sets destination IP address.
+     *
+     * @param ip the destination IP address
+     * @return this builder
+     */
+    public PacketMatchBuilder setDstIp(IPv4 ip) {
+        return setDstIp(ip, (short) 32);
+    }
+
+    /**
+     * Sets IP protocol number.
+     *
+     * @param ipProtoNo the IP protocol
+     * @return this builder
+     */
+    public PacketMatchBuilder setIpProto(byte ipProtoNo) {
+        ipProto = ipProtoNo;
+        return this;
+    }
+
+    /**
+     * Sets source TCP port number.
+     *
+     * @param tcpPort the source TCP port number
+     * @return this builder
+     */
+    public PacketMatchBuilder setSrcTcpPort(short tcpPort) {
+        srcTcpPort = tcpPort;
+        return this;
+    }
+
+    /**
+     * Sets destination TCP port number.
+     *
+     * @param tcpPort the destination TCP port number
+     * @return this builder
+     */
+    public PacketMatchBuilder setDstTcpPort(short tcpPort) {
+        dstTcpPort = tcpPort;
+        return this;
+    }
+
+    /**
+     * Builds the PacketMatch instance.
+     *
+     * @return the PacketMatch instance
+     */
+    public PacketMatch build() {
+        // TODO: check consistency among fields
+
+        return new PacketMatch(srcMac, dstMac,
+                etherType,
+                srcIp, dstIp, ipProto,
+                srcTcpPort, dstTcpPort);
+    }
+}