diff --git a/src/main/java/net/floodlightcontroller/util/ActionUtils.java b/src/main/java/net/floodlightcontroller/util/ActionUtils.java
index 117226b5788fb042b7ef9e6eb45758ca925feaa8..6d905ab9627e07dbfa4b308546a2821ea609cbca 100644
--- a/src/main/java/net/floodlightcontroller/util/ActionUtils.java
+++ b/src/main/java/net/floodlightcontroller/util/ActionUtils.java
@@ -125,1428 +125,1395 @@ import com.fasterxml.jackson.core.JsonParser;
  * @author Ryan Izard <ryan.izard@bigswitch.com, rizard@g.clemson.edu>
  */
 public class ActionUtils {
-	private static final Logger log = LoggerFactory.getLogger(ActionUtils.class);
+    private static final Logger log = LoggerFactory.getLogger(ActionUtils.class);
 
-	/* OF1.3 ACTIONS (includes OF1.0) */
-	public static final String STR_OUTPUT = "output";
-	public static final String STR_ENQUEUE = "enqueue";
-	public static final String STR_VLAN_STRIP = "strip_vlan";
-	public static final String STR_VLAN_POP = "pop_vlan";
-	public static final String STR_VLAN_PUSH = "push_vlan";
-	public static final String STR_VLAN_SET_PCP = "set_vlan_pcp";
-	public static final String STR_VLAN_SET_VID = "set_vlan_vid";
-	public static final String STR_QUEUE_SET = "set_queue";
-	public static final String STR_DL_SRC_SET = "set_eth_src";
-	public static final String STR_DL_DST_SET = "set_eth_dst";
-	public static final String STR_NW_SRC_SET = "set_ipv4_src";
-	public static final String STR_NW_DST_SET = "set_ipv4_dst";
-	public static final String STR_NW_ECN_SET = "set_ip_ecn";
-	public static final String STR_NW_TOS_SET = "set_ip_tos";
-	public static final String STR_NW_TTL_SET = "set_ip_ttl";
-	public static final String STR_NW_TTL_DEC = "dec_ip_ttl";
-	public static final String STR_TTL_IN_COPY = "copy_ip_ttl_in";
-	public static final String STR_TTL_OUT_COPY = "copy_ip_ttl_out";
-	public static final String STR_MPLS_LABEL_SET = "set_mpls_label";
-	public static final String STR_MPLS_TC_SET = "set_mpls_tc";
-	public static final String STR_MPLS_TTL_SET = "set_mpls_ttl";
-	public static final String STR_MPLS_TTL_DEC = "dec_mpls_ttl";
-	public static final String STR_MPLS_PUSH = "push_mpls";
-	public static final String STR_MPLS_POP = "pop_mpls";
-	public static final String STR_TP_SRC_SET = "set_tp_src";
-	public static final String STR_TP_DST_SET = "set_tp_dst";
-	public static final String STR_PBB_PUSH = "push_pbb";
-	public static final String STR_PBB_POP = "pop_pbb";
-	public static final String STR_GROUP = "group";
-	public static final String STR_FIELD_SET = "set_field";
-	public static final String STR_FIELD_COPY = "copy_field";
-	public static final String STR_METER = "meter";
-	public static final String STR_EXPERIMENTER = "experimenter";
-	public static final String STR_NOT_APPLICABLE = "n/a";
+    /* OF1.3 ACTIONS (includes OF1.0) */
+    public static final String STR_OUTPUT = "output";
+    public static final String STR_ENQUEUE = "enqueue";
+    public static final String STR_VLAN_STRIP = "strip_vlan";
+    public static final String STR_VLAN_POP = "pop_vlan";
+    public static final String STR_VLAN_PUSH = "push_vlan";
+    public static final String STR_VLAN_SET_PCP = "set_vlan_pcp";
+    public static final String STR_VLAN_SET_VID = "set_vlan_vid";
+    public static final String STR_QUEUE_SET = "set_queue";
+    public static final String STR_DL_SRC_SET = "set_eth_src";
+    public static final String STR_DL_DST_SET = "set_eth_dst";
+    public static final String STR_NW_SRC_SET = "set_ipv4_src";
+    public static final String STR_NW_DST_SET = "set_ipv4_dst";
+    public static final String STR_NW_ECN_SET = "set_ip_ecn";
+    public static final String STR_NW_TOS_SET = "set_ip_tos";
+    public static final String STR_NW_TTL_SET = "set_ip_ttl";
+    public static final String STR_NW_TTL_DEC = "dec_ip_ttl";
+    public static final String STR_TTL_IN_COPY = "copy_ip_ttl_in";
+    public static final String STR_TTL_OUT_COPY = "copy_ip_ttl_out";
+    public static final String STR_MPLS_LABEL_SET = "set_mpls_label";
+    public static final String STR_MPLS_TC_SET = "set_mpls_tc";
+    public static final String STR_MPLS_TTL_SET = "set_mpls_ttl";
+    public static final String STR_MPLS_TTL_DEC = "dec_mpls_ttl";
+    public static final String STR_MPLS_PUSH = "push_mpls";
+    public static final String STR_MPLS_POP = "pop_mpls";
+    public static final String STR_TP_SRC_SET = "set_tp_src";
+    public static final String STR_TP_DST_SET = "set_tp_dst";
+    public static final String STR_PBB_PUSH = "push_pbb";
+    public static final String STR_PBB_POP = "pop_pbb";
+    public static final String STR_GROUP = "group";
+    public static final String STR_FIELD_SET = "set_field";
+    public static final String STR_FIELD_COPY = "copy_field";
+    public static final String STR_METER = "meter";
+    public static final String STR_EXPERIMENTER = "experimenter";
+    public static final String STR_NOT_APPLICABLE = "n/a";
 
-	/* OF1.3 set-field operations are defined as any OF1.3 match.
-	 * We will borrow MatchUtils's String definitions of all OF1.3
-	 * set-field operations to be consistent.
-	 */
+    /* OF1.3 set-field operations are defined as any OF1.3 match.
+     * We will borrow MatchUtils's String definitions of all OF1.3
+     * set-field operations to be consistent.
+     */
 
-	private static final JsonFactory jsonFactory = new JsonFactory();
-	private static final String JSON_EMPTY_OBJECT = "{}";
+    private static final JsonFactory jsonFactory = new JsonFactory();
+    private static final String JSON_EMPTY_OBJECT = "{}";
 
 
-	/**
-	 * Returns a String representation of all the OpenFlow actions.
-	 * @param actions; A list of OFActions to encode into one string
-	 * @return A dpctl-style string of the actions
-	 */
-	public static String actionsToString(List<OFAction> actions) {
-		StringBuilder sb = new StringBuilder();
-		for (OFAction a : actions) {
-			if (sb.length() > 0) {
-				sb.append(',');
-			}
-			switch(a.getType()) {
-			case OUTPUT:
-				sb.append(STR_OUTPUT).append("=").append(Integer.toString(((OFActionOutput)a).getPort().getPortNumber()));
-				break;
-			case ENQUEUE:
-				long queue = ((OFActionEnqueue)a).getQueueId();
-				OFPort port = ((OFActionEnqueue)a).getPort();
-				sb.append(STR_ENQUEUE).append("=").append(Integer.toString(port.getPortNumber())).append(":0x").append(String.format("%02x", queue));
-				break;
-			case STRIP_VLAN:
-				sb.append(STR_VLAN_STRIP);
-				break;
-			case POP_VLAN:
-				sb.append(STR_VLAN_POP);
-				break;
-			case PUSH_VLAN:
-				sb.append(STR_VLAN_PUSH).append("=").append(Integer.toString(((OFActionPushVlan)a).getEthertype().getValue()));
-				break;
-			case SET_VLAN_VID:
-				sb.append(STR_VLAN_SET_VID).append("=").append(Short.toString(((OFActionSetVlanVid)a).getVlanVid().getVlan()));
-				break;
-			case SET_VLAN_PCP:
-				sb.append(STR_VLAN_SET_PCP).append("=").append(Byte.toString(((OFActionSetVlanPcp)a).getVlanPcp().getValue()));
-				break;
-			case SET_QUEUE:
-				sb.append(STR_QUEUE_SET).append("=").append(Long.toString(((OFActionSetQueue)a).getQueueId()));
-			case SET_DL_SRC:
-				sb.append(STR_DL_SRC_SET).append("=").append( ((OFActionSetDlSrc)a).getDlAddr().toString());
-				break;
-			case SET_DL_DST:
-				sb.append(STR_DL_DST_SET).append("=").append(((OFActionSetDlDst)a).getDlAddr().toString());
-				break;
-			case SET_NW_ECN:
-				sb.append(STR_NW_ECN_SET).append("=").append(Byte.toString(((OFActionSetNwEcn)a).getNwEcn().getEcnValue()));
-				break;
-			case SET_NW_TOS:
-				sb.append(STR_NW_TOS_SET).append("=").append(Short.toString(((OFActionSetNwTos)a).getNwTos()));
-				break;
-			case SET_NW_TTL:
-				sb.append(STR_NW_TTL_SET).append("=").append(Short.toString(((OFActionSetNwTtl)a).getNwTtl()));
-				break;
-			case DEC_NW_TTL:
-				sb.append(STR_NW_TTL_DEC);
-				break;
-			case SET_MPLS_LABEL:
-				sb.append(STR_MPLS_LABEL_SET).append("=").append(Long.toString(((OFActionSetMplsLabel)a).getMplsLabel()));
-				break;
-			case SET_MPLS_TC:
-				sb.append(STR_MPLS_TC_SET).append("=").append(Short.toString(((OFActionSetMplsTc)a).getMplsTc()));
-				break;
-			case SET_MPLS_TTL:
-				sb.append(STR_MPLS_TTL_SET).append("=").append(Short.toString(((OFActionSetMplsTtl)a).getMplsTtl()));
-				break;
-			case DEC_MPLS_TTL:
-				sb.append(STR_MPLS_TTL_DEC);
-				break;
-			case PUSH_MPLS:
-				sb.append(STR_MPLS_PUSH).append("=").append(Integer.toString(((OFActionPushMpls)a).getEthertype().getValue()));
-				break;
-			case POP_MPLS:
-				sb.append(STR_MPLS_POP).append("=").append(Integer.toString(((OFActionPopMpls)a).getEthertype().getValue()));
-				break;
-			case SET_NW_SRC:
-				sb.append(STR_NW_SRC_SET).append("=").append(((OFActionSetNwSrc)a).getNwAddr().toString());
-				break;
-			case SET_NW_DST:
-				sb.append(STR_NW_DST_SET).append("=").append(((OFActionSetNwDst)a).getNwAddr().toString());
-				break;
-			case SET_TP_SRC:
-				sb.append(STR_TP_SRC_SET).append("=").append(((OFActionSetTpSrc)a).getTpPort().toString());
-				break;
-			case SET_TP_DST:
-				sb.append(STR_TP_DST_SET).append("=").append(((OFActionSetTpDst)a).getTpPort().toString());
-				break;
-			case COPY_TTL_IN:
-				sb.append(STR_TTL_IN_COPY);
-				break;
-			case COPY_TTL_OUT:
-				sb.append(STR_TTL_OUT_COPY);
-				break;
-			case PUSH_PBB:
-				sb.append(STR_PBB_PUSH).append("=").append(Integer.toString(((OFActionPushPbb)a).getEthertype().getValue()));
-				break;
-			case POP_PBB:
-				sb.append(STR_PBB_POP);
-				break;
-			case EXPERIMENTER:
-				sb.append(STR_EXPERIMENTER).append("=").append(Long.toString(((OFActionExperimenter)a).getExperimenter()));
-				break;
-			case GROUP:
-				sb.append(STR_GROUP).append("=").append(Integer.toString(((OFActionGroup)a).getGroup().getGroupNumber()));
-				break;
-			case SET_FIELD:
-				log.debug("Got Set-Field action. Setting {}", ((OFActionSetField)a));
-				/* ARP */
-				if (((OFActionSetField)a).getField() instanceof OFOxmArpOp) {
-					sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_ARP_OPCODE)
-					.append(MatchUtils.SET_FIELD_DELIM)
-					.append(Integer.toString(((OFOxmArpOp) ((OFActionSetField) a).getField()).getValue().getOpcode()));
-				} else if (((OFActionSetField)a).getField() instanceof OFOxmArpSha) {
-					sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_ARP_SHA)
-					.append(MatchUtils.SET_FIELD_DELIM)
-					.append(((OFOxmArpSha) ((OFActionSetField) a).getField()).getValue().toString()); // macaddress formats string already
-				} else if (((OFActionSetField)a).getField() instanceof OFOxmArpTha) {
-					sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_ARP_DHA)
-					.append(MatchUtils.SET_FIELD_DELIM)
-					.append(((OFOxmArpTha) ((OFActionSetField) a).getField()).getValue().toString());
-				} else if (((OFActionSetField)a).getField() instanceof OFOxmArpSpa) {
-					sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_ARP_SPA)
-					.append(MatchUtils.SET_FIELD_DELIM)
-					.append(((OFOxmArpSpa) ((OFActionSetField) a).getField()).getValue().toString()); // ipaddress formats string already
-				} else if (((OFActionSetField)a).getField() instanceof OFOxmArpTpa) {
-					sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_ARP_DPA)
-					.append(MatchUtils.SET_FIELD_DELIM)
-					.append(((OFOxmArpTpa) ((OFActionSetField) a).getField()).getValue().toString()); 
-				} else if (((OFActionSetField)a).getField() instanceof OFOxmIpv6NdSll) {                		
-					sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_IPV6_ND_SLL)
-					.append(MatchUtils.SET_FIELD_DELIM)
-					.append(((OFOxmIpv6NdSll) ((OFActionSetField) a).getField()).getValue().toString()); // macaddress formats string already
-				} else if (((OFActionSetField)a).getField() instanceof OFOxmIpv6NdTll) {                		
-					sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_IPV6_ND_TLL)
-					.append(MatchUtils.SET_FIELD_DELIM)
-					.append(((OFOxmIpv6NdTll) ((OFActionSetField) a).getField()).getValue().toString()); // macaddress formats string already
-				} else if (((OFActionSetField)a).getField() instanceof OFOxmIpv6NdTarget) {                		
-					sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_IPV6_ND_TARGET)
-					.append(MatchUtils.SET_FIELD_DELIM)
-					.append(((OFOxmIpv6NdTarget) ((OFActionSetField) a).getField()).getValue().toString()); 
-				}
-				/* DATA LAYER */
-				else if (((OFActionSetField)a).getField() instanceof OFOxmEthType) {
-					sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_DL_TYPE)
-					.append(MatchUtils.SET_FIELD_DELIM)
-					.append(Integer.toString(((OFOxmEthType) ((OFActionSetField) a).getField()).getValue().getValue()));
-				} else if (((OFActionSetField)a).getField() instanceof OFOxmEthSrc) {
-					sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_DL_SRC)
-					.append(MatchUtils.SET_FIELD_DELIM)
-					.append(((OFOxmEthSrc) ((OFActionSetField) a).getField()).getValue().toString());
-				} else if (((OFActionSetField)a).getField() instanceof OFOxmEthDst) {
-					sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_DL_DST)
-					.append(MatchUtils.SET_FIELD_DELIM)
-					.append(((OFOxmEthDst) ((OFActionSetField) a).getField()).getValue().toString()); 
-				} else if (((OFActionSetField)a).getField() instanceof OFOxmVlanVid) {
-					sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_DL_VLAN)
-					.append(MatchUtils.SET_FIELD_DELIM)
-					.append(Short.toString(((OFOxmVlanVid) ((OFActionSetField) a).getField()).getValue().getVlan())); 
-				} else if (((OFActionSetField)a).getField() instanceof OFOxmVlanPcp) {
-					sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_DL_VLAN_PCP)
-					.append(MatchUtils.SET_FIELD_DELIM)
-					.append(Byte.toString(((OFOxmVlanPcp) ((OFActionSetField) a).getField()).getValue().getValue())); 
-				} 
-				/* ICMP */
-				else if (((OFActionSetField)a).getField() instanceof OFOxmIcmpv4Code) {
-					sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_ICMP_CODE)
-					.append(MatchUtils.SET_FIELD_DELIM)
-					.append(Short.toString(((OFOxmIcmpv4Code) ((OFActionSetField) a).getField()).getValue().getCode())); 
-				} else if (((OFActionSetField)a).getField() instanceof OFOxmIcmpv4Type) {
-					sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_ICMP_TYPE)
-					.append(MatchUtils.SET_FIELD_DELIM)
-					.append(Short.toString(((OFOxmIcmpv4Type) ((OFActionSetField) a).getField()).getValue().getType())); 
-				} else if (((OFActionSetField)a).getField() instanceof OFOxmIcmpv6Code) {                		
-					sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_ICMPV6_CODE)
-					.append(MatchUtils.SET_FIELD_DELIM)
-					.append(Short.toString(((OFOxmIcmpv6Code) ((OFActionSetField) a).getField()).getValue().getRaw())); 
-				}  else if (((OFActionSetField)a).getField() instanceof OFOxmIcmpv6Type) {                		
-					sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_ICMPV6_TYPE)
-					.append(MatchUtils.SET_FIELD_DELIM)
-					.append(Short.toString(((OFOxmIcmpv6Type) ((OFActionSetField) a).getField()).getValue().getRaw())); 
-				}
-				/* NETWORK LAYER */
-				else if (((OFActionSetField)a).getField() instanceof OFOxmIpProto) {
-					sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_NW_PROTO)
-					.append(MatchUtils.SET_FIELD_DELIM)
-					.append(Short.toString(((OFOxmIpProto) ((OFActionSetField) a).getField()).getValue().getIpProtocolNumber())); 
-				} else if (((OFActionSetField)a).getField() instanceof OFOxmIpv4Src) {
-					sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_NW_SRC)
-					.append(MatchUtils.SET_FIELD_DELIM)
-					.append(((OFOxmIpv4Src) ((OFActionSetField) a).getField()).getValue().toString()); 
-				} else if (((OFActionSetField)a).getField() instanceof OFOxmIpv4Dst) {
-					sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_NW_DST)
-					.append(MatchUtils.SET_FIELD_DELIM)
-					.append(((OFOxmIpv4Dst) ((OFActionSetField) a).getField()).getValue().toString()); 
-				} else if (((OFActionSetField)a).getField() instanceof OFOxmIpv6Src) {                		
-					sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_IPV6_SRC)
-					.append(MatchUtils.SET_FIELD_DELIM)
-					.append(((OFOxmIpv6Src) ((OFActionSetField) a).getField()).getValue().toString()); 
-				} else if (((OFActionSetField)a).getField() instanceof OFOxmIpv6Dst) {                		
-					sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_IPV6_DST)
-					.append(MatchUtils.SET_FIELD_DELIM)
-					.append(((OFOxmIpv6Dst) ((OFActionSetField) a).getField()).getValue().toString()); 
-				} else if (((OFActionSetField)a).getField() instanceof OFOxmIpv6Flabel) {                		
-					sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_IPV6_FLOW_LABEL)
-					.append(MatchUtils.SET_FIELD_DELIM)
-					.append(((OFOxmIpv6Flabel) ((OFActionSetField) a).getField()).getValue().toString()); 
-				} else if (((OFActionSetField)a).getField() instanceof OFOxmIpv6Exthdr) {                		
-					sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_IPV6_EXTHDR)
-					.append(MatchUtils.SET_FIELD_DELIM)
-					.append(((OFOxmIpv6Exthdr) ((OFActionSetField) a).getField()).getValue().toString()); 
-				} else if (((OFActionSetField)a).getField() instanceof OFOxmIpEcn) { 
-					sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_NW_ECN)
-					.append(MatchUtils.SET_FIELD_DELIM)
-					.append(Byte.toString(((OFOxmIpEcn) ((OFActionSetField) a).getField()).getValue().getEcnValue())); 
-				} else if (((OFActionSetField)a).getField() instanceof OFOxmIpDscp) {
-					sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_NW_DSCP)
-					.append(MatchUtils.SET_FIELD_DELIM)
-					.append(Byte.toString(((OFOxmIpDscp) ((OFActionSetField) a).getField()).getValue().getDscpValue())); 
-				} 
-				/* TRANSPORT LAYER, TCP, UDP, and SCTP */
-				else if (((OFActionSetField)a).getField() instanceof OFOxmTcpSrc) {
-					sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_TCP_SRC)
-					.append(MatchUtils.SET_FIELD_DELIM)
-					.append(Integer.toString(((OFOxmTcpSrc) ((OFActionSetField) a).getField()).getValue().getPort())); 
-				} else if (((OFActionSetField)a).getField() instanceof OFOxmTcpDst) {
-					sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_TCP_DST)
-					.append(MatchUtils.SET_FIELD_DELIM)
-					.append(Integer.toString(((OFOxmTcpDst) ((OFActionSetField) a).getField()).getValue().getPort())); 
-				} else if (((OFActionSetField)a).getField() instanceof OFOxmUdpSrc) {
-					sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_UDP_SRC)
-					.append(MatchUtils.SET_FIELD_DELIM)
-					.append(Integer.toString(((OFOxmUdpSrc) ((OFActionSetField) a).getField()).getValue().getPort())); 
-				} else if (((OFActionSetField)a).getField() instanceof OFOxmUdpDst) {
-					sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_UDP_DST)
-					.append(MatchUtils.SET_FIELD_DELIM)
-					.append(Integer.toString(((OFOxmUdpDst) ((OFActionSetField) a).getField()).getValue().getPort())); 
-				} else if (((OFActionSetField)a).getField() instanceof OFOxmSctpSrc) {
-					sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_SCTP_SRC)
-					.append(MatchUtils.SET_FIELD_DELIM)
-					.append(Integer.toString(((OFOxmSctpSrc) ((OFActionSetField) a).getField()).getValue().getPort())); 
-				} else if (((OFActionSetField)a).getField() instanceof OFOxmSctpDst) {
-					sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_SCTP_DST)
-					.append(MatchUtils.SET_FIELD_DELIM)
-					.append(Integer.toString(((OFOxmSctpDst) ((OFActionSetField) a).getField()).getValue().getPort())); 
-				} else if (((OFActionSetField)a).getField() instanceof OFOxmTcpFlags) {
-					sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_TCP_FLAGS)
-					.append(MatchUtils.SET_FIELD_DELIM)
-					.append(Integer.toString(((OFOxmTcpFlags) ((OFActionSetField) a).getField()).getValue().getValue())); 
-				}
-				/* MPLS */
-				else if (((OFActionSetField)a).getField() instanceof OFOxmMplsLabel) {
-					sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_MPLS_LABEL)
-					.append(MatchUtils.SET_FIELD_DELIM)
-					.append(Long.toString(((OFOxmMplsLabel) ((OFActionSetField) a).getField()).getValue().getValue())); 
-				} else if (((OFActionSetField)a).getField() instanceof OFOxmMplsTc) {
-					sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_MPLS_TC)
-					.append(MatchUtils.SET_FIELD_DELIM)
-					.append(Short.toString(((OFOxmMplsTc) ((OFActionSetField) a).getField()).getValue().getValue())); 
-				} else if (((OFActionSetField)a).getField() instanceof OFOxmMplsBos) {
-					sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_MPLS_BOS)
-					.append(MatchUtils.SET_FIELD_DELIM)
-					.append(Boolean.toString(((OFOxmMplsBos) ((OFActionSetField) a).getField()).getValue().getValue())); 
-				}
-				/* ACTSET_OUTPUT */
-				else if (((OFActionSetField)a).getField() instanceof OFOxmActsetOutput) {
-					sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_ACTSET_OUTPUT)
-					.append(MatchUtils.SET_FIELD_DELIM)
-					.append(Integer.toString(((OFOxmActsetOutput) ((OFActionSetField) a).getField()).getValue().getPortNumber())); 
-				}
-				/* PACKET_TYPE */
-				else if (((OFActionSetField)a).getField() instanceof OFOxmPacketType) { // TODO hard-coded "/" as delimiter...fix this
-					sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_PACKET_TYPE)
-					.append(MatchUtils.SET_FIELD_DELIM)
-					.append(Integer.toString(((OFOxmPacketType) ((OFActionSetField) a).getField()).getValue().getNamespace())) 
-							.append("/")
-							.append(Integer.toString(((OFOxmPacketType) ((OFActionSetField) a).getField()).getValue().getNsType())); 
-				}
-				/* METADATA */
-				else if (((OFActionSetField)a).getField() instanceof OFOxmMetadata) {
-					sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_METADATA)
-					.append(MatchUtils.SET_FIELD_DELIM)
-					.append(Long.toString(((OFOxmMetadata) ((OFActionSetField) a).getField()).getValue().getValue().getValue())); 
-				} else {
-					log.error("Could not decode Set-Field action field: {}", ((OFActionSetField) a));
-				}
-				break;
-			case COPY_FIELD:
-            	sb.append(STR_FIELD_COPY).append("=").append(copyFieldToJson((OFActionCopyField) a));
-				break;
-			case METER:
-				sb.append(STR_METER).append("=").append(Long.toString(((OFActionMeter)a).getMeterId()));
-				break;
-			default:
-				log.error("Could not decode action: {}", a);
-				break;
-			}
+    /**
+     * Returns a String representation of all the OpenFlow actions.
+     * @param actions; A list of OFActions to encode into one string
+     * @return A dpctl-style string of the actions
+     */
+    public static String actionsToString(List<OFAction> actions) {
+        StringBuilder sb = new StringBuilder();
+        for (OFAction a : actions) {
+            if (sb.length() > 0) {
+                sb.append(',');
+            }
+            switch(a.getType()) {
+            case OUTPUT:
+                sb.append(STR_OUTPUT).append("=").append(Integer.toString(((OFActionOutput)a).getPort().getPortNumber()));
+                break;
+            case ENQUEUE:
+                long queue = ((OFActionEnqueue)a).getQueueId();
+                sb.append(STR_ENQUEUE).append("=").append(portToString(((OFActionEnqueue)a).getPort())).append(":0x").append(String.format("%02x", queue));
+                break;
+            case STRIP_VLAN:
+                sb.append(STR_VLAN_STRIP);
+                break;
+            case POP_VLAN:
+                sb.append(STR_VLAN_POP);
+                break;
+            case PUSH_VLAN:
+                sb.append(STR_VLAN_PUSH).append("=").append(Integer.toString(((OFActionPushVlan)a).getEthertype().getValue()));
+                break;
+            case SET_VLAN_VID:
+                sb.append(STR_VLAN_SET_VID).append("=").append(Short.toString(((OFActionSetVlanVid)a).getVlanVid().getVlan()));
+                break;
+            case SET_VLAN_PCP:
+                sb.append(STR_VLAN_SET_PCP).append("=").append(Byte.toString(((OFActionSetVlanPcp)a).getVlanPcp().getValue()));
+                break;
+            case SET_QUEUE:
+                sb.append(STR_QUEUE_SET).append("=").append(Long.toString(((OFActionSetQueue)a).getQueueId()));
+            case SET_DL_SRC:
+                sb.append(STR_DL_SRC_SET).append("=").append( ((OFActionSetDlSrc)a).getDlAddr().toString());
+                break;
+            case SET_DL_DST:
+                sb.append(STR_DL_DST_SET).append("=").append(((OFActionSetDlDst)a).getDlAddr().toString());
+                break;
+            case SET_NW_ECN:
+                sb.append(STR_NW_ECN_SET).append("=").append(Byte.toString(((OFActionSetNwEcn)a).getNwEcn().getEcnValue()));
+                break;
+            case SET_NW_TOS:
+                sb.append(STR_NW_TOS_SET).append("=").append(Short.toString(((OFActionSetNwTos)a).getNwTos()));
+                break;
+            case SET_NW_TTL:
+                sb.append(STR_NW_TTL_SET).append("=").append(Short.toString(((OFActionSetNwTtl)a).getNwTtl()));
+                break;
+            case DEC_NW_TTL:
+                sb.append(STR_NW_TTL_DEC);
+                break;
+            case SET_MPLS_LABEL:
+                sb.append(STR_MPLS_LABEL_SET).append("=").append(Long.toString(((OFActionSetMplsLabel)a).getMplsLabel()));
+                break;
+            case SET_MPLS_TC:
+                sb.append(STR_MPLS_TC_SET).append("=").append(Short.toString(((OFActionSetMplsTc)a).getMplsTc()));
+                break;
+            case SET_MPLS_TTL:
+                sb.append(STR_MPLS_TTL_SET).append("=").append(Short.toString(((OFActionSetMplsTtl)a).getMplsTtl()));
+                break;
+            case DEC_MPLS_TTL:
+                sb.append(STR_MPLS_TTL_DEC);
+                break;
+            case PUSH_MPLS:
+                sb.append(STR_MPLS_PUSH).append("=").append(Integer.toString(((OFActionPushMpls)a).getEthertype().getValue()));
+                break;
+            case POP_MPLS:
+                sb.append(STR_MPLS_POP).append("=").append(Integer.toString(((OFActionPopMpls)a).getEthertype().getValue()));
+                break;
+            case SET_NW_SRC:
+                sb.append(STR_NW_SRC_SET).append("=").append(((OFActionSetNwSrc)a).getNwAddr().toString());
+                break;
+            case SET_NW_DST:
+                sb.append(STR_NW_DST_SET).append("=").append(((OFActionSetNwDst)a).getNwAddr().toString());
+                break;
+            case SET_TP_SRC:
+                sb.append(STR_TP_SRC_SET).append("=").append(((OFActionSetTpSrc)a).getTpPort().toString());
+                break;
+            case SET_TP_DST:
+                sb.append(STR_TP_DST_SET).append("=").append(((OFActionSetTpDst)a).getTpPort().toString());
+                break;
+            case COPY_TTL_IN:
+                sb.append(STR_TTL_IN_COPY);
+                break;
+            case COPY_TTL_OUT:
+                sb.append(STR_TTL_OUT_COPY);
+                break;
+            case PUSH_PBB:
+                sb.append(STR_PBB_PUSH).append("=").append(Integer.toString(((OFActionPushPbb)a).getEthertype().getValue()));
+                break;
+            case POP_PBB:
+                sb.append(STR_PBB_POP);
+                break;
+            case EXPERIMENTER:
+                sb.append(STR_EXPERIMENTER).append("=").append(Long.toString(((OFActionExperimenter)a).getExperimenter()));
+                break;
+            case GROUP:
+                sb.append(STR_GROUP).append("=").append(Integer.toString(((OFActionGroup)a).getGroup().getGroupNumber()));
+                break;
+            case SET_FIELD:
+                log.debug("Got Set-Field action. Setting {}", ((OFActionSetField)a));
+                /* ARP */
+                if (((OFActionSetField)a).getField() instanceof OFOxmArpOp) {
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_ARP_OPCODE)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(Integer.toString(((OFOxmArpOp) ((OFActionSetField) a).getField()).getValue().getOpcode()));
+                } else if (((OFActionSetField)a).getField() instanceof OFOxmArpSha) {
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_ARP_SHA)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(((OFOxmArpSha) ((OFActionSetField) a).getField()).getValue().toString()); // macaddress formats string already
+                } else if (((OFActionSetField)a).getField() instanceof OFOxmArpTha) {
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_ARP_DHA)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(((OFOxmArpTha) ((OFActionSetField) a).getField()).getValue().toString());
+                } else if (((OFActionSetField)a).getField() instanceof OFOxmArpSpa) {
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_ARP_SPA)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(((OFOxmArpSpa) ((OFActionSetField) a).getField()).getValue().toString()); // ipaddress formats string already
+                } else if (((OFActionSetField)a).getField() instanceof OFOxmArpTpa) {
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_ARP_DPA)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(((OFOxmArpTpa) ((OFActionSetField) a).getField()).getValue().toString()); 
+                } else if (((OFActionSetField)a).getField() instanceof OFOxmIpv6NdSll) {                		
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_IPV6_ND_SLL)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(((OFOxmIpv6NdSll) ((OFActionSetField) a).getField()).getValue().toString()); // macaddress formats string already
+                } else if (((OFActionSetField)a).getField() instanceof OFOxmIpv6NdTll) {                		
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_IPV6_ND_TLL)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(((OFOxmIpv6NdTll) ((OFActionSetField) a).getField()).getValue().toString()); // macaddress formats string already
+                } else if (((OFActionSetField)a).getField() instanceof OFOxmIpv6NdTarget) {                		
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_IPV6_ND_TARGET)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(((OFOxmIpv6NdTarget) ((OFActionSetField) a).getField()).getValue().toString()); 
+                }
+                /* DATA LAYER */
+                else if (((OFActionSetField)a).getField() instanceof OFOxmEthType) {
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_DL_TYPE)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(Integer.toString(((OFOxmEthType) ((OFActionSetField) a).getField()).getValue().getValue()));
+                } else if (((OFActionSetField)a).getField() instanceof OFOxmEthSrc) {
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_DL_SRC)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(((OFOxmEthSrc) ((OFActionSetField) a).getField()).getValue().toString());
+                } else if (((OFActionSetField)a).getField() instanceof OFOxmEthDst) {
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_DL_DST)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(((OFOxmEthDst) ((OFActionSetField) a).getField()).getValue().toString()); 
+                } else if (((OFActionSetField)a).getField() instanceof OFOxmVlanVid) {
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_DL_VLAN)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(Short.toString(((OFOxmVlanVid) ((OFActionSetField) a).getField()).getValue().getVlan())); 
+                } else if (((OFActionSetField)a).getField() instanceof OFOxmVlanPcp) {
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_DL_VLAN_PCP)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(Byte.toString(((OFOxmVlanPcp) ((OFActionSetField) a).getField()).getValue().getValue())); 
+                } 
+                /* ICMP */
+                else if (((OFActionSetField)a).getField() instanceof OFOxmIcmpv4Code) {
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_ICMP_CODE)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(Short.toString(((OFOxmIcmpv4Code) ((OFActionSetField) a).getField()).getValue().getCode())); 
+                } else if (((OFActionSetField)a).getField() instanceof OFOxmIcmpv4Type) {
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_ICMP_TYPE)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(Short.toString(((OFOxmIcmpv4Type) ((OFActionSetField) a).getField()).getValue().getType())); 
+                } else if (((OFActionSetField)a).getField() instanceof OFOxmIcmpv6Code) {                		
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_ICMPV6_CODE)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(Short.toString(((OFOxmIcmpv6Code) ((OFActionSetField) a).getField()).getValue().getRaw())); 
+                }  else if (((OFActionSetField)a).getField() instanceof OFOxmIcmpv6Type) {                		
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_ICMPV6_TYPE)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(Short.toString(((OFOxmIcmpv6Type) ((OFActionSetField) a).getField()).getValue().getRaw())); 
+                }
+                /* NETWORK LAYER */
+                else if (((OFActionSetField)a).getField() instanceof OFOxmIpProto) {
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_NW_PROTO)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(Short.toString(((OFOxmIpProto) ((OFActionSetField) a).getField()).getValue().getIpProtocolNumber())); 
+                } else if (((OFActionSetField)a).getField() instanceof OFOxmIpv4Src) {
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_NW_SRC)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(((OFOxmIpv4Src) ((OFActionSetField) a).getField()).getValue().toString()); 
+                } else if (((OFActionSetField)a).getField() instanceof OFOxmIpv4Dst) {
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_NW_DST)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(((OFOxmIpv4Dst) ((OFActionSetField) a).getField()).getValue().toString()); 
+                } else if (((OFActionSetField)a).getField() instanceof OFOxmIpv6Src) {                		
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_IPV6_SRC)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(((OFOxmIpv6Src) ((OFActionSetField) a).getField()).getValue().toString()); 
+                } else if (((OFActionSetField)a).getField() instanceof OFOxmIpv6Dst) {                		
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_IPV6_DST)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(((OFOxmIpv6Dst) ((OFActionSetField) a).getField()).getValue().toString()); 
+                } else if (((OFActionSetField)a).getField() instanceof OFOxmIpv6Flabel) {                		
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_IPV6_FLOW_LABEL)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(((OFOxmIpv6Flabel) ((OFActionSetField) a).getField()).getValue().toString()); 
+                } else if (((OFActionSetField)a).getField() instanceof OFOxmIpv6Exthdr) {                		
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_IPV6_EXTHDR)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(((OFOxmIpv6Exthdr) ((OFActionSetField) a).getField()).getValue().toString()); 
+                } else if (((OFActionSetField)a).getField() instanceof OFOxmIpEcn) { 
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_NW_ECN)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(Byte.toString(((OFOxmIpEcn) ((OFActionSetField) a).getField()).getValue().getEcnValue())); 
+                } else if (((OFActionSetField)a).getField() instanceof OFOxmIpDscp) {
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_NW_DSCP)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(Byte.toString(((OFOxmIpDscp) ((OFActionSetField) a).getField()).getValue().getDscpValue())); 
+                } 
+                /* TRANSPORT LAYER, TCP, UDP, and SCTP */
+                else if (((OFActionSetField)a).getField() instanceof OFOxmTcpSrc) {
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_TCP_SRC)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(Integer.toString(((OFOxmTcpSrc) ((OFActionSetField) a).getField()).getValue().getPort())); 
+                } else if (((OFActionSetField)a).getField() instanceof OFOxmTcpDst) {
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_TCP_DST)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(Integer.toString(((OFOxmTcpDst) ((OFActionSetField) a).getField()).getValue().getPort())); 
+                } else if (((OFActionSetField)a).getField() instanceof OFOxmUdpSrc) {
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_UDP_SRC)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(Integer.toString(((OFOxmUdpSrc) ((OFActionSetField) a).getField()).getValue().getPort())); 
+                } else if (((OFActionSetField)a).getField() instanceof OFOxmUdpDst) {
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_UDP_DST)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(Integer.toString(((OFOxmUdpDst) ((OFActionSetField) a).getField()).getValue().getPort())); 
+                } else if (((OFActionSetField)a).getField() instanceof OFOxmSctpSrc) {
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_SCTP_SRC)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(Integer.toString(((OFOxmSctpSrc) ((OFActionSetField) a).getField()).getValue().getPort())); 
+                } else if (((OFActionSetField)a).getField() instanceof OFOxmSctpDst) {
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_SCTP_DST)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(Integer.toString(((OFOxmSctpDst) ((OFActionSetField) a).getField()).getValue().getPort())); 
+                } else if (((OFActionSetField)a).getField() instanceof OFOxmTcpFlags) {
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_TCP_FLAGS)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(Integer.toString(((OFOxmTcpFlags) ((OFActionSetField) a).getField()).getValue().getValue())); 
+                }
+                /* MPLS */
+                else if (((OFActionSetField)a).getField() instanceof OFOxmMplsLabel) {
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_MPLS_LABEL)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(Long.toString(((OFOxmMplsLabel) ((OFActionSetField) a).getField()).getValue().getValue())); 
+                } else if (((OFActionSetField)a).getField() instanceof OFOxmMplsTc) {
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_MPLS_TC)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(Short.toString(((OFOxmMplsTc) ((OFActionSetField) a).getField()).getValue().getValue())); 
+                } else if (((OFActionSetField)a).getField() instanceof OFOxmMplsBos) {
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_MPLS_BOS)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(Boolean.toString(((OFOxmMplsBos) ((OFActionSetField) a).getField()).getValue().getValue())); 
+                }
+                /* ACTSET_OUTPUT */
+                else if (((OFActionSetField)a).getField() instanceof OFOxmActsetOutput) {
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_ACTSET_OUTPUT)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(Integer.toString(((OFOxmActsetOutput) ((OFActionSetField) a).getField()).getValue().getPortNumber())); 
+                }
+                /* PACKET_TYPE */
+                else if (((OFActionSetField)a).getField() instanceof OFOxmPacketType) { // TODO hard-coded "/" as delimiter...fix this
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_PACKET_TYPE)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(Integer.toString(((OFOxmPacketType) ((OFActionSetField) a).getField()).getValue().getNamespace())) 
+                    .append("/")
+                    .append(Integer.toString(((OFOxmPacketType) ((OFActionSetField) a).getField()).getValue().getNsType())); 
+                }
+                /* METADATA */
+                else if (((OFActionSetField)a).getField() instanceof OFOxmMetadata) {
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_METADATA)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(Long.toString(((OFOxmMetadata) ((OFActionSetField) a).getField()).getValue().getValue().getValue())); 
+                } else {
+                    log.error("Could not decode Set-Field action field: {}", ((OFActionSetField) a));
+                }
+                break;
+            case COPY_FIELD:
+                sb.append(STR_FIELD_COPY).append("=").append(copyFieldToJson((OFActionCopyField) a));
+                break;
+            case METER:
+                sb.append(STR_METER).append("=").append(Long.toString(((OFActionMeter)a).getMeterId()));
+                break;
+            default:
+                log.error("Could not decode action: {}", a);
+                break;
+            }
 
-		}
+        }
 
-		return sb.toString();
-	}
+        return sb.toString();
+    }
 
-	public static List<OFAction> fromString(String s, OFVersion v) {
-		List<OFAction> actions = new LinkedList<OFAction>();
-		OFFactory f = OFFactories.getFactory(v);
-		if (s != null && !s.trim().isEmpty()) {
-			s = s.toLowerCase();
-			String[] bigStringSplit = s.split(","); // split into separate action=value or action=key@value pairs
+    public static List<OFAction> fromString(String s, OFVersion v) {
+        List<OFAction> actions = new LinkedList<OFAction>();
+        OFFactory f = OFFactories.getFactory(v);
+        if (s != null && !s.trim().isEmpty()) {
+            s = s.toLowerCase();
+            String[] bigStringSplit = s.split(","); // split into separate action=value or action=key@value pairs
 
-			String[] tmp;
-			ArrayDeque<String[]> actionToDecode = new ArrayDeque<String[]>();
-			for (int i = 0; i < bigStringSplit.length; i++) {
-				tmp = bigStringSplit[i].split("="); // split into separate [action, value] or [action, key@value] singles
-				if (tmp.length != 2) {
-					log.debug("Token " + bigStringSplit[i] + " does not have form 'key=value' parsing " + s);
-				}
-				actionToDecode.add(tmp); // actionToDecode contains [key, value] pairs. Create a queue of pairs to process.
-			}	
+            String[] tmp;
+            ArrayDeque<String[]> actionToDecode = new ArrayDeque<String[]>();
+            for (int i = 0; i < bigStringSplit.length; i++) {
+                tmp = bigStringSplit[i].split("="); // split into separate [action, value] or [action, key@value] singles
+                if (tmp.length != 2) {
+                    log.debug("Token " + bigStringSplit[i] + " does not have form 'key=value' parsing " + s);
+                }
+                actionToDecode.add(tmp); // actionToDecode contains [key, value] pairs. Create a queue of pairs to process.
+            }	
 
-			while (!actionToDecode.isEmpty()) {
-				String[] keyPair = actionToDecode.pollFirst();
-				String key;
-				String pair;
-				if (keyPair.length != 2) {
-					log.debug("[Key, Value] {} does not have form 'key=value' parsing, which is okay for some actions e.g. 'pop_vlan'.", keyPair);
-					key = keyPair[0]; // could the be case of a constant actions (e.g. copy_ttl_in)
-					pair = "";
-				} else {
-					key = keyPair[0];
-					pair = keyPair[1];
-				}
+            while (!actionToDecode.isEmpty()) {
+                String[] keyPair = actionToDecode.pollFirst();
+                String key;
+                String pair;
+                if (keyPair.length != 2) {
+                    log.debug("[Key, Value] {} does not have form 'key=value' parsing, which is okay for some actions e.g. 'pop_vlan'.", keyPair);
+                    key = keyPair[0]; // could the be case of a constant actions (e.g. copy_ttl_in)
+                    pair = "";
+                } else {
+                    key = keyPair[0];
+                    pair = keyPair[1];
+                }
 
-				OFAction a = null;
-				try {
-					switch (key) {
-					case STR_OUTPUT:
-						a = decode_output(pair, v);
-						break;
-					case STR_ENQUEUE:
-						a = decode_enqueue(pair, v);
-						break;
-					case STR_DL_SRC_SET:
-						a = decode_set_src_mac(pair, v);
-						break;
-					case STR_DL_DST_SET:
-						a = decode_set_dst_mac(pair, v);
-						break;
-					case STR_EXPERIMENTER:
-						//no-op. Not implemented
-						log.error("OFAction EXPERIMENTER not implemented.");
-						break;
-					case STR_FIELD_SET: /* ONLY OF1.1+ should get in here. These should only be header fields valid within a set-field. */
-						String[] actionData = pair.split(MatchUtils.SET_FIELD_DELIM);
-						if (actionData.length != 2) {
-							throw new IllegalArgumentException("[Action, Data] " + keyPair + " does not have form 'action=data'" + actionData);
-						}
+                OFAction a = null;
+                try {
+                    switch (key) {
+                    case STR_OUTPUT:
+                        a = decode_output(pair, v);
+                        break;
+                    case STR_ENQUEUE:
+                        a = decode_enqueue(pair, v);
+                        break;
+                    case STR_DL_SRC_SET:
+                        a = decode_set_src_mac(pair, v);
+                        break;
+                    case STR_DL_DST_SET:
+                        a = decode_set_dst_mac(pair, v);
+                        break;
+                    case STR_EXPERIMENTER:
+                        //no-op. Not implemented
+                        log.error("OFAction EXPERIMENTER not implemented.");
+                        break;
+                    case STR_FIELD_SET: /* ONLY OF1.1+ should get in here. These should only be header fields valid within a set-field. */
+                        String[] actionData = pair.split(MatchUtils.SET_FIELD_DELIM);
+                        if (actionData.length != 2) {
+                            throw new IllegalArgumentException("[Action, Data] " + keyPair + " does not have form 'action=data'" + actionData);
+                        }
 
-						switch (actionData[0]) {
-						case MatchUtils.STR_ARP_OPCODE:
-							if (actionData[1].startsWith("0x")) {
-								a = f.actions().buildSetField()
-										.setField(f.oxms().buildArpOp().setValue(ArpOpcode.of(Integer.parseInt(actionData[1].replaceFirst("0x", ""), 16))).build())
-										.build();
-							} else {
-								a = f.actions().buildSetField()
-										.setField(f.oxms().buildArpOp().setValue(ArpOpcode.of(Integer.parseInt(actionData[1]))).build())
-										.build();
-							}
-							break;
-						case MatchUtils.STR_ARP_SHA:
-							a = f.actions().buildSetField()
-							.setField(f.oxms().buildArpSha().setValue(MacAddress.of(actionData[1])).build())
-							.build();
-							break;
-						case MatchUtils.STR_ARP_DHA:
-							a = f.actions().buildSetField()
-							.setField(f.oxms().buildArpTha().setValue(MacAddress.of(actionData[1])).build())
-							.build();
-							break;
-						case MatchUtils.STR_ARP_SPA:
-							a = f.actions().buildSetField()
-							.setField(f.oxms().buildArpSpa().setValue(IPv4Address.of(actionData[1])).build())
-							.build();
-							break;
-						case MatchUtils.STR_ARP_DPA:
-							a = f.actions().buildSetField()
-							.setField(f.oxms().buildArpTpa().setValue(IPv4Address.of(actionData[1])).build())
-							.build();
-							break;
-						case MatchUtils.STR_IPV6_ND_SLL:
-							a = f.actions().buildSetField()
-							.setField(f.oxms().buildIpv6NdSll().setValue(MacAddress.of(actionData[1])).build())
-							.build();
-							break;
-						case MatchUtils.STR_IPV6_ND_TLL:
-							a = f.actions().buildSetField()
-							.setField(f.oxms().buildIpv6NdTll().setValue(MacAddress.of(actionData[1])).build())
-							.build();
-							break;
-						case MatchUtils.STR_IPV6_ND_TARGET:
-							a = f.actions().buildSetField()
-							.setField(f.oxms().buildIpv6NdTarget().setValue(IPv6Address.of(actionData[1])).build())
-							.build();
-							break;
-						case MatchUtils.STR_DL_TYPE:
-							if (actionData[1].startsWith("0x")) {
-								a = f.actions().buildSetField()
-										.setField(f.oxms().buildEthType().setValue(EthType.of(Integer.parseInt(actionData[1].replaceFirst("0x", ""), 16))).build())
-										.build();
-							} else {
-								a = f.actions().buildSetField()
-										.setField(f.oxms().buildEthType().setValue(EthType.of(Integer.parseInt(actionData[1]))).build())
-										.build();
-							}
-							break;
-						case MatchUtils.STR_DL_SRC:
-							a = f.actions().buildSetField()
-							.setField(f.oxms().buildEthSrc().setValue(MacAddress.of(actionData[1])).build())
-							.build();
-							break;
-						case MatchUtils.STR_DL_DST:
-							a = f.actions().buildSetField()
-							.setField(f.oxms().buildEthDst().setValue(MacAddress.of(actionData[1])).build())
-							.build();
-							break;
-						case MatchUtils.STR_DL_VLAN:
-							if (actionData[1].startsWith("0x")) {
-								a = f.actions().buildSetField()
-										.setField(f.oxms().buildVlanVid().setValue(OFVlanVidMatch.ofVlan(Integer.parseInt(actionData[1].replaceFirst("0x", ""), 16))).build())
-										.build();
-							} else {
-								a = f.actions().buildSetField()
-										.setField(f.oxms().buildVlanVid().setValue(OFVlanVidMatch.ofVlan(Integer.parseInt(actionData[1]))).build())
-										.build();
-							}
-							break;
-						case MatchUtils.STR_DL_VLAN_PCP:
-							if (actionData[1].startsWith("0x")) {
-								a = f.actions().buildSetField()
-										.setField(f.oxms().buildVlanPcp().setValue(VlanPcp.of(Byte.parseByte(actionData[1].replaceFirst("0x", ""), 16))).build())
-										.build();
-							} else {
-								a = f.actions().buildSetField()
-										.setField(f.oxms().buildVlanPcp().setValue(VlanPcp.of(Byte.parseByte(actionData[1]))).build())
-										.build();
-							}
-							break;
-						case MatchUtils.STR_ICMP_CODE:
-							if (actionData[1].startsWith("0x")) {
-								a = f.actions().buildSetField()
-										.setField(f.oxms().buildIcmpv4Code().setValue(ICMPv4Code.of(Short.parseShort(actionData[1].replaceFirst("0x", ""), 16))).build())
-										.build();
-							} else {
-								a = f.actions().buildSetField()
-										.setField(f.oxms().buildIcmpv4Code().setValue(ICMPv4Code.of(Short.parseShort(actionData[1]))).build())
-										.build();
-							}
-							break;
-						case MatchUtils.STR_ICMP_TYPE:
-							if (actionData[1].startsWith("0x")) {
-								a = f.actions().buildSetField()
-										.setField(f.oxms().buildIcmpv4Type().setValue(ICMPv4Type.of(Short.parseShort(actionData[1].replaceFirst("0x", ""), 16))).build())
-										.build();
-							} else {
-								a = f.actions().buildSetField()
-										.setField(f.oxms().buildIcmpv4Type().setValue(ICMPv4Type.of(Short.parseShort(actionData[1]))).build())
-										.build();
-							}
-							break;
-						case MatchUtils.STR_ICMPV6_CODE:
-							if (actionData[1].startsWith("0x")) {
-								a = f.actions().buildSetField()
-										.setField(f.oxms().buildIcmpv6Code().setValue(U8.of(Short.parseShort(actionData[1].replaceFirst("0x", ""), 16))).build())
-										.build();
-							} else {
-								a = f.actions().buildSetField()
-										.setField(f.oxms().buildIcmpv6Code().setValue(U8.of(Short.parseShort(actionData[1]))).build())
-										.build();
-							}
-							break;
-						case MatchUtils.STR_ICMPV6_TYPE:
-							if (actionData[1].startsWith("0x")) {
-								a = f.actions().buildSetField()
-										.setField(f.oxms().buildIcmpv6Type().setValue(U8.of(Short.parseShort(actionData[1].replaceFirst("0x", ""), 16))).build())
-										.build();
-							} else {
-								a = f.actions().buildSetField()
-										.setField(f.oxms().buildIcmpv6Type().setValue(U8.of(Short.parseShort(actionData[1]))).build())
-										.build();
-							}
-							break;
-						case MatchUtils.STR_NW_PROTO:
-							if (actionData[1].startsWith("0x")) {
-								a = f.actions().buildSetField()
-										.setField(f.oxms().buildIpProto().setValue(IpProtocol.of(Short.parseShort(actionData[1].replaceFirst("0x", ""), 16))).build())
-										.build();
-							} else {
-								a = f.actions().buildSetField()
-										.setField(f.oxms().buildIpProto().setValue(IpProtocol.of(Short.parseShort(actionData[1]))).build())
-										.build();
-							}
-							break;
-						case MatchUtils.STR_NW_SRC:
-							a = f.actions().buildSetField()
-							.setField(f.oxms().buildIpv4Src().setValue(IPv4Address.of(actionData[1])).build())
-							.build();						
-							break;
-						case MatchUtils.STR_NW_DST:
-							a = f.actions().buildSetField()
-							.setField(f.oxms().buildIpv4Dst().setValue(IPv4Address.of(actionData[1])).build())
-							.build();						
-							break;
-						case MatchUtils.STR_IPV6_SRC:
-							a = f.actions().buildSetField()
-							.setField(f.oxms().buildIpv6Src().setValue(IPv6Address.of(actionData[1])).build())
-							.build();						
-							break;
-						case MatchUtils.STR_IPV6_DST:
-							a = f.actions().buildSetField()
-							.setField(f.oxms().buildIpv6Dst().setValue(IPv6Address.of(actionData[1])).build())
-							.build();						
-							break;
-						case MatchUtils.STR_IPV6_FLOW_LABEL:
-							if (actionData[1].startsWith("0x")) {
-								a = f.actions().buildSetField()
-										.setField(f.oxms().buildIpv6Flabel().setValue(IPv6FlowLabel.of(Integer.parseInt(actionData[1].replaceFirst("0x", ""), 16))).build())
-										.build();			
-							} else {
-								a = f.actions().buildSetField()
-										.setField(f.oxms().buildIpv6Flabel().setValue(IPv6FlowLabel.of(Integer.parseInt(actionData[1]))).build())
-										.build();
-							}
-							break;
-						case MatchUtils.STR_NW_ECN:
-							if (actionData[1].startsWith("0x")) {
-								a = f.actions().buildSetField()
-										.setField(f.oxms().buildIpEcn().setValue(IpEcn.of(Byte.parseByte(actionData[1].replaceFirst("0x", ""), 16))).build())
-										.build();
-							} else {
-								a = f.actions().buildSetField()
-										.setField(f.oxms().buildIpEcn().setValue(IpEcn.of(Byte.parseByte(actionData[1]))).build())
-										.build();
-							}
-							break;
-						case MatchUtils.STR_NW_DSCP:
-							if (actionData[1].startsWith("0x")) {
-								a = f.actions().buildSetField()
-										.setField(f.oxms().buildIpDscp().setValue(IpDscp.of(Byte.parseByte(actionData[1].replaceFirst("0x", ""), 16))).build())
-										.build();
-							} else {
-								a = f.actions().buildSetField()
-										.setField(f.oxms().buildIpDscp().setValue(IpDscp.of(Byte.parseByte(actionData[1]))).build())
-										.build();
-							}
-							break;
-						case MatchUtils.STR_SCTP_SRC:
-							a = f.actions().buildSetField()
-							.setField(f.oxms().buildSctpSrc().setValue(TransportPort.of(Integer.parseInt(actionData[1]))).build())
-							.build();	
-							break;
-						case MatchUtils.STR_SCTP_DST:
-							a = f.actions().buildSetField()
-							.setField(f.oxms().buildSctpDst().setValue(TransportPort.of(Integer.parseInt(actionData[1]))).build())
-							.build();	
-							break;
-						case MatchUtils.STR_TCP_SRC:
-							a = f.actions().buildSetField()
-							.setField(f.oxms().buildTcpSrc().setValue(TransportPort.of(Integer.parseInt(actionData[1]))).build())
-							.build();	
-							break;
-						case MatchUtils.STR_TCP_DST:
-							a = f.actions().buildSetField()
-							.setField(f.oxms().buildTcpDst().setValue(TransportPort.of(Integer.parseInt(actionData[1]))).build())
-							.build();	
-							break;
-						case MatchUtils.STR_UDP_SRC:
-							a = f.actions().buildSetField()
-							.setField(f.oxms().buildUdpSrc().setValue(TransportPort.of(Integer.parseInt(actionData[1]))).build())
-							.build();	
-							break;
-						case MatchUtils.STR_UDP_DST:
-							a = f.actions().buildSetField()
-							.setField(f.oxms().buildUdpDst().setValue(TransportPort.of(Integer.parseInt(actionData[1]))).build())
-							.build();	
-							break;
-						case MatchUtils.STR_MPLS_LABEL:
-							if (actionData[1].startsWith("0x")) {
-								a = f.actions().buildSetField()
-										.setField(f.oxms().buildMplsLabel().setValue(U32.of(Long.parseLong(actionData[1].replaceFirst("0x", ""), 16))).build())
-										.build();
-							} else {
-								a = f.actions().buildSetField()
-										.setField(f.oxms().buildMplsLabel().setValue(U32.of(Long.parseLong(actionData[1]))).build())
-										.build();
-							}
-							break;
-						case MatchUtils.STR_MPLS_TC:
-							if (actionData[1].startsWith("0x")) {
-								a = f.actions().buildSetField()
-										.setField(f.oxms().buildMplsTc().setValue(U8.of(Short.parseShort(actionData[1].replaceFirst("0x", ""), 16))).build())
-										.build();
-							} else {
-								a = f.actions().buildSetField()
-										.setField(f.oxms().buildMplsTc().setValue(U8.of(Short.parseShort(actionData[1]))).build())
-										.build();
-							}
-							break;
-						case MatchUtils.STR_MPLS_BOS:
-							a = f.actions().buildSetField()
-							.setField(f.oxms().buildMplsBos().setValue(OFBooleanValue.of(Boolean.parseBoolean(actionData[1]))).build()) // interprets anything other than "true" as false
-							.build();
-							break;
-						case MatchUtils.STR_METADATA:
-							if (actionData[1].startsWith("0x")) {
-								a = f.actions().buildSetField()
-										.setField(f.oxms().buildMetadata().setValue(OFMetadata.of(U64.of(Long.parseLong(actionData[1].replaceFirst("0x", ""), 16)))).build())
-										.build();
-							} else {
-								a = f.actions().buildSetField()
-										.setField(f.oxms().buildMetadata().setValue(OFMetadata.of(U64.of(Long.parseLong(actionData[1])))).build())
-										.build();
-							}
-							break;
-						case MatchUtils.STR_ACTSET_OUTPUT:
-							if (actionData[1].startsWith("0x")) {
-								a = f.actions().buildSetField()
-										.setField(f.oxms().buildActsetOutput().setValue(OFPort.of(U32.of(Long.parseLong(actionData[1].replaceFirst("0x", ""), 16)).getRaw())).build())
-										.build();
-							} else {
-								a = f.actions().buildSetField()
-										.setField(f.oxms().buildActsetOutput().setValue(OFPort.of(U32.of(Long.parseLong(actionData[1])).getRaw())).build())
-										.build();
-							}
-							break;
-						case MatchUtils.STR_TCP_FLAGS:
-							if (actionData[1].startsWith("0x")) {
-								a = f.actions().buildSetField()
-										.setField(f.oxms().buildTcpFlags().setValue(U16.of(Integer.parseInt(actionData[1].replaceFirst("0x", ""), 16))).build())
-										.build();
-							} else {
-								a = f.actions().buildSetField()
-										.setField(f.oxms().buildTcpFlags().setValue(U16.of(Integer.parseInt(actionData[1]))).build())
-										.build();
-							}
-							break;
-						default:
-							log.error("Unexpected OF1.2+ setfield '{}'", actionData);
-							break;
-						}					
-						break;
-					case STR_GROUP:
-						if (pair.startsWith("0x")) {
-							a = f.actions().buildGroup()
-									.setGroup(OFGroup.of(Integer.parseInt(pair.replaceFirst("0x", ""), 16)))
-									.build();	
-						} else {
-							a = f.actions().buildGroup()
-									.setGroup(OFGroup.of(Integer.parseInt(pair)))
-									.build();		
-						}
-						break;
-					case STR_MPLS_LABEL_SET:
-						if (pair.startsWith("0x")) {
-							a = f.actions().buildSetMplsLabel()
-									.setMplsLabel(Long.parseLong(pair.replaceFirst("0x", ""), 16))
-									.build();			
-						} else {
-							a = f.actions().buildSetMplsLabel()
-									.setMplsLabel(Long.parseLong(pair))
-									.build();					
-						}
-						break;
-					case STR_MPLS_POP:
-						if (pair.startsWith("0x")) {
-							a = f.actions().buildPopMpls()
-									.setEthertype(EthType.of(Integer.parseInt(pair.replaceFirst("0x", ""), 16)))
-									.build();
-						} else {
-							a = f.actions().buildPopMpls()
-									.setEthertype(EthType.of(Integer.parseInt(pair)))
-									.build();	
-						}
-						break;
-					case STR_MPLS_PUSH:
-						if (pair.startsWith("0x")) {
-							a = f.actions().buildPushMpls()
-									.setEthertype(EthType.of(Integer.parseInt(pair.replaceFirst("0x", ""), 16)))
-									.build();		
-						} else {
-							a = f.actions().buildPushMpls()
-									.setEthertype(EthType.of(Integer.parseInt(pair)))
-									.build();			
-						}
-						break;
-					case STR_MPLS_TC_SET:
-						if (pair.startsWith("0x")) {
-							a = f.actions().buildSetMplsTc()
-									.setMplsTc(Short.parseShort(pair.replaceFirst("0x", ""), 16))
-									.build();	
-						} else {
-							a = f.actions().buildSetMplsTc()
-									.setMplsTc(Short.parseShort(pair))
-									.build();			
-						}
-						break;
-					case STR_MPLS_TTL_DEC:
-						a = f.actions().decMplsTtl();
-						break;
-					case STR_MPLS_TTL_SET:
-						if (pair.startsWith("0x")) {
-							a = f.actions().buildSetMplsTtl()
-									.setMplsTtl(Short.parseShort(pair.replaceFirst("0x", ""), 16))
-									.build();	
-						} else {
-							a = f.actions().buildSetMplsTtl()
-									.setMplsTtl(Short.parseShort(pair))
-									.build();				
-						}
-						break;
-					case STR_NW_TOS_SET:
-						a = decode_set_tos_bits(pair, v); // should only be used by OF1.0
-						break;
-					case STR_NW_SRC_SET:
-						a = decode_set_src_ip(pair, v);
-						break;
-					case STR_NW_DST_SET:
-						a = decode_set_dst_ip(pair, v);
-						break;
-					case STR_NW_ECN_SET: // loxi does not support DSCP set for OF1.1
-						if (pair.startsWith("0x")) {
-							a = f.actions().buildSetNwEcn()
-									.setNwEcn(IpEcn.of(Byte.parseByte(pair.replaceFirst("0x", ""), 16)))
-									.build();		
-						} else {
-							a = f.actions().buildSetNwEcn()
-									.setNwEcn(IpEcn.of(Byte.parseByte(pair)))
-									.build();							
-						}
-						break;
-					case STR_NW_TTL_DEC:
-						a = f.actions().decNwTtl();
-						break;
-					case STR_NW_TTL_SET:
-						if (pair.startsWith("0x")) {
-							a = f.actions().buildSetNwTtl()
-									.setNwTtl(Short.parseShort(pair.replaceFirst("0x", ""), 16))
-									.build();
-						} else {
-							a = f.actions().buildSetNwTtl()
-									.setNwTtl(Short.parseShort(pair))
-									.build();						
-						}
-						break;
-					case STR_PBB_POP:
-						a = f.actions().popPbb();
-						break;
-					case STR_PBB_PUSH:
-						if (pair.startsWith("0x")) {
-							a = f.actions().buildPushPbb()
-									.setEthertype(EthType.of(Integer.parseInt(pair.replaceFirst("0x", ""), 16)))
-									.build();				
-						} else {
-							a = f.actions().buildPushPbb()
-									.setEthertype(EthType.of(Integer.parseInt(pair)))
-									.build();					
-						}
-						break;
-					case STR_QUEUE_SET:
-						if (pair.startsWith("0x")) {
-							a = f.actions().buildSetQueue()
-									.setQueueId(Long.parseLong(pair.replaceFirst("0x", ""), 16))
-									.build();	
-						} else {
-							a = f.actions().buildSetQueue()
-									.setQueueId(Long.parseLong(pair))
-									.build();					
-						}
-						break;
-					case STR_TP_SRC_SET:
-						a = decode_set_src_port(pair, v);
-						break;
-					case STR_TP_DST_SET:
-						a = decode_set_dst_port(pair, v);
-						break;
-					case STR_TTL_IN_COPY:
-						a = f.actions().copyTtlIn();
-						break;
-					case STR_TTL_OUT_COPY:
-						a = f.actions().copyTtlOut();
-						break;
-					case STR_VLAN_POP:
-						a = f.actions().popVlan();
-						break;
-					case STR_VLAN_PUSH:
-						if (pair.startsWith("0x")) {
-							a = f.actions().buildPushVlan()
-									.setEthertype(EthType.of(Integer.parseInt(pair.replaceFirst("0x", ""), 16)))
-									.build();
-						} else {
-							a = f.actions().buildPushVlan()
-									.setEthertype(EthType.of(Integer.parseInt(pair)))
-									.build();		
-						}
-						break;
-					case STR_VLAN_STRIP:
-						a = f.actions().stripVlan();
-						break;
-					case STR_VLAN_SET_VID:
-						a = decode_set_vlan_id(pair, v);
-						break;
-					case STR_VLAN_SET_PCP:
-						a = decode_set_vlan_priority(pair, v);
-						break;
-					case STR_METER:
-						if (pair.startsWith("0x")) {
-							a = f.actions().buildMeter()
-									.setMeterId(Long.parseLong(pair.replaceFirst("0x", ""), 16))
-									.build();
-						} else {
-							a = f.actions().buildMeter()
-									.setMeterId(Long.parseLong(pair))
-									.build();	
-						}
-						break;
-					case STR_FIELD_COPY:
-						a = (OFAction) copyFieldFromJson(pair, f.getVersion());
-						break;
-					default:
-						log.error("Unexpected action key '{}'", keyPair);
-						break;
-					}
+                        switch (actionData[0]) {
+                        case MatchUtils.STR_ARP_OPCODE:
+                            if (actionData[1].startsWith("0x")) {
+                                a = f.actions().buildSetField()
+                                        .setField(f.oxms().buildArpOp().setValue(ArpOpcode.of(Integer.parseInt(actionData[1].replaceFirst("0x", ""), 16))).build())
+                                        .build();
+                            } else {
+                                a = f.actions().buildSetField()
+                                        .setField(f.oxms().buildArpOp().setValue(ArpOpcode.of(Integer.parseInt(actionData[1]))).build())
+                                        .build();
+                            }
+                            break;
+                        case MatchUtils.STR_ARP_SHA:
+                            a = f.actions().buildSetField()
+                            .setField(f.oxms().buildArpSha().setValue(MacAddress.of(actionData[1])).build())
+                            .build();
+                            break;
+                        case MatchUtils.STR_ARP_DHA:
+                            a = f.actions().buildSetField()
+                            .setField(f.oxms().buildArpTha().setValue(MacAddress.of(actionData[1])).build())
+                            .build();
+                            break;
+                        case MatchUtils.STR_ARP_SPA:
+                            a = f.actions().buildSetField()
+                            .setField(f.oxms().buildArpSpa().setValue(IPv4Address.of(actionData[1])).build())
+                            .build();
+                            break;
+                        case MatchUtils.STR_ARP_DPA:
+                            a = f.actions().buildSetField()
+                            .setField(f.oxms().buildArpTpa().setValue(IPv4Address.of(actionData[1])).build())
+                            .build();
+                            break;
+                        case MatchUtils.STR_IPV6_ND_SLL:
+                            a = f.actions().buildSetField()
+                            .setField(f.oxms().buildIpv6NdSll().setValue(MacAddress.of(actionData[1])).build())
+                            .build();
+                            break;
+                        case MatchUtils.STR_IPV6_ND_TLL:
+                            a = f.actions().buildSetField()
+                            .setField(f.oxms().buildIpv6NdTll().setValue(MacAddress.of(actionData[1])).build())
+                            .build();
+                            break;
+                        case MatchUtils.STR_IPV6_ND_TARGET:
+                            a = f.actions().buildSetField()
+                            .setField(f.oxms().buildIpv6NdTarget().setValue(IPv6Address.of(actionData[1])).build())
+                            .build();
+                            break;
+                        case MatchUtils.STR_DL_TYPE:
+                            if (actionData[1].startsWith("0x")) {
+                                a = f.actions().buildSetField()
+                                        .setField(f.oxms().buildEthType().setValue(EthType.of(Integer.parseInt(actionData[1].replaceFirst("0x", ""), 16))).build())
+                                        .build();
+                            } else {
+                                a = f.actions().buildSetField()
+                                        .setField(f.oxms().buildEthType().setValue(EthType.of(Integer.parseInt(actionData[1]))).build())
+                                        .build();
+                            }
+                            break;
+                        case MatchUtils.STR_DL_SRC:
+                            a = f.actions().buildSetField()
+                            .setField(f.oxms().buildEthSrc().setValue(MacAddress.of(actionData[1])).build())
+                            .build();
+                            break;
+                        case MatchUtils.STR_DL_DST:
+                            a = f.actions().buildSetField()
+                            .setField(f.oxms().buildEthDst().setValue(MacAddress.of(actionData[1])).build())
+                            .build();
+                            break;
+                        case MatchUtils.STR_DL_VLAN:
+                            if (actionData[1].startsWith("0x")) {
+                                a = f.actions().buildSetField()
+                                        .setField(f.oxms().buildVlanVid().setValue(OFVlanVidMatch.ofVlan(Integer.parseInt(actionData[1].replaceFirst("0x", ""), 16))).build())
+                                        .build();
+                            } else {
+                                a = f.actions().buildSetField()
+                                        .setField(f.oxms().buildVlanVid().setValue(OFVlanVidMatch.ofVlan(Integer.parseInt(actionData[1]))).build())
+                                        .build();
+                            }
+                            break;
+                        case MatchUtils.STR_DL_VLAN_PCP:
+                            if (actionData[1].startsWith("0x")) {
+                                a = f.actions().buildSetField()
+                                        .setField(f.oxms().buildVlanPcp().setValue(VlanPcp.of(Byte.parseByte(actionData[1].replaceFirst("0x", ""), 16))).build())
+                                        .build();
+                            } else {
+                                a = f.actions().buildSetField()
+                                        .setField(f.oxms().buildVlanPcp().setValue(VlanPcp.of(Byte.parseByte(actionData[1]))).build())
+                                        .build();
+                            }
+                            break;
+                        case MatchUtils.STR_ICMP_CODE:
+                            if (actionData[1].startsWith("0x")) {
+                                a = f.actions().buildSetField()
+                                        .setField(f.oxms().buildIcmpv4Code().setValue(ICMPv4Code.of(Short.parseShort(actionData[1].replaceFirst("0x", ""), 16))).build())
+                                        .build();
+                            } else {
+                                a = f.actions().buildSetField()
+                                        .setField(f.oxms().buildIcmpv4Code().setValue(ICMPv4Code.of(Short.parseShort(actionData[1]))).build())
+                                        .build();
+                            }
+                            break;
+                        case MatchUtils.STR_ICMP_TYPE:
+                            if (actionData[1].startsWith("0x")) {
+                                a = f.actions().buildSetField()
+                                        .setField(f.oxms().buildIcmpv4Type().setValue(ICMPv4Type.of(Short.parseShort(actionData[1].replaceFirst("0x", ""), 16))).build())
+                                        .build();
+                            } else {
+                                a = f.actions().buildSetField()
+                                        .setField(f.oxms().buildIcmpv4Type().setValue(ICMPv4Type.of(Short.parseShort(actionData[1]))).build())
+                                        .build();
+                            }
+                            break;
+                        case MatchUtils.STR_ICMPV6_CODE:
+                            if (actionData[1].startsWith("0x")) {
+                                a = f.actions().buildSetField()
+                                        .setField(f.oxms().buildIcmpv6Code().setValue(U8.of(Short.parseShort(actionData[1].replaceFirst("0x", ""), 16))).build())
+                                        .build();
+                            } else {
+                                a = f.actions().buildSetField()
+                                        .setField(f.oxms().buildIcmpv6Code().setValue(U8.of(Short.parseShort(actionData[1]))).build())
+                                        .build();
+                            }
+                            break;
+                        case MatchUtils.STR_ICMPV6_TYPE:
+                            if (actionData[1].startsWith("0x")) {
+                                a = f.actions().buildSetField()
+                                        .setField(f.oxms().buildIcmpv6Type().setValue(U8.of(Short.parseShort(actionData[1].replaceFirst("0x", ""), 16))).build())
+                                        .build();
+                            } else {
+                                a = f.actions().buildSetField()
+                                        .setField(f.oxms().buildIcmpv6Type().setValue(U8.of(Short.parseShort(actionData[1]))).build())
+                                        .build();
+                            }
+                            break;
+                        case MatchUtils.STR_NW_PROTO:
+                            if (actionData[1].startsWith("0x")) {
+                                a = f.actions().buildSetField()
+                                        .setField(f.oxms().buildIpProto().setValue(IpProtocol.of(Short.parseShort(actionData[1].replaceFirst("0x", ""), 16))).build())
+                                        .build();
+                            } else {
+                                a = f.actions().buildSetField()
+                                        .setField(f.oxms().buildIpProto().setValue(IpProtocol.of(Short.parseShort(actionData[1]))).build())
+                                        .build();
+                            }
+                            break;
+                        case MatchUtils.STR_NW_SRC:
+                            a = f.actions().buildSetField()
+                            .setField(f.oxms().buildIpv4Src().setValue(IPv4Address.of(actionData[1])).build())
+                            .build();						
+                            break;
+                        case MatchUtils.STR_NW_DST:
+                            a = f.actions().buildSetField()
+                            .setField(f.oxms().buildIpv4Dst().setValue(IPv4Address.of(actionData[1])).build())
+                            .build();						
+                            break;
+                        case MatchUtils.STR_IPV6_SRC:
+                            a = f.actions().buildSetField()
+                            .setField(f.oxms().buildIpv6Src().setValue(IPv6Address.of(actionData[1])).build())
+                            .build();						
+                            break;
+                        case MatchUtils.STR_IPV6_DST:
+                            a = f.actions().buildSetField()
+                            .setField(f.oxms().buildIpv6Dst().setValue(IPv6Address.of(actionData[1])).build())
+                            .build();						
+                            break;
+                        case MatchUtils.STR_IPV6_FLOW_LABEL:
+                            if (actionData[1].startsWith("0x")) {
+                                a = f.actions().buildSetField()
+                                        .setField(f.oxms().buildIpv6Flabel().setValue(IPv6FlowLabel.of(Integer.parseInt(actionData[1].replaceFirst("0x", ""), 16))).build())
+                                        .build();			
+                            } else {
+                                a = f.actions().buildSetField()
+                                        .setField(f.oxms().buildIpv6Flabel().setValue(IPv6FlowLabel.of(Integer.parseInt(actionData[1]))).build())
+                                        .build();
+                            }
+                            break;
+                        case MatchUtils.STR_NW_ECN:
+                            if (actionData[1].startsWith("0x")) {
+                                a = f.actions().buildSetField()
+                                        .setField(f.oxms().buildIpEcn().setValue(IpEcn.of(Byte.parseByte(actionData[1].replaceFirst("0x", ""), 16))).build())
+                                        .build();
+                            } else {
+                                a = f.actions().buildSetField()
+                                        .setField(f.oxms().buildIpEcn().setValue(IpEcn.of(Byte.parseByte(actionData[1]))).build())
+                                        .build();
+                            }
+                            break;
+                        case MatchUtils.STR_NW_DSCP:
+                            if (actionData[1].startsWith("0x")) {
+                                a = f.actions().buildSetField()
+                                        .setField(f.oxms().buildIpDscp().setValue(IpDscp.of(Byte.parseByte(actionData[1].replaceFirst("0x", ""), 16))).build())
+                                        .build();
+                            } else {
+                                a = f.actions().buildSetField()
+                                        .setField(f.oxms().buildIpDscp().setValue(IpDscp.of(Byte.parseByte(actionData[1]))).build())
+                                        .build();
+                            }
+                            break;
+                        case MatchUtils.STR_SCTP_SRC:
+                            a = f.actions().buildSetField()
+                            .setField(f.oxms().buildSctpSrc().setValue(TransportPort.of(Integer.parseInt(actionData[1]))).build())
+                            .build();	
+                            break;
+                        case MatchUtils.STR_SCTP_DST:
+                            a = f.actions().buildSetField()
+                            .setField(f.oxms().buildSctpDst().setValue(TransportPort.of(Integer.parseInt(actionData[1]))).build())
+                            .build();	
+                            break;
+                        case MatchUtils.STR_TCP_SRC:
+                            a = f.actions().buildSetField()
+                            .setField(f.oxms().buildTcpSrc().setValue(TransportPort.of(Integer.parseInt(actionData[1]))).build())
+                            .build();	
+                            break;
+                        case MatchUtils.STR_TCP_DST:
+                            a = f.actions().buildSetField()
+                            .setField(f.oxms().buildTcpDst().setValue(TransportPort.of(Integer.parseInt(actionData[1]))).build())
+                            .build();	
+                            break;
+                        case MatchUtils.STR_UDP_SRC:
+                            a = f.actions().buildSetField()
+                            .setField(f.oxms().buildUdpSrc().setValue(TransportPort.of(Integer.parseInt(actionData[1]))).build())
+                            .build();	
+                            break;
+                        case MatchUtils.STR_UDP_DST:
+                            a = f.actions().buildSetField()
+                            .setField(f.oxms().buildUdpDst().setValue(TransportPort.of(Integer.parseInt(actionData[1]))).build())
+                            .build();	
+                            break;
+                        case MatchUtils.STR_MPLS_LABEL:
+                            if (actionData[1].startsWith("0x")) {
+                                a = f.actions().buildSetField()
+                                        .setField(f.oxms().buildMplsLabel().setValue(U32.of(Long.parseLong(actionData[1].replaceFirst("0x", ""), 16))).build())
+                                        .build();
+                            } else {
+                                a = f.actions().buildSetField()
+                                        .setField(f.oxms().buildMplsLabel().setValue(U32.of(Long.parseLong(actionData[1]))).build())
+                                        .build();
+                            }
+                            break;
+                        case MatchUtils.STR_MPLS_TC:
+                            if (actionData[1].startsWith("0x")) {
+                                a = f.actions().buildSetField()
+                                        .setField(f.oxms().buildMplsTc().setValue(U8.of(Short.parseShort(actionData[1].replaceFirst("0x", ""), 16))).build())
+                                        .build();
+                            } else {
+                                a = f.actions().buildSetField()
+                                        .setField(f.oxms().buildMplsTc().setValue(U8.of(Short.parseShort(actionData[1]))).build())
+                                        .build();
+                            }
+                            break;
+                        case MatchUtils.STR_MPLS_BOS:
+                            a = f.actions().buildSetField()
+                            .setField(f.oxms().buildMplsBos().setValue(OFBooleanValue.of(Boolean.parseBoolean(actionData[1]))).build()) // interprets anything other than "true" as false
+                            .build();
+                            break;
+                        case MatchUtils.STR_METADATA:
+                            if (actionData[1].startsWith("0x")) {
+                                a = f.actions().buildSetField()
+                                        .setField(f.oxms().buildMetadata().setValue(OFMetadata.of(U64.of(Long.parseLong(actionData[1].replaceFirst("0x", ""), 16)))).build())
+                                        .build();
+                            } else {
+                                a = f.actions().buildSetField()
+                                        .setField(f.oxms().buildMetadata().setValue(OFMetadata.of(U64.of(Long.parseLong(actionData[1])))).build())
+                                        .build();
+                            }
+                            break;
+                        case MatchUtils.STR_ACTSET_OUTPUT:
+                                a = f.actions().buildSetField()
+                                        .setField(f.oxms().buildActsetOutput().setValue(portFromString(actionData[1])).build())
+                                        .build();
+                           
+                            break;
+                        case MatchUtils.STR_TCP_FLAGS:
+                            if (actionData[1].startsWith("0x")) {
+                                a = f.actions().buildSetField()
+                                        .setField(f.oxms().buildTcpFlags().setValue(U16.of(Integer.parseInt(actionData[1].replaceFirst("0x", ""), 16))).build())
+                                        .build();
+                            } else {
+                                a = f.actions().buildSetField()
+                                        .setField(f.oxms().buildTcpFlags().setValue(U16.of(Integer.parseInt(actionData[1]))).build())
+                                        .build();
+                            }
+                            break;
+                        default:
+                            log.error("Unexpected OF1.2+ setfield '{}'", actionData);
+                            break;
+                        }					
+                        break;
+                    case STR_GROUP:
+                        if (pair.startsWith("0x")) {
+                            a = f.actions().buildGroup()
+                                    .setGroup(OFGroup.of(Integer.parseInt(pair.replaceFirst("0x", ""), 16)))
+                                    .build();	
+                        } else {
+                            a = f.actions().buildGroup()
+                                    .setGroup(OFGroup.of(Integer.parseInt(pair)))
+                                    .build();		
+                        }
+                        break;
+                    case STR_MPLS_LABEL_SET:
+                        if (pair.startsWith("0x")) {
+                            a = f.actions().buildSetMplsLabel()
+                                    .setMplsLabel(Long.parseLong(pair.replaceFirst("0x", ""), 16))
+                                    .build();			
+                        } else {
+                            a = f.actions().buildSetMplsLabel()
+                                    .setMplsLabel(Long.parseLong(pair))
+                                    .build();					
+                        }
+                        break;
+                    case STR_MPLS_POP:
+                        if (pair.startsWith("0x")) {
+                            a = f.actions().buildPopMpls()
+                                    .setEthertype(EthType.of(Integer.parseInt(pair.replaceFirst("0x", ""), 16)))
+                                    .build();
+                        } else {
+                            a = f.actions().buildPopMpls()
+                                    .setEthertype(EthType.of(Integer.parseInt(pair)))
+                                    .build();	
+                        }
+                        break;
+                    case STR_MPLS_PUSH:
+                        if (pair.startsWith("0x")) {
+                            a = f.actions().buildPushMpls()
+                                    .setEthertype(EthType.of(Integer.parseInt(pair.replaceFirst("0x", ""), 16)))
+                                    .build();		
+                        } else {
+                            a = f.actions().buildPushMpls()
+                                    .setEthertype(EthType.of(Integer.parseInt(pair)))
+                                    .build();			
+                        }
+                        break;
+                    case STR_MPLS_TC_SET:
+                        if (pair.startsWith("0x")) {
+                            a = f.actions().buildSetMplsTc()
+                                    .setMplsTc(Short.parseShort(pair.replaceFirst("0x", ""), 16))
+                                    .build();	
+                        } else {
+                            a = f.actions().buildSetMplsTc()
+                                    .setMplsTc(Short.parseShort(pair))
+                                    .build();			
+                        }
+                        break;
+                    case STR_MPLS_TTL_DEC:
+                        a = f.actions().decMplsTtl();
+                        break;
+                    case STR_MPLS_TTL_SET:
+                        if (pair.startsWith("0x")) {
+                            a = f.actions().buildSetMplsTtl()
+                                    .setMplsTtl(Short.parseShort(pair.replaceFirst("0x", ""), 16))
+                                    .build();	
+                        } else {
+                            a = f.actions().buildSetMplsTtl()
+                                    .setMplsTtl(Short.parseShort(pair))
+                                    .build();				
+                        }
+                        break;
+                    case STR_NW_TOS_SET:
+                        a = decode_set_tos_bits(pair, v); // should only be used by OF1.0
+                        break;
+                    case STR_NW_SRC_SET:
+                        a = decode_set_src_ip(pair, v);
+                        break;
+                    case STR_NW_DST_SET:
+                        a = decode_set_dst_ip(pair, v);
+                        break;
+                    case STR_NW_ECN_SET: // loxi does not support DSCP set for OF1.1
+                        if (pair.startsWith("0x")) {
+                            a = f.actions().buildSetNwEcn()
+                                    .setNwEcn(IpEcn.of(Byte.parseByte(pair.replaceFirst("0x", ""), 16)))
+                                    .build();		
+                        } else {
+                            a = f.actions().buildSetNwEcn()
+                                    .setNwEcn(IpEcn.of(Byte.parseByte(pair)))
+                                    .build();							
+                        }
+                        break;
+                    case STR_NW_TTL_DEC:
+                        a = f.actions().decNwTtl();
+                        break;
+                    case STR_NW_TTL_SET:
+                        if (pair.startsWith("0x")) {
+                            a = f.actions().buildSetNwTtl()
+                                    .setNwTtl(Short.parseShort(pair.replaceFirst("0x", ""), 16))
+                                    .build();
+                        } else {
+                            a = f.actions().buildSetNwTtl()
+                                    .setNwTtl(Short.parseShort(pair))
+                                    .build();						
+                        }
+                        break;
+                    case STR_PBB_POP:
+                        a = f.actions().popPbb();
+                        break;
+                    case STR_PBB_PUSH:
+                        if (pair.startsWith("0x")) {
+                            a = f.actions().buildPushPbb()
+                                    .setEthertype(EthType.of(Integer.parseInt(pair.replaceFirst("0x", ""), 16)))
+                                    .build();				
+                        } else {
+                            a = f.actions().buildPushPbb()
+                                    .setEthertype(EthType.of(Integer.parseInt(pair)))
+                                    .build();					
+                        }
+                        break;
+                    case STR_QUEUE_SET:
+                        if (pair.startsWith("0x")) {
+                            a = f.actions().buildSetQueue()
+                                    .setQueueId(Long.parseLong(pair.replaceFirst("0x", ""), 16))
+                                    .build();	
+                        } else {
+                            a = f.actions().buildSetQueue()
+                                    .setQueueId(Long.parseLong(pair))
+                                    .build();					
+                        }
+                        break;
+                    case STR_TP_SRC_SET:
+                        a = decode_set_src_port(pair, v);
+                        break;
+                    case STR_TP_DST_SET:
+                        a = decode_set_dst_port(pair, v);
+                        break;
+                    case STR_TTL_IN_COPY:
+                        a = f.actions().copyTtlIn();
+                        break;
+                    case STR_TTL_OUT_COPY:
+                        a = f.actions().copyTtlOut();
+                        break;
+                    case STR_VLAN_POP:
+                        a = f.actions().popVlan();
+                        break;
+                    case STR_VLAN_PUSH:
+                        if (pair.startsWith("0x")) {
+                            a = f.actions().buildPushVlan()
+                                    .setEthertype(EthType.of(Integer.parseInt(pair.replaceFirst("0x", ""), 16)))
+                                    .build();
+                        } else {
+                            a = f.actions().buildPushVlan()
+                                    .setEthertype(EthType.of(Integer.parseInt(pair)))
+                                    .build();		
+                        }
+                        break;
+                    case STR_VLAN_STRIP:
+                        a = f.actions().stripVlan();
+                        break;
+                    case STR_VLAN_SET_VID:
+                        a = decode_set_vlan_id(pair, v);
+                        break;
+                    case STR_VLAN_SET_PCP:
+                        a = decode_set_vlan_priority(pair, v);
+                        break;
+                    case STR_METER:
+                        if (pair.startsWith("0x")) {
+                            a = f.actions().buildMeter()
+                                    .setMeterId(Long.parseLong(pair.replaceFirst("0x", ""), 16))
+                                    .build();
+                        } else {
+                            a = f.actions().buildMeter()
+                                    .setMeterId(Long.parseLong(pair))
+                                    .build();	
+                        }
+                        break;
+                    case STR_FIELD_COPY:
+                        a = (OFAction) copyFieldFromJson(pair, f.getVersion());
+                        break;
+                    default:
+                        log.error("Unexpected action key '{}'", keyPair);
+                        break;
+                    }
 
-				} catch (Exception e) {
-					log.error("Illegal Action: {}", e.getMessage());
-				}
-				if (a != null) {
-					actions.add(a);
-				}
-			}
-		} else {
-			log.debug("actions not found --> drop");
-		}	
-		return actions;
-	}
+                } catch (Exception e) {
+                    log.error("Illegal Action: {}", e.getMessage());
+                }
+                if (a != null) {
+                    actions.add(a);
+                }
+            }
+        } else {
+            log.debug("actions not found --> drop");
+        }	
+        return actions;
+    }
 
-	/**
-	 * Append OFActionCopyField object to an existing JsonGenerator.
-	 * This method assumes the field name of the action has been
-	 * written already, if required. The appended data will
-	 * be formatted as follows:
-	 *   {
-	 *       "src_field":"name",
-	 *       "dst_field":"name",
-	 *       "src_offset_bits":"bits",
-	 *       "dst_offset_bits":"bits",
-	 *       "num_bits":"bits"
-	 *   }
-	 * @param jsonGen
-	 * @param c
-	 */
-	public static void copyFieldToJson(JsonGenerator jsonGen, OFActionCopyField c) {
-		jsonGen.configure(Feature.WRITE_NUMBERS_AS_STRINGS, true);
+    /**
+     * Append OFActionCopyField object to an existing JsonGenerator.
+     * This method assumes the field name of the action has been
+     * written already, if required. The appended data will
+     * be formatted as follows:
+     *   {
+     *       "src_field":"name",
+     *       "dst_field":"name",
+     *       "src_offset_bits":"bits",
+     *       "dst_offset_bits":"bits",
+     *       "num_bits":"bits"
+     *   }
+     * @param jsonGen
+     * @param c
+     */
+    public static void copyFieldToJson(JsonGenerator jsonGen, OFActionCopyField c) {
+        jsonGen.configure(Feature.WRITE_NUMBERS_AS_STRINGS, true);
 
-		try {
-			jsonGen.writeStartObject();
-			Iterator<OFOxm<?>> i = c.getOxmIds().iterator();
-			if (i.hasNext()) {
-				jsonGen.writeStringField("src_field" , OXMUtils.oxmIdToString(U32.of(i.next().getCanonical().getTypeLen())));
-			} else {
-				log.error("either src_field or dst_field or both not set in {}", c);
-			}
-			if (i.hasNext()) {
-				jsonGen.writeStringField("dst_field" , OXMUtils.oxmIdToString(U32.of(i.next().getCanonical().getTypeLen())));
-			} else {
-				log.error("either src_field or dst_field not set in {}", c);
-			}
-			if (i.hasNext()) {
-				log.warn("OFOxmList should only have src_field followed by dst_field. Extra field {}", i.next());
-			}
-			jsonGen.writeNumberField("src_offset_bits", c.getSrcOffset());
-			jsonGen.writeNumberField("dst_offset_bits", c.getDstOffset());
-			jsonGen.writeNumberField("num_bits", c.getNBits());
-			jsonGen.writeEndObject();
-			jsonGen.close();
-		} catch (IOException e) {
-			log.error("Error composing OFActionCopyField JSON object. {}", e.getMessage());
-			return;
-		}
-	}
+        try {
+            jsonGen.writeStartObject();
+            Iterator<OFOxm<?>> i = c.getOxmIds().iterator();
+            if (i.hasNext()) {
+                jsonGen.writeStringField("src_field" , OXMUtils.oxmIdToString(U32.of(i.next().getCanonical().getTypeLen())));
+            } else {
+                log.error("either src_field or dst_field or both not set in {}", c);
+            }
+            if (i.hasNext()) {
+                jsonGen.writeStringField("dst_field" , OXMUtils.oxmIdToString(U32.of(i.next().getCanonical().getTypeLen())));
+            } else {
+                log.error("either src_field or dst_field not set in {}", c);
+            }
+            if (i.hasNext()) {
+                log.warn("OFOxmList should only have src_field followed by dst_field. Extra field {}", i.next());
+            }
+            jsonGen.writeNumberField("src_offset_bits", c.getSrcOffset());
+            jsonGen.writeNumberField("dst_offset_bits", c.getDstOffset());
+            jsonGen.writeNumberField("num_bits", c.getNBits());
+            jsonGen.writeEndObject();
+            jsonGen.close();
+        } catch (IOException e) {
+            log.error("Error composing OFActionCopyField JSON object. {}", e.getMessage());
+            return;
+        }
+    }
 
-	/**
-	 * Convert OFActionCopyField object to a JSON string.
-	 * This method assumes the field name of the action has been
-	 * written already, if required. The appended data will
-	 * be formatted as follows:
-	 *   {
-	 *       "src_field":"name",
-	 *       "dst_field":"name",
-	 *       "src_offset_bits":"bits",
-	 *       "dst_offset_bits":"bits",
-	 *       "num_bits":"bits"
-	 *   }
-	 * @param jsonGen
-	 * @param c
-	 */
-	public static String copyFieldToJson(OFActionCopyField c) {
-		Writer w = new StringWriter();
-		JsonGenerator jsonGen;
-		try {
-			jsonGen = jsonFactory.createGenerator(w);
-		} catch (IOException e) {
-			log.error("Could not instantiate JSON Generator. {}", e.getMessage());
-			return JSON_EMPTY_OBJECT;
-		}
+    /**
+     * Convert OFActionCopyField object to a JSON string.
+     * This method assumes the field name of the action has been
+     * written already, if required. The appended data will
+     * be formatted as follows:
+     *   {
+     *       "src_field":"name",
+     *       "dst_field":"name",
+     *       "src_offset_bits":"bits",
+     *       "dst_offset_bits":"bits",
+     *       "num_bits":"bits"
+     *   }
+     * @param jsonGen
+     * @param c
+     */
+    public static String copyFieldToJson(OFActionCopyField c) {
+        Writer w = new StringWriter();
+        JsonGenerator jsonGen;
+        try {
+            jsonGen = jsonFactory.createGenerator(w);
+        } catch (IOException e) {
+            log.error("Could not instantiate JSON Generator. {}", e.getMessage());
+            return JSON_EMPTY_OBJECT;
+        }
 
-		copyFieldToJson(jsonGen, c);
+        copyFieldToJson(jsonGen, c);
 
-		return w.toString(); /* overridden impl returns contents of Writer's StringBuffer */
-	}
+        return w.toString(); /* overridden impl returns contents of Writer's StringBuffer */
+    }
 
-	/**
-	 * Convert a JSON string to an OFActionCopyField object.
-	 * The format of the input JSON is expected to be:
-	 *   {
-	 *       "src_field":"name",
-	 *       "dst_field":"name",
-	 *       "src_offset_bits":"bits",
-	 *       "dst_offset_bits":"bits",
-	 *       "num_bits":"bits"
-	 *   }
-	 * @param json
-	 * @param v
-	 * @return
-	 */
-	public static OFActionCopyField copyFieldFromJson(String json, OFVersion v) {
-		if (json == null) {
-			throw new IllegalArgumentException("JSON string cannot be null");
-		}
-		if (v == null) {
-			throw new IllegalArgumentException("OFVersion cannot be null");
-		}
+    /**
+     * Convert a JSON string to an OFActionCopyField object.
+     * The format of the input JSON is expected to be:
+     *   {
+     *       "src_field":"name",
+     *       "dst_field":"name",
+     *       "src_offset_bits":"bits",
+     *       "dst_offset_bits":"bits",
+     *       "num_bits":"bits"
+     *   }
+     * @param json
+     * @param v
+     * @return
+     */
+    public static OFActionCopyField copyFieldFromJson(String json, OFVersion v) {
+        if (json == null) {
+            throw new IllegalArgumentException("JSON string cannot be null");
+        }
+        if (v == null) {
+            throw new IllegalArgumentException("OFVersion cannot be null");
+        }
 
-		final JsonParser jp;
-		try {
-			jp = jsonFactory.createParser(json);
-		} catch (IOException e) {
-			log.error("Could not create JSON parser for OFActionCopyField {}", json);
-			return null;
-		}
-		try {
-			if (jp.nextToken() != JsonToken.START_OBJECT) {
-				throw new IOException("Expected START_OBJECT");
-			}
-			
-			OFActionCopyField.Builder b = OFFactories.getFactory(v).buildActionCopyField();
-			OFOxm<?> srcField = null;
-			OFOxm<?> dstField = null;
-			
-			while (jp.nextToken() != JsonToken.END_OBJECT) {
-				String key = jp.getCurrentName().toLowerCase().trim();
-				jp.nextToken();
-				String value = jp.getText().toLowerCase().trim();
-				switch (key) {
-				case "src_field":
-					srcField = OXMUtils.oxmStringToOxm(value, v);
-					break;
-				case "dst_field":
-					dstField = OXMUtils.oxmStringToOxm(value, v);
-					break;
-				case "src_offset_bits":
-					b.setSrcOffset(Integer.parseInt(value));
-					break;
-				case "dst_offset_bits":
-					b.setDstOffset(Integer.parseInt(value));
-					break;
-				case "num_bits":
-					b.setNBits(Integer.parseInt(value));
-					break;
-				default:
-					log.warn("Unexpected OFActionCopyField key {}", key);
-					break;
-				}
-			}
-			if (srcField == null || dstField == null) {
-				log.error("Src and dst OXMs must be specified. Got {} and {}, respectively", srcField, dstField);
-				return null;
-			} else {
-				b.setOxmIds(OFOxmList.of(srcField, dstField));
-				return b.build();
-			}
-		} catch (IOException e) {
-			log.error("Could not parse: {}", json);
-			log.error("JSON parse error message: {}", e.getMessage());
-			return null;
-		}
-	}
+        final JsonParser jp;
+        try {
+            jp = jsonFactory.createParser(json);
+        } catch (IOException e) {
+            log.error("Could not create JSON parser for OFActionCopyField {}", json);
+            return null;
+        }
+        try {
+            if (jp.nextToken() != JsonToken.START_OBJECT) {
+                throw new IOException("Expected START_OBJECT");
+            }
 
-	/**
-	 * Parses OFFlowMod actions from strings.
-	 * @param fmb The OFFlowMod.Builder to set the actions for
-	 * @param s The string containing all the actions
-	 * @param log A logger to log for errors.
-	 */
-	public static void fromString(OFFlowMod.Builder fmb, String s) {
-		List<OFAction> actions = fromString(s, fmb.getVersion());
-		log.debug("actions: {}", actions);
-		fmb.setActions(actions);
-		return;
-	} 
+            OFActionCopyField.Builder b = OFFactories.getFactory(v).buildActionCopyField();
+            OFOxm<?> srcField = null;
+            OFOxm<?> dstField = null;
 
-	/**
-	 * Parse string and numerical port representations.
-	 * The key and delimiter for the action should be omitted, and only the
-	 * data should be presented to this decoder. Data can be any signed integer
-	 * or hex (w/leading 0x prefix) as a string or the special string port
-	 * STR_PORT_* as defined in {@link MatchUtils}.
-	 * 
-	 * @param actionToDecode; The action as a string to decode
-	 * @param version; The OF version to create the action for
-	 * @return
-	 */
-	private static OFActionOutput decode_output(String actionToDecode, OFVersion version) {
-		OFActionOutput.Builder ab = OFFactories.getFactory(version).actions().buildOutput();
-		actionToDecode = actionToDecode.trim().toLowerCase();
-		OFPort port = OFPort.ZERO;
-		switch (actionToDecode) {
-		case MatchUtils.STR_PORT_ALL:
-			port = OFPort.ALL;
-			break;
-		case MatchUtils.STR_PORT_CONTROLLER:
-			port = OFPort.CONTROLLER;
-			break;
-		case MatchUtils.STR_PORT_FLOOD:
-			port = OFPort.FLOOD;
-			break;
-		case MatchUtils.STR_PORT_IN_PORT:
-			port = OFPort.IN_PORT;
-			break;
-		case MatchUtils.STR_PORT_LOCAL:
-			port = OFPort.LOCAL;
-			break;
-		case MatchUtils.STR_PORT_NORMAL:
-			port = OFPort.NORMAL;
-			break;
-		case MatchUtils.STR_PORT_TABLE:
-			port = OFPort.TABLE;
-			break;
-		default:
-			log.debug("Port {} was not a special port string. Parsing as raw int or hex", actionToDecode);
-		}
-		if (!port.equals(OFPort.ZERO)) {
-			ab.setPort(port);
-			ab.setMaxLen(Integer.MAX_VALUE);
-			log.debug("action {}", ab);
-			return ab.build();
-		} else {
-			try {
-				port = OFPort.of(U32.of(actionToDecode.contains("0x") ? 
-						Long.parseLong(actionToDecode.replaceFirst("0x", ""), 16) : 
-							Long.parseLong(actionToDecode)).getRaw());
-				ab.setPort(port);
-				ab.setMaxLen(Integer.MAX_VALUE);
-				return ab.build();
-			} catch (NumberFormatException e) {
-				log.error("Could not parse port: '{}'", actionToDecode);
-				return null;
-			}
-		}
-	}
+            while (jp.nextToken() != JsonToken.END_OBJECT) {
+                String key = jp.getCurrentName().toLowerCase().trim();
+                jp.nextToken();
+                String value = jp.getText().toLowerCase().trim();
+                switch (key) {
+                case "src_field":
+                    srcField = OXMUtils.oxmStringToOxm(value, v);
+                    break;
+                case "dst_field":
+                    dstField = OXMUtils.oxmStringToOxm(value, v);
+                    break;
+                case "src_offset_bits":
+                    b.setSrcOffset(Integer.parseInt(value));
+                    break;
+                case "dst_offset_bits":
+                    b.setDstOffset(Integer.parseInt(value));
+                    break;
+                case "num_bits":
+                    b.setNBits(Integer.parseInt(value));
+                    break;
+                default:
+                    log.warn("Unexpected OFActionCopyField key {}", key);
+                    break;
+                }
+            }
+            if (srcField == null || dstField == null) {
+                log.error("Src and dst OXMs must be specified. Got {} and {}, respectively", srcField, dstField);
+                return null;
+            } else {
+                b.setOxmIds(OFOxmList.of(srcField, dstField));
+                return b.build();
+            }
+        } catch (IOException e) {
+            log.error("Could not parse: {}", json);
+            log.error("JSON parse error message: {}", e.getMessage());
+            return null;
+        }
+    }
 
-	/**
-	 * Parse enqueue actions.
-	 * The key and delimiter for the action should be omitted, and only the
-	 * data should be presented to this decoder. Data with a leading 0x is permitted.
-	 *
-	 * @param actionToDecode; The action as a string to decode
-	 * @param version; The OF version to create the action for
-	 * @return
-	 */
-	private static OFActionEnqueue decode_enqueue(String actionToDecode, OFVersion version) {
-		Matcher n = Pattern.compile("(?:((?:0x)?\\d+)\\:((?:0x)?\\d+))").matcher(actionToDecode);
-		if (n.matches()) {
-			OFPort port = OFPort.of(0);
-			if (n.group(1) != null) {
-				try {
-					port = OFPort.of(get_short(n.group(1)));
-				}
-				catch (NumberFormatException e) {
-					log.debug("Invalid port-num in: '{}' (error ignored)", actionToDecode);
-					return null;
-				}
-			}
+    /**
+     * Parses OFFlowMod actions from strings.
+     * @param fmb The OFFlowMod.Builder to set the actions for
+     * @param s The string containing all the actions
+     * @param log A logger to log for errors.
+     */
+    public static void fromString(OFFlowMod.Builder fmb, String s) {
+        List<OFAction> actions = fromString(s, fmb.getVersion());
+        log.debug("actions: {}", actions);
+        fmb.setActions(actions);
+        return;
+    }
 
-			int queueid = 0;
-			if (n.group(2) != null) {
-				try {
-					queueid = get_int(n.group(2));
-				}
-				catch (NumberFormatException e) {
-					log.debug("Invalid queue-id in: '{}' (error ignored)", actionToDecode);
-					return null;
-				}
-			}
-			OFActionEnqueue a = OFFactories.getFactory(version).actions().buildEnqueue()
-					.setPort(port)
-					.setQueueId(queueid)
-					.build();
-			log.debug("action {}", a);
-			return a;
-		}
-		else {
-			log.debug("Invalid action: '{}'", actionToDecode);
-			return null;
-		}
-	}
+    public static OFPort portFromString(String s) {
+        return MatchUtils.portFromString(s);
+    }
+    
+    public static String portToString(OFPort p) {
+        return MatchUtils.portToString(p);
+    }
 
-	/**
-	 * Parse set_vlan_id actions.
-	 * The key and delimiter for the action should be omitted, and only the
-	 * data should be presented to this decoder. Data with a leading 0x is permitted.
-	 * 
-	 * @param actionToDecode; The action as a string to decode
-	 * @param version; The OF version to create the action for
-	 * @return
-	 */
-	private static OFActionSetVlanVid decode_set_vlan_id(String actionToDecode, OFVersion version) {
-		Matcher n = Pattern.compile("((?:0x)?\\d+)").matcher(actionToDecode);
-		if (n.matches()) {            
-			if (n.group(1) != null) {
-				try {
-					VlanVid vlanid = VlanVid.ofVlan(get_short(n.group(1)));
-					OFActionSetVlanVid a = OFFactories.getFactory(version).actions().buildSetVlanVid()
-							.setVlanVid(vlanid)
-							.build();
-					log.debug("action {}", a);
-					return a;
-				}
-				catch (NumberFormatException e) {
-					log.debug("Invalid VLAN in: {} (error ignored)", actionToDecode);
-					return null;
-				}
-			}          
-		}
-		else {
-			log.debug("Invalid action: '{}'", actionToDecode);
-			return null;
-		}
-		return null;
-	}
+    /**
+     * Parse string and numerical port representations.
+     * The key and delimiter for the action should be omitted, and only the
+     * data should be presented to this decoder. Data can be any signed integer
+     * or hex (w/leading 0x prefix) as a string or the special string port
+     * STR_PORT_* as defined in {@link MatchUtils}.
+     * 
+     * @param actionToDecode; The action as a string to decode
+     * @param version; The OF version to create the action for
+     * @return
+     */
+    private static OFActionOutput decode_output(String actionToDecode, OFVersion version) {
+        OFActionOutput.Builder ab = OFFactories.getFactory(version).actions().buildOutput();
+        OFPort port = portFromString(actionToDecode);
+        if (port == null) {
+            log.error("Could not parse output port {}", actionToDecode);
+            return null;
+        } else {
+            ab.setPort(port);
+            ab.setMaxLen(Integer.MAX_VALUE);
+            log.debug("action {}", ab);
+            return ab.build();
+        } 
+    }
 
-	/**
-	 * Parse set_vlan_pcp actions.
-	 * The key and delimiter for the action should be omitted, and only the
-	 * data should be presented to this decoder. Data with a leading 0x is permitted.
-	 * 
-	 * @param actionToDecode; The action as a string to decode
-	 * @param version; The OF version to create the action for
-	 * @return
-	 */
-	private static OFActionSetVlanPcp decode_set_vlan_priority(String actionToDecode, OFVersion version) {
-		Matcher n = Pattern.compile("((?:0x)?\\d+)").matcher(actionToDecode); 
-		if (n.matches()) {            
-			if (n.group(1) != null) {
-				try {
-					OFActionSetVlanPcp a = OFFactories.getFactory(version).actions().buildSetVlanPcp()
-							.setVlanPcp(VlanPcp.of(get_byte(n.group(1))))
-							.build();
-					log.debug("action {}", a);
-					return a;
-				}
-				catch (NumberFormatException e) {
-					log.debug("Invalid VLAN priority in: {} (error ignored)", actionToDecode);
-					return null;
-				}
-			}
-		}
-		else {
-			log.debug("Invalid action: '{}'", actionToDecode);
-			return null;
-		}
-		return null;
-	}
+    /**
+     * Parse enqueue actions.
+     * The key and delimiter for the action should be omitted, and only the
+     * data should be presented to this decoder. Data with a leading 0x is permitted.
+     *
+     * @param actionToDecode; The action as a string to decode
+     * @param version; The OF version to create the action for
+     * @return
+     */
+    private static OFActionEnqueue decode_enqueue(String actionToDecode, OFVersion version) {
+        Matcher n = Pattern.compile("(?:((?:0x)?\\d+)\\:((?:0x)?\\d+))").matcher(actionToDecode);
+        if (n.matches()) {
+            OFPort port;
+            if (n.group(1) != null) {
+                port = portFromString(n.group(1));
+                if (port == null) {
+                    log.error("Invalid port {}", n.group(1));
+                    return null;
+                }
+            } else {
+                log.error("Missing port number for enqueue action");
+                return null;
+            }
 
-	/**
-	 * Parse set_dl_src actions.
-	 * The key and delimiter for the action should be omitted, and only the
-	 * data should be presented to this decoder.
-	 * 
-	 * @param actionToDecode; The action as a string to decode
-	 * @param version; The OF version to create the action for
-	 * @return
-	 */
-	private static OFActionSetDlSrc decode_set_src_mac(String actionToDecode, OFVersion version) {
-		try {
-			OFActionSetDlSrc a = OFFactories.getFactory(version).actions().buildSetDlSrc()
-					.setDlAddr(MacAddress.of(actionToDecode))
-					.build();
-			log.debug("action {}", a);
-			return a;
-		}
-		catch (Exception e) {
-			log.debug("Invalid action: '{}'", actionToDecode);
-			return null;
-		}
-	}
+            int queueid = 0;
+            if (n.group(2) != null) {
+                try {
+                    queueid = get_int(n.group(2));
+                }
+                catch (NumberFormatException e) {
+                    log.debug("Invalid queue-id in: '{}' (error ignored)", actionToDecode);
+                    return null;
+                }
+            }
+            OFActionEnqueue a = OFFactories.getFactory(version).actions().buildEnqueue()
+                    .setPort(port)
+                    .setQueueId(queueid)
+                    .build();
+            log.debug("action {}", a);
+            return a;
+        }
+        else {
+            log.debug("Invalid action: '{}'", actionToDecode);
+            return null;
+        }
+    }
 
-	/**
-	 * Parse set_dl_dst actions.
-	 * The key and delimiter for the action should be omitted, and only the
-	 * data should be presented to this decoder.
-	 * 
-	 * @param actionToDecode; The action as a string to decode
-	 * @param version; The OF version to create the action for
-	 * @return
-	 */
-	private static OFActionSetDlDst decode_set_dst_mac(String actionToDecode, OFVersion version) {
-		try {
-			OFActionSetDlDst a = OFFactories.getFactory(version).actions().buildSetDlDst()
-					.setDlAddr(MacAddress.of(actionToDecode))
-					.build();
-			log.debug("action {}", a);
-			return a;
-		}
-		catch (Exception e) {
-			log.debug("Invalid action: '{}'", actionToDecode);
-			return null;
-		}
-	}
+    /**
+     * Parse set_vlan_id actions.
+     * The key and delimiter for the action should be omitted, and only the
+     * data should be presented to this decoder. Data with a leading 0x is permitted.
+     * 
+     * @param actionToDecode; The action as a string to decode
+     * @param version; The OF version to create the action for
+     * @return
+     */
+    private static OFActionSetVlanVid decode_set_vlan_id(String actionToDecode, OFVersion version) {
+        Matcher n = Pattern.compile("((?:0x)?\\d+)").matcher(actionToDecode);
+        if (n.matches()) {            
+            if (n.group(1) != null) {
+                try {
+                    VlanVid vlanid = VlanVid.ofVlan(get_short(n.group(1)));
+                    OFActionSetVlanVid a = OFFactories.getFactory(version).actions().buildSetVlanVid()
+                            .setVlanVid(vlanid)
+                            .build();
+                    log.debug("action {}", a);
+                    return a;
+                }
+                catch (NumberFormatException e) {
+                    log.debug("Invalid VLAN in: {} (error ignored)", actionToDecode);
+                    return null;
+                }
+            }          
+        }
+        else {
+            log.debug("Invalid action: '{}'", actionToDecode);
+            return null;
+        }
+        return null;
+    }
 
-	/**
-	 * Parse set_tos actions.
-	 * The key and delimiter for the action should be omitted, and only the
-	 * data should be presented to this decoder. A leading 0x is permitted.
-	 * 
-	 * @param actionToDecode; The action as a string to decode
-	 * @param version; The OF version to create the action for
-	 * @return
-	 */
-	private static OFActionSetNwTos decode_set_tos_bits(String actionToDecode, OFVersion version) {
-		Matcher n = Pattern.compile("((?:0x)?\\d+)").matcher(actionToDecode); 
-		if (n.matches()) {
-			if (n.group(1) != null) {
-				try {
-					OFActionSetNwTos a = OFFactories.getFactory(version).actions().buildSetNwTos()
-							.setNwTos(get_byte(n.group(1)))
-							.build();
-					log.debug("action {}", a);
-					return a;
-				}
-				catch (NumberFormatException e) {
-					log.debug("Invalid dst-port in: {} (error ignored)", actionToDecode);
-					return null;
-				}
-			}
-		}
-		else {
-			log.debug("Invalid action: '{}'", actionToDecode);
-			return null;
-		}
-		return null;
-	}
+    /**
+     * Parse set_vlan_pcp actions.
+     * The key and delimiter for the action should be omitted, and only the
+     * data should be presented to this decoder. Data with a leading 0x is permitted.
+     * 
+     * @param actionToDecode; The action as a string to decode
+     * @param version; The OF version to create the action for
+     * @return
+     */
+    private static OFActionSetVlanPcp decode_set_vlan_priority(String actionToDecode, OFVersion version) {
+        Matcher n = Pattern.compile("((?:0x)?\\d+)").matcher(actionToDecode); 
+        if (n.matches()) {            
+            if (n.group(1) != null) {
+                try {
+                    OFActionSetVlanPcp a = OFFactories.getFactory(version).actions().buildSetVlanPcp()
+                            .setVlanPcp(VlanPcp.of(get_byte(n.group(1))))
+                            .build();
+                    log.debug("action {}", a);
+                    return a;
+                }
+                catch (NumberFormatException e) {
+                    log.debug("Invalid VLAN priority in: {} (error ignored)", actionToDecode);
+                    return null;
+                }
+            }
+        }
+        else {
+            log.debug("Invalid action: '{}'", actionToDecode);
+            return null;
+        }
+        return null;
+    }
 
-	/**
-	 * Parse set_nw_src actions.
-	 * The key and delimiter for the action should be omitted, and only the
-	 * data should be presented to this decoder.
-	 * 
-	 * @param actionToDecode; The action as a string to decode
-	 * @param version; The OF version to create the action for
-	 * @return
-	 */
-	private static OFActionSetNwSrc decode_set_src_ip(String actionToDecode, OFVersion version) {
-		try {
-			OFActionSetNwSrc a = OFFactories.getFactory(version).actions().buildSetNwSrc()
-					.setNwAddr(IPv4Address.of(actionToDecode))
-					.build();
-			log.debug("action {}", a);
-			return a;
-		} catch (Exception e) {
-			log.debug("Invalid action: '{}'", actionToDecode);
-			return null;
-		}
-	}
+    /**
+     * Parse set_dl_src actions.
+     * The key and delimiter for the action should be omitted, and only the
+     * data should be presented to this decoder.
+     * 
+     * @param actionToDecode; The action as a string to decode
+     * @param version; The OF version to create the action for
+     * @return
+     */
+    private static OFActionSetDlSrc decode_set_src_mac(String actionToDecode, OFVersion version) {
+        try {
+            OFActionSetDlSrc a = OFFactories.getFactory(version).actions().buildSetDlSrc()
+                    .setDlAddr(MacAddress.of(actionToDecode))
+                    .build();
+            log.debug("action {}", a);
+            return a;
+        }
+        catch (Exception e) {
+            log.debug("Invalid action: '{}'", actionToDecode);
+            return null;
+        }
+    }
 
-	/**
-	 * Parse set_nw_dst actions.
-	 * The key and delimiter for the action should be omitted, and only the
-	 * data should be presented to this decoder.
-	 * 
-	 * @param actionToDecode; The action as a string to decode
-	 * @param version; The OF version to create the action for
-	 * @return
-	 */
-	private static OFActionSetNwDst decode_set_dst_ip(String actionToDecode, OFVersion version) {
-		try {
-			OFActionSetNwDst a = OFFactories.getFactory(version).actions().buildSetNwDst()
-					.setNwAddr(IPv4Address.of(actionToDecode))
-					.build();
-			log.debug("action {}", a);
-			return a;
-		} catch (Exception e) {
-			log.debug("Invalid action: '{}'", actionToDecode);
-			return null;
-		}
-	}
+    /**
+     * Parse set_dl_dst actions.
+     * The key and delimiter for the action should be omitted, and only the
+     * data should be presented to this decoder.
+     * 
+     * @param actionToDecode; The action as a string to decode
+     * @param version; The OF version to create the action for
+     * @return
+     */
+    private static OFActionSetDlDst decode_set_dst_mac(String actionToDecode, OFVersion version) {
+        try {
+            OFActionSetDlDst a = OFFactories.getFactory(version).actions().buildSetDlDst()
+                    .setDlAddr(MacAddress.of(actionToDecode))
+                    .build();
+            log.debug("action {}", a);
+            return a;
+        }
+        catch (Exception e) {
+            log.debug("Invalid action: '{}'", actionToDecode);
+            return null;
+        }
+    }
 
-	/**
-	 * Parse set_tp_src actions.
-	 * The key and delimiter for the action should be omitted, and only the
-	 * data should be presented to this decoder. A leading 0x is permitted.
-	 * 
-	 * @param actionToDecode; The action as a string to decode
-	 * @param version; The OF version to create the action for
-	 * @return
-	 */
-	private static OFActionSetTpSrc decode_set_src_port(String actionToDecode, OFVersion version) {
-		try {
-			OFActionSetTpSrc a = OFFactories.getFactory(version).actions().buildSetTpSrc()
-					.setTpPort(TransportPort.of(Integer.parseInt(actionToDecode)))
-					.build();
-			log.debug("action {}", a);
-			return a;
-		} 
-		catch (NumberFormatException e) {
-			log.debug("Invalid src-port in: {} (error ignored)", actionToDecode);
-			return null;
-		}
-	}
+    /**
+     * Parse set_tos actions.
+     * The key and delimiter for the action should be omitted, and only the
+     * data should be presented to this decoder. A leading 0x is permitted.
+     * 
+     * @param actionToDecode; The action as a string to decode
+     * @param version; The OF version to create the action for
+     * @return
+     */
+    private static OFActionSetNwTos decode_set_tos_bits(String actionToDecode, OFVersion version) {
+        Matcher n = Pattern.compile("((?:0x)?\\d+)").matcher(actionToDecode); 
+        if (n.matches()) {
+            if (n.group(1) != null) {
+                try {
+                    OFActionSetNwTos a = OFFactories.getFactory(version).actions().buildSetNwTos()
+                            .setNwTos(get_byte(n.group(1)))
+                            .build();
+                    log.debug("action {}", a);
+                    return a;
+                }
+                catch (NumberFormatException e) {
+                    log.debug("Invalid dst-port in: {} (error ignored)", actionToDecode);
+                    return null;
+                }
+            }
+        }
+        else {
+            log.debug("Invalid action: '{}'", actionToDecode);
+            return null;
+        }
+        return null;
+    }
 
-	/**
-	 * Parse set_tp_dst actions.
-	 * The key and delimiter for the action should be omitted, and only the
-	 * data should be presented to this decoder. A leading 0x is permitted.
-	 * 
-	 * @param actionToDecode; The action as a string to decode
-	 * @param version; The OF version to create the action for
-	 * @return
-	 */
-	private static OFAction decode_set_dst_port(String actionToDecode, OFVersion version) {
-		try {
-			OFActionSetTpSrc a = OFFactories.getFactory(version).actions().buildSetTpSrc()
-					.setTpPort(TransportPort.of(Integer.parseInt(actionToDecode)))
-					.build();
-			log.debug("action {}", a);
-			return a;
-		} 
-		catch (NumberFormatException e) {
-			log.debug("Invalid src-port in: {} (error ignored)", actionToDecode);
-			return null;
-		}
-	}
+    /**
+     * Parse set_nw_src actions.
+     * The key and delimiter for the action should be omitted, and only the
+     * data should be presented to this decoder.
+     * 
+     * @param actionToDecode; The action as a string to decode
+     * @param version; The OF version to create the action for
+     * @return
+     */
+    private static OFActionSetNwSrc decode_set_src_ip(String actionToDecode, OFVersion version) {
+        try {
+            OFActionSetNwSrc a = OFFactories.getFactory(version).actions().buildSetNwSrc()
+                    .setNwAddr(IPv4Address.of(actionToDecode))
+                    .build();
+            log.debug("action {}", a);
+            return a;
+        } catch (Exception e) {
+            log.debug("Invalid action: '{}'", actionToDecode);
+            return null;
+        }
+    }
 
-	/**
-	 * Parse int as decimal, hex (start with 0x or #) or octal (starts with 0)
-	 * @param str
-	 * @return
-	 */
-	private static int get_int(String str) {
-		return Integer.decode(str);
-	}
+    /**
+     * Parse set_nw_dst actions.
+     * The key and delimiter for the action should be omitted, and only the
+     * data should be presented to this decoder.
+     * 
+     * @param actionToDecode; The action as a string to decode
+     * @param version; The OF version to create the action for
+     * @return
+     */
+    private static OFActionSetNwDst decode_set_dst_ip(String actionToDecode, OFVersion version) {
+        try {
+            OFActionSetNwDst a = OFFactories.getFactory(version).actions().buildSetNwDst()
+                    .setNwAddr(IPv4Address.of(actionToDecode))
+                    .build();
+            log.debug("action {}", a);
+            return a;
+        } catch (Exception e) {
+            log.debug("Invalid action: '{}'", actionToDecode);
+            return null;
+        }
+    }
 
-	/**
-	 * Parse short as decimal, hex (start with 0x or #) or octal (starts with 0)
-	 * @param str
-	 * @return
-	 */
-	private static short get_short(String str) {
-		return (short)(int)Integer.decode(str);
-	}
+    /**
+     * Parse set_tp_src actions.
+     * The key and delimiter for the action should be omitted, and only the
+     * data should be presented to this decoder. A leading 0x is permitted.
+     * 
+     * @param actionToDecode; The action as a string to decode
+     * @param version; The OF version to create the action for
+     * @return
+     */
+    private static OFActionSetTpSrc decode_set_src_port(String actionToDecode, OFVersion version) {
+        try {
+            OFActionSetTpSrc a = OFFactories.getFactory(version).actions().buildSetTpSrc()
+                    .setTpPort(TransportPort.of(Integer.parseInt(actionToDecode)))
+                    .build();
+            log.debug("action {}", a);
+            return a;
+        } 
+        catch (NumberFormatException e) {
+            log.debug("Invalid src-port in: {} (error ignored)", actionToDecode);
+            return null;
+        }
+    }
 
-	/**
-	 * Parse byte as decimal, hex (start with 0x or #) or octal (starts with 0)
-	 * @param str
-	 * @return
-	 */
-	private static byte get_byte(String str) {
-		return Integer.decode(str).byteValue();
-	}
+    /**
+     * Parse set_tp_dst actions.
+     * The key and delimiter for the action should be omitted, and only the
+     * data should be presented to this decoder. A leading 0x is permitted.
+     * 
+     * @param actionToDecode; The action as a string to decode
+     * @param version; The OF version to create the action for
+     * @return
+     */
+    private static OFAction decode_set_dst_port(String actionToDecode, OFVersion version) {
+        try {
+            OFActionSetTpSrc a = OFFactories.getFactory(version).actions().buildSetTpSrc()
+                    .setTpPort(TransportPort.of(Integer.parseInt(actionToDecode)))
+                    .build();
+            log.debug("action {}", a);
+            return a;
+        } 
+        catch (NumberFormatException e) {
+            log.debug("Invalid src-port in: {} (error ignored)", actionToDecode);
+            return null;
+        }
+    }
 
-}
+    /**
+     * Parse int as decimal, hex (start with 0x or #) or octal (starts with 0)
+     * @param str
+     * @return
+     */
+    private static int get_int(String str) {
+        return Integer.parseInt(str);
+    }
+
+    /**
+     * Parse short as decimal, hex (start with 0x or #) or octal (starts with 0)
+     * @param str
+     * @return
+     */
+    private static short get_short(String str) {
+        return (short) Integer.parseInt(str);
+    }
+
+    /**
+     * Parse byte as decimal, hex (start with 0x or #) or octal (starts with 0)
+     * @param str
+     * @return
+     */
+    private static byte get_byte(String str) {
+        return (byte) Integer.parseInt(str);
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/util/GroupUtils.java b/src/main/java/net/floodlightcontroller/util/GroupUtils.java
index 33bb2a6eafbe9dcdd8d54860322850dfb3a228ab..36bbab38a965f25489e4d4e1eb9194ecd91810a6 100644
--- a/src/main/java/net/floodlightcontroller/util/GroupUtils.java
+++ b/src/main/java/net/floodlightcontroller/util/GroupUtils.java
@@ -16,7 +16,6 @@ import org.projectfloodlight.openflow.protocol.OFGroupMod;
 import org.projectfloodlight.openflow.protocol.OFGroupModify;
 import org.projectfloodlight.openflow.protocol.OFGroupType;
 import org.projectfloodlight.openflow.types.OFGroup;
-import org.projectfloodlight.openflow.types.OFPort;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -35,305 +34,344 @@ import com.fasterxml.jackson.databind.MappingJsonFactory;
  *
  */
 public class GroupUtils {
-	private static final Logger log = LoggerFactory.getLogger(GroupUtils.class);
-
-	public static final String GROUP_ID = "group_id";
-
-	public static final String GROUP_TYPE = "group_type";
-	public static final String GROUP_TYPE_FF = "fast_failover";
-	public static final String GROUP_TYPE_ALL = "all";
-	public static final String GROUP_TYPE_SELECT = "select";
-	public static final String GROUP_TYPE_INDIRECT = "indirect";
-
-	public static final String GROUP_BUCKETS = "group_buckets";
-	public static final String BUCKET_ID = "bucket_id";
-	public static final String BUCKET_WEIGHT = "bucket_weight";
-	public static final String BUCKET_WATCH_PORT = "bucket_watch_port";
-	public static final String BUCKET_WATCH_GROUP = "bucket_watch_group";
-	public static final String BUCKET_ACTIONS = "bucket_actions";
-	
-	private static final JsonFactory jsonFactory = new JsonFactory();
-	
-	private static final String JSON_EMPTY_ARRAY = "[]";
-	private static final String JSON_EMPTY_VALUE = "";
-
-	private GroupUtils() { }
-	
-	public static OFGroupModify toGroupModify(OFGroupMod gm) {
-		return OFFactories.getFactory(gm.getVersion()).buildGroupModify()
-				.setBuckets(gm.getBuckets())
-				.setGroup(gm.getGroup())
-				.setGroupType(gm.getGroupType())
-				.setXid(gm.getXid())
-				.build();
-	}
-	
-	public static OFGroupDelete toGroupDelete(OFGroupMod gm) {
-		return OFFactories.getFactory(gm.getVersion()).buildGroupDelete()
-				/* don't care about buckets or type */
-				.setGroup(gm.getGroup())
-				.setGroupType(gm.getGroupType())
-				.setXid(gm.getXid())
-				.build();
-	}
-	
-	public static OFGroupAdd toGroupAdd(OFGroupMod gm) {
-		return OFFactories.getFactory(gm.getVersion()).buildGroupAdd()
-				.setBuckets(gm.getBuckets())
-				.setGroup(gm.getGroup())
-				.setGroupType(gm.getGroupType())
-				.setXid(gm.getXid())
-				.build();
-	}
-
-	public static boolean setGroupIdFromString(OFGroupMod.Builder g, String s) {
-		if (g == null) {
-			throw new IllegalArgumentException("OFGroupMod cannot be null");
-		}
-		if (s == null) {
-			throw new IllegalArgumentException("String cannot be null");
-		}
-
-		s = s.trim().toLowerCase();
-		try {
-			g.setGroup(OFGroup.of(s.startsWith("0x") ? Integer.parseInt(s, 16) : Integer.parseInt(s)));
-			return true;
-		} catch (Exception e) {
-			log.error("Could not parse group ID {}", s);
-			return false;
-		}
-	}
-
-	public static OFGroupType groupTypeFromString(String s) {
-		if (s == null) {
-			throw new IllegalArgumentException("String cannot be null");
-		}
-
-		s = s.trim().toLowerCase();
-
-		switch (s) {
-		case GROUP_TYPE_ALL:
-			return OFGroupType.ALL;
-		case GROUP_TYPE_FF:
-			return OFGroupType.FF;
-		case GROUP_TYPE_INDIRECT:
-			return OFGroupType.INDIRECT;
-		case GROUP_TYPE_SELECT:
-			return OFGroupType.SELECT;
-		default:
-			log.error("Unrecognized group type {}", s);
-			return null;
-		}
-	}
-	
-	public static boolean setGroupTypeFromString(OFGroupMod.Builder g, String s) {
-		if (g == null) {
-			throw new IllegalArgumentException("OFGroupMod cannot be null");
-		}
-
-		OFGroupType t = groupTypeFromString(s);
-		if (t != null) {
-			g.setGroupType(t);
-			return true;
-		}
-		return false;
-	}
-	
-	public static String groupTypeToString(OFGroupType t) {
-		if (t == null) {
-			throw new IllegalArgumentException("OFGroupType cannot be null");
-		}
-
-		switch (t) {
-		case ALL:
-			return GROUP_TYPE_ALL;
-		case FF:
-			return GROUP_TYPE_FF;
-		case INDIRECT:
-			return GROUP_TYPE_INDIRECT;
-		case SELECT:
-			return GROUP_TYPE_SELECT;
-		default:
-			log.error("Unrecognized group type {}", t);
-			return JSON_EMPTY_VALUE;
-		}
-	}
-
-	/**
-	 * Append an array of buckets to an existing JsonGenerator.
-	 * This method assumes the field name of the array has been
-	 * written already, if required. The appended data will
-	 * be formatted as follows:
-	 *   [
-	 *     {
-	 *       bucket-1
-	 *     },
-	 *     {
-	 *       bucket-2
-	 *     },
-	 *     ...,
-	 *     {
-	 *       bucket-n
-	 *     }
-	 *   ]
-	 * @param jsonGen
-	 * @param bucketList
-	 */
-	public static void groupBucketsToJsonArray(JsonGenerator jsonGen, List<OFBucket> bucketList) {
-		jsonGen.configure(Feature.WRITE_NUMBERS_AS_STRINGS, true);
-		
-		try {
-			int bucketId = 1;
-			jsonGen.writeStartArray();
-			for (OFBucket b : bucketList) {
-				jsonGen.writeStartObject();
-				jsonGen.writeNumberField(BUCKET_ID, bucketId++); /* not preserved from original, but indicates order */
-				jsonGen.writeNumberField(BUCKET_WATCH_GROUP, b.getWatchGroup().getGroupNumber());
-				jsonGen.writeNumberField(BUCKET_WATCH_PORT, b.getWatchPort().getPortNumber());
-				jsonGen.writeNumberField(BUCKET_WEIGHT, b.getWeight());
-				jsonGen.writeStringField(BUCKET_ACTIONS, ActionUtils.actionsToString(b.getActions())); /* TODO update to object array */
-				jsonGen.writeEndObject();
-			}
-			jsonGen.writeEndArray();
-		} catch (IOException e) {
-			log.error("Error composing group bucket JSON array. {}", e.getMessage());
-			return;
-		}
-	}
-	
-	/**
-	 * Convert a list of group buckets into a JSON-formatted
-	 * string. The string data will be formatted as follows:
-	 *   [
-	 *     {
-	 *       bucket-1
-	 *     },
-	 *     {
-	 *       bucket-2
-	 *     },
-	 *     ...,
-	 *     {
-	 *       bucket-n
-	 *     }
-	 *   ]
-	 * @param bucketList
-	 * @return the string, formatted as described above
-	 */
-	public static String groupBucketsToJsonArray(List<OFBucket> bucketList) {
-		Writer w = new StringWriter();
-		JsonGenerator jsonGen;
-		try {
-			jsonGen = jsonFactory.createGenerator(w);
-		} catch (IOException e) {
-			log.error("Could not instantiate JSON Generator. {}", e.getMessage());
-			return JSON_EMPTY_ARRAY;
-		}
-		
-		groupBucketsToJsonArray(jsonGen, bucketList);
-		
-		return w.toString(); /* overridden impl returns contents of Writer's StringBuffer */
-	}
-	
-	/**
-	 * Convert a JSON-formatted string of group buckets to a
-	 * Java list of buckets. The JSON format expected is:
-	 *   [
-	 *     {
-	 *       bucket-1
-	 *     },
-	 *     {
-	 *       bucket-2
-	 *     },
-	 *     ...,
-	 *     {
-	 *       bucket-n
-	 *     }
-	 *   ]
-	 * @param g the group-mod message to add the buckets
-	 * @param json the string, formatted as described above
-	 */
-	public static boolean setGroupBucketsFromJsonArray(OFGroupMod.Builder g, String json) {
-		final Map<Integer, OFBucket> bucketsById = new HashMap<Integer, OFBucket>();
-		final MappingJsonFactory f = new MappingJsonFactory();
-
-		if (g == null) {
-			throw new IllegalArgumentException("OFGroupMod cannot be null");
-		}
-		if (json == null) {
-			throw new IllegalArgumentException("JSON string cannot be null");
-		}
-
-		final JsonParser jp;
-		try {
-			jp = f.createParser(json);
-		} catch (IOException e) {
-			log.error("Could not create JSON parser for {}", json);
-			return false;
-		}
-
-		try {
-			if (jp.nextToken() != JsonToken.START_ARRAY) {
-				throw new IOException("Expected START_ARRAY");
-			}
-
-			while (jp.nextToken() != JsonToken.END_ARRAY) {
-				if (jp.getCurrentToken() != JsonToken.START_OBJECT) {
-					throw new IOException("Expected START_OBJECT");
-				}
-
-				OFBucket.Builder b = OFFactories.getFactory(g.getVersion()).buildBucket();
-				int bucketId = -1;
-
-				while (jp.nextToken() != JsonToken.END_OBJECT) {
-					if (jp.getCurrentToken() != JsonToken.FIELD_NAME) {
-						throw new IOException("Expected FIELD_NAME");
-					}
-
-					String key = jp.getCurrentName().toLowerCase().trim();
-					jp.nextToken();
-					String value = jp.getText().toLowerCase().trim();
-					switch (key) {
-					case BUCKET_ID:
-						bucketId = value.startsWith("0x") ? Integer.parseInt(value, 16) : Integer.parseInt(value);
-						break;
-					case BUCKET_WATCH_GROUP:
-						b.setWatchGroup(OFGroup.of(value.startsWith("0x") ? Integer.parseInt(value, 16) : Integer.parseInt(value)));
-						break;
-					case BUCKET_WATCH_PORT:
-						b.setWatchPort(OFPort.of(value.startsWith("0x") ? Integer.parseInt(value, 16) : Integer.parseInt(value)));
-						break;
-					case BUCKET_WEIGHT:
-						b.setWeight(value.startsWith("0x") ? Integer.parseInt(value, 16) : Integer.parseInt(value));
-						break;
-					case BUCKET_ACTIONS:
-						b.setActions(ActionUtils.fromString(value, b.getVersion()));
-						break;
-					default:
-						log.warn("Unknown bucket key {}", key);
-						break;
-					}
-				}
-				if (bucketId != -1) {
-					bucketsById.put(bucketId, b.build());
-				} else {
-					log.error("Must provide a bucket ID for bucket {}", b);
-				}
-			}
-		} catch (IOException e) {
-			log.error("Could not parse: {}", json);
-			log.error("JSON parse error message: {}", e.getMessage());
-			return false;
-		}
-
-		g.setBuckets(
-				bucketsById.entrySet()
-				.stream()
-				.sorted( /* invert to get sorted smallest to largest */
-						(e1, e2) -> Integer.compare(e1.getKey(), e2.getKey())
-						)
-						.map(Map.Entry::getValue)
-						.collect(Collectors.toList())
-				);
-		return true;
-	}
+    private static final Logger log = LoggerFactory.getLogger(GroupUtils.class);
+
+    public static final String GROUP_ID = "group_id";
+    public static final String GROUP_ID_MAX = "max";
+    public static final String GROUP_ID_ANY = "any";
+    public static final String GROUP_ID_ALL = "all";
+
+    public static final String GROUP_TYPE = "group_type";
+    public static final String GROUP_TYPE_FF = "fast_failover";
+    public static final String GROUP_TYPE_ALL = "all";
+    public static final String GROUP_TYPE_SELECT = "select";
+    public static final String GROUP_TYPE_INDIRECT = "indirect";
+
+    public static final String GROUP_BUCKETS = "group_buckets";
+    public static final String BUCKET_ID = "bucket_id";
+    public static final String BUCKET_WEIGHT = "bucket_weight";
+    public static final String BUCKET_WATCH_PORT = "bucket_watch_port";
+    public static final String BUCKET_WATCH_GROUP = "bucket_watch_group";
+    public static final String BUCKET_ACTIONS = "bucket_actions";
+
+    private static final JsonFactory jsonFactory = new JsonFactory();
+
+    private static final String JSON_EMPTY_ARRAY = "[]";
+    private static final String JSON_EMPTY_VALUE = "";
+
+    private GroupUtils() { }
+
+    public static OFGroupModify toGroupModify(OFGroupMod gm) {
+        return OFFactories.getFactory(gm.getVersion()).buildGroupModify()
+                .setBuckets(gm.getBuckets())
+                .setGroup(gm.getGroup())
+                .setGroupType(gm.getGroupType())
+                .setXid(gm.getXid())
+                .build();
+    }
+
+    public static OFGroupDelete toGroupDelete(OFGroupMod gm) {
+        return OFFactories.getFactory(gm.getVersion()).buildGroupDelete()
+                /* don't care about buckets or type */
+                .setGroup(gm.getGroup())
+                .setGroupType(gm.getGroupType())
+                .setXid(gm.getXid())
+                .build();
+    }
+
+    public static OFGroupAdd toGroupAdd(OFGroupMod gm) {
+        return OFFactories.getFactory(gm.getVersion()).buildGroupAdd()
+                .setBuckets(gm.getBuckets())
+                .setGroup(gm.getGroup())
+                .setGroupType(gm.getGroupType())
+                .setXid(gm.getXid())
+                .build();
+    }
+
+    public static boolean setGroupIdFromString(OFGroupMod.Builder g, String s) {
+        if (g == null) {
+            throw new IllegalArgumentException("OFGroupMod cannot be null");
+        }
+        if (s == null) {
+            throw new IllegalArgumentException("String cannot be null");
+        }
+        OFGroup group = groupIdFromString(s);
+        if (group == null) {
+            log.error("Could not set group ID {} due to parse error", s);
+            return false;
+        } else {
+            g.setGroup(group);
+            return true;
+        }	
+    }
+
+    public static OFGroup groupIdFromString(String s) {
+        if (s == null) {
+            throw new IllegalArgumentException("String cannot be null");
+        }
+        s = s.trim().toLowerCase();
+        try {
+            if (s.equals(GROUP_ID_ALL)) {
+                return OFGroup.ALL;
+            } else if (s.equals(GROUP_ID_ANY)) {
+                return OFGroup.ANY;
+            } else if (s.equals(GROUP_ID_MAX)) {
+                return OFGroup.MAX;
+            } else {
+                return OFGroup.of(s.startsWith("0x") ? Integer.parseInt(s.replaceFirst("0x", ""), 16) : Integer.parseInt(s));
+            }
+        } catch (Exception e) {
+            log.error("Could not parse group ID {}", s);
+            return null;
+        }
+    }
+
+    public static String groupIdToString(OFGroup g) {
+        if (g == null) {
+            throw new IllegalArgumentException("Group ID cannot be null");
+        }
+        if (g.equals(OFGroup.ALL)) {
+            return GROUP_ID_ALL;
+        }
+        if (g.equals(OFGroup.ANY)) {
+            return GROUP_ID_ANY;
+        }
+        if (g.equals(OFGroup.MAX)) {
+            return GROUP_ID_MAX;
+        }
+        return Integer.toString(g.getGroupNumber());
+    }
+
+    public static OFGroupType groupTypeFromString(String s) {
+        if (s == null) {
+            throw new IllegalArgumentException("String cannot be null");
+        }
+
+        s = s.trim().toLowerCase();
+
+        switch (s) {
+        case GROUP_TYPE_ALL:
+            return OFGroupType.ALL;
+        case GROUP_TYPE_FF:
+            return OFGroupType.FF;
+        case GROUP_TYPE_INDIRECT:
+            return OFGroupType.INDIRECT;
+        case GROUP_TYPE_SELECT:
+            return OFGroupType.SELECT;
+        default:
+            log.error("Unrecognized group type {}", s);
+            return null;
+        }
+    }
+
+    public static boolean setGroupTypeFromString(OFGroupMod.Builder g, String s) {
+        if (g == null) {
+            throw new IllegalArgumentException("OFGroupMod cannot be null");
+        }
+
+        OFGroupType t = groupTypeFromString(s);
+        if (t != null) {
+            g.setGroupType(t);
+            return true;
+        }
+        return false;
+    }
+
+    public static String groupTypeToString(OFGroupType t) {
+        if (t == null) {
+            throw new IllegalArgumentException("OFGroupType cannot be null");
+        }
+
+        switch (t) {
+        case ALL:
+            return GROUP_TYPE_ALL;
+        case FF:
+            return GROUP_TYPE_FF;
+        case INDIRECT:
+            return GROUP_TYPE_INDIRECT;
+        case SELECT:
+            return GROUP_TYPE_SELECT;
+        default:
+            log.error("Unrecognized group type {}", t);
+            return JSON_EMPTY_VALUE;
+        }
+    }
+
+    /**
+     * Append an array of buckets to an existing JsonGenerator.
+     * This method assumes the field name of the array has been
+     * written already, if required. The appended data will
+     * be formatted as follows:
+     *   [
+     *     {
+     *       bucket-1
+     *     },
+     *     {
+     *       bucket-2
+     *     },
+     *     ...,
+     *     {
+     *       bucket-n
+     *     }
+     *   ]
+     * @param jsonGen
+     * @param bucketList
+     */
+    public static void groupBucketsToJsonArray(JsonGenerator jsonGen, List<OFBucket> bucketList) {
+        jsonGen.configure(Feature.WRITE_NUMBERS_AS_STRINGS, true);
+
+        try {
+            int bucketId = 1;
+            jsonGen.writeStartArray();
+            for (OFBucket b : bucketList) {
+                jsonGen.writeStartObject();
+                jsonGen.writeNumberField(BUCKET_ID, bucketId++); /* not preserved from original, but indicates order */
+                jsonGen.writeStringField(BUCKET_WATCH_GROUP, groupIdToString(b.getWatchGroup()));
+                jsonGen.writeStringField(BUCKET_WATCH_PORT, MatchUtils.portToString(b.getWatchPort()));
+                jsonGen.writeNumberField(BUCKET_WEIGHT, b.getWeight());
+                jsonGen.writeStringField(BUCKET_ACTIONS, ActionUtils.actionsToString(b.getActions())); /* TODO update to object array */
+                jsonGen.writeEndObject();
+            }
+            jsonGen.writeEndArray();
+        } catch (IOException e) {
+            log.error("Error composing group bucket JSON array. {}", e.getMessage());
+            return;
+        }
+    }
+
+    /**
+     * Convert a list of group buckets into a JSON-formatted
+     * string. The string data will be formatted as follows:
+     *   [
+     *     {
+     *       bucket-1
+     *     },
+     *     {
+     *       bucket-2
+     *     },
+     *     ...,
+     *     {
+     *       bucket-n
+     *     }
+     *   ]
+     * @param bucketList
+     * @return the string, formatted as described above
+     */
+    public static String groupBucketsToJsonArray(List<OFBucket> bucketList) {
+        Writer w = new StringWriter();
+        JsonGenerator jsonGen;
+        try {
+            jsonGen = jsonFactory.createGenerator(w);
+        } catch (IOException e) {
+            log.error("Could not instantiate JSON Generator. {}", e.getMessage());
+            return JSON_EMPTY_ARRAY;
+        }
+
+        groupBucketsToJsonArray(jsonGen, bucketList);
+
+        return w.toString(); /* overridden impl returns contents of Writer's StringBuffer */
+    }
+
+    /**
+     * Convert a JSON-formatted string of group buckets to a
+     * Java list of buckets. The JSON format expected is:
+     *   [
+     *     {
+     *       bucket-1
+     *     },
+     *     {
+     *       bucket-2
+     *     },
+     *     ...,
+     *     {
+     *       bucket-n
+     *     }
+     *   ]
+     * @param g the group-mod message to add the buckets
+     * @param json the string, formatted as described above
+     */
+    public static boolean setGroupBucketsFromJsonArray(OFGroupMod.Builder g, String json) {
+        final Map<Integer, OFBucket> bucketsById = new HashMap<Integer, OFBucket>();
+        final MappingJsonFactory f = new MappingJsonFactory();
+
+        if (g == null) {
+            throw new IllegalArgumentException("OFGroupMod cannot be null");
+        }
+        if (json == null) {
+            throw new IllegalArgumentException("JSON string cannot be null");
+        }
+
+        final JsonParser jp;
+        try {
+            jp = f.createParser(json);
+        } catch (IOException e) {
+            log.error("Could not create JSON parser for {}", json);
+            return false;
+        }
+
+        try {
+            if (jp.nextToken() != JsonToken.START_ARRAY) {
+                throw new IOException("Expected START_ARRAY");
+            }
+
+            while (jp.nextToken() != JsonToken.END_ARRAY) {
+                if (jp.getCurrentToken() != JsonToken.START_OBJECT) {
+                    throw new IOException("Expected START_OBJECT");
+                }
+
+                OFBucket.Builder b = OFFactories.getFactory(g.getVersion()).buildBucket();
+                int bucketId = -1;
+
+                while (jp.nextToken() != JsonToken.END_OBJECT) {
+                    if (jp.getCurrentToken() != JsonToken.FIELD_NAME) {
+                        throw new IOException("Expected FIELD_NAME");
+                    }
+
+                    String key = jp.getCurrentName().toLowerCase().trim();
+                    jp.nextToken();
+                    String value = jp.getText().toLowerCase().trim();
+                    switch (key) {
+                    case BUCKET_ID:
+                        bucketId = value.startsWith("0x") ? Integer.parseInt(value.replaceFirst("0x", ""), 16) : Integer.parseInt(value);
+                        break;
+                    case BUCKET_WATCH_GROUP:
+                        b.setWatchGroup(groupIdFromString(key));
+                        break;
+                    case BUCKET_WATCH_PORT:
+                        b.setWatchPort(MatchUtils.portFromString(key));
+                        break;
+                    case BUCKET_WEIGHT:
+                        b.setWeight(value.startsWith("0x") ? Integer.parseInt(value.replaceFirst("0x", ""), 16) : Integer.parseInt(value));
+                        break;
+                    case BUCKET_ACTIONS:
+                        b.setActions(ActionUtils.fromString(value, b.getVersion())); // TODO update to JSON 
+                        break;
+                    default:
+                        log.warn("Unknown bucket key {}", key);
+                        break;
+                    }
+                }
+                if (bucketId != -1) {
+                    bucketsById.put(bucketId, b.build());
+                } else {
+                    log.error("Must provide a bucket ID for bucket {}", b);
+                }
+            }
+        } catch (IOException e) {
+            log.error("Could not parse: {}", json);
+            log.error("JSON parse error message: {}", e.getMessage());
+            return false;
+        }
+
+        g.setBuckets(
+                bucketsById.entrySet()
+                .stream()
+                .sorted( /* invert to get sorted smallest to largest */
+                        (e1, e2) -> Integer.compare(e1.getKey(), e2.getKey())
+                        )
+                .map(Map.Entry::getValue)
+                .collect(Collectors.toList())
+                );
+        return true;
+    }
 }
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/util/MatchUtils.java b/src/main/java/net/floodlightcontroller/util/MatchUtils.java
index 77fbf3492f79fe9edd99979cd3b6ed96016a5f91..b11a9ed061096b97c7dc5dc608651a8145770c24 100644
--- a/src/main/java/net/floodlightcontroller/util/MatchUtils.java
+++ b/src/main/java/net/floodlightcontroller/util/MatchUtils.java
@@ -27,7 +27,6 @@ import org.projectfloodlight.openflow.types.IpProtocol;
 import org.projectfloodlight.openflow.types.MacAddress;
 import org.projectfloodlight.openflow.types.OFBooleanValue;
 import org.projectfloodlight.openflow.types.OFMetadata;
-import org.projectfloodlight.openflow.types.OFPort;
 import org.projectfloodlight.openflow.types.OFVlanVidMatch;
 import org.projectfloodlight.openflow.types.OFVlanVidMatchWithMask;
 import org.projectfloodlight.openflow.types.PacketType;
@@ -37,6 +36,7 @@ import org.projectfloodlight.openflow.types.U32;
 import org.projectfloodlight.openflow.types.U64;
 import org.projectfloodlight.openflow.types.U8;
 import org.projectfloodlight.openflow.types.VlanPcp;
+import org.projectfloodlight.openflow.types.OFPort;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -77,6 +77,9 @@ public class MatchUtils {
 	public static final String STR_PORT_FLOOD = "flood";
 	public static final String STR_PORT_NORMAL = "normal";
 	public static final String STR_PORT_TABLE = "table";
+	public static final String STR_PORT_MAX = "max";
+	public static final String STR_PORT_ANY = "any";
+
 
 	public static final String STR_IN_PHYS_PORT = "in_phys_port";
 
@@ -611,16 +614,9 @@ public class MatchUtils {
 			switch (key_value[0]) {
 			case STR_IN_PORT:
 				if (dataMask.length == 1) {
-					if (dataMask[0].equals(STR_PORT_LOCAL)) {
-						mb.setExact(MatchField.IN_PORT, OFPort.LOCAL);
-					} else if (dataMask[0].equals(STR_PORT_CONTROLLER)) {
-						mb.setExact(MatchField.IN_PORT, OFPort.CONTROLLER);
-					} else {
-						mb.setExact(MatchField.IN_PORT, OFPort.of(U32.of(dataMask[0].contains("0x") ? Long.parseLong(dataMask[0].replaceFirst("0x", ""), 16) : Long.parseLong(dataMask[0])).getRaw()));
-					}
+				    mb.setExact(MatchField.IN_PORT, portFromString(dataMask[0]));
 				} else {
-					mb.setMasked(MatchField.IN_PORT, OFPort.of(U32.of(dataMask[0].contains("0x") ? Long.parseLong(dataMask[0].replaceFirst("0x", ""), 16) : Long.parseLong(dataMask[0])).getRaw()), 
-							OFPort.of(U32.of(dataMask[1].contains("0x") ? Long.parseLong(dataMask[1].replaceFirst("0x", ""), 16) : Long.parseLong(dataMask[1])).getRaw()));
+					mb.setMasked(MatchField.IN_PORT, portFromString(dataMask[0]), portFromString(dataMask[1]));
 				}
 				break;
 			case STR_DL_DST: /* Only accept hex-string for MAC addresses */
@@ -1027,10 +1023,9 @@ public class MatchUtils {
 					log.warn("Why are we here?");
 				}*/
 				if (dataMask.length == 1) {
-					mb.setExact(MatchField.ACTSET_OUTPUT, OFPort.of(U32.of(dataMask[0].contains("0x") ? Long.parseLong(dataMask[0].replaceFirst("0x", ""), 16) : Long.parseLong(dataMask[0])).getRaw()));
+					mb.setExact(MatchField.ACTSET_OUTPUT, portFromString(dataMask[0]));
 				} else {
-					mb.setMasked(MatchField.ACTSET_OUTPUT, OFPort.of(U32.of(dataMask[0].contains("0x") ? Long.parseLong(dataMask[0].replaceFirst("0x", ""), 16) : Long.parseLong(dataMask[0])).getRaw()), 
-							OFPort.of(U32.of(dataMask[1].contains("0x") ? Long.parseLong(dataMask[1].replaceFirst("0x", ""), 16) : Long.parseLong(dataMask[1])).getRaw()));
+					mb.setMasked(MatchField.ACTSET_OUTPUT, portFromString(dataMask[0]), portFromString(dataMask[1]));
 				}
 				break;
 			case STR_PACKET_TYPE:
@@ -1049,4 +1044,78 @@ public class MatchUtils {
 		}
 		return mb.build();
 	}
-}
+		
+	public static OFPort portFromString(String s) {
+        if (s == null) {
+            throw new IllegalArgumentException("Port string cannot be null");
+        }
+        
+        s = s.trim().toLowerCase();
+        switch (s) {
+        case MatchUtils.STR_PORT_ALL:
+            return OFPort.ALL;
+        case MatchUtils.STR_PORT_CONTROLLER:
+            return OFPort.CONTROLLER;
+        case MatchUtils.STR_PORT_FLOOD:
+            return OFPort.FLOOD;
+        case MatchUtils.STR_PORT_IN_PORT:
+            return OFPort.IN_PORT;
+        case MatchUtils.STR_PORT_LOCAL:
+            return OFPort.LOCAL;
+        case MatchUtils.STR_PORT_NORMAL:
+            return OFPort.NORMAL;
+        case MatchUtils.STR_PORT_TABLE:
+            return OFPort.TABLE;
+        case MatchUtils.STR_PORT_MAX:
+            return OFPort.MAX;
+        case MatchUtils.STR_PORT_ANY:
+            return OFPort.ANY;
+        default:
+            log.debug("Port {} was not a special port string. Parsing as raw int or hex", s);
+        }
+
+        try {
+            return OFPort.of(U32.of(s.contains("0x") ? 
+                    Long.parseLong(s.replaceFirst("0x", ""), 16) : 
+                        Long.parseLong(s)).getRaw());
+        } catch (NumberFormatException e) {
+            log.error("Could not parse port '{}'", s);
+            return null;
+        }
+    }
+	
+	public static String portToString(OFPort p) {
+        if (p == null) {
+            throw new IllegalArgumentException("Port cannot be null");
+        }
+        
+        if (p.equals(OFPort.ALL)) {
+            return STR_PORT_ALL;
+        } 
+        if (p.equals(OFPort.ANY)) {
+            return STR_PORT_ANY;
+        }
+        if (p.equals(OFPort.CONTROLLER)) {
+            return STR_PORT_CONTROLLER;
+        }
+        if (p.equals(OFPort.FLOOD)) {
+            return STR_PORT_FLOOD;
+        }
+        if (p.equals(OFPort.IN_PORT)) {
+            return STR_PORT_IN_PORT;
+        }
+        if (p.equals(OFPort.LOCAL)) {
+            return STR_PORT_LOCAL;
+        }
+        if (p.equals(OFPort.NORMAL)) {
+            return STR_PORT_NORMAL;
+        }
+        if (p.equals(OFPort.MAX)) {
+            return STR_PORT_MAX;
+        }
+        if (p.equals(OFPort.TABLE)) {
+            return STR_PORT_TABLE;
+        }
+        return Integer.toString(p.getPortNumber());
+    }
+}
\ No newline at end of file