Match field iteration
diff --git a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/protocol/match/Match.java b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/protocol/match/Match.java
index 5e68de5..0efdcbb 100644
--- a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/protocol/match/Match.java
+++ b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/protocol/match/Match.java
@@ -26,7 +26,7 @@
  * them in part. For example, OF1.0 supports exact match and (full) wildcarding for all fields, but it
  * does only supports partial masking for IP source/destination fields, and this partial masking must be
  * in the CIDR prefix format. Thus, OF1.0 implementation may throw <code>UnsupportedOperationException</code> if given
- * in <code>setMaksed</code> an IP mask of, for example, 255.0.255.0, or if <code>setMasked</code> is called for any field
+ * in <code>setMasked</code> an IP mask of, for example, 255.0.255.0, or if <code>setMasked</code> is called for any field
  * which is not IP source/destination address.
  * <br><br>
  * On prerequisites:<br>
@@ -37,7 +37,7 @@
  * be ignored unless the Ethertype is specified as MPLS. Likewise, the IP header and
  * transport header fields will be ignored unless the Ethertype is specified as either
  * IPv4 or ARP. The tp_src and tp_dst fields will be ignored unless the network protocol
- * specified is as TCP, UDP or SCTP. Fields that are ignored don�t need to be wildcarded
+ * specified is as TCP, UDP or SCTP. Fields that are ignored don't need to be wildcarded
  * and should be set to 0."
  * <br><br>
  * This interface uses generics to assure type safety in users code. However, implementing classes may have to suppress
@@ -124,6 +124,15 @@
     public boolean isPartiallyMasked(MatchField<?> field) throws UnsupportedOperationException;
 
     /**
+     * Get an Iterable over the match fields that have been specified for the
+     * match. This includes the match fields that are exact or masked match
+     * (but not fully wildcarded).
+     *
+     * @return
+     */
+    public Iterable<MatchField<?>> getMatchFields();
+
+    /**
      * Returns a builder to build new instances of this type of match object.
      * @return Match builder
      */
diff --git a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/OFMetadata.java b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/OFMetadata.java
index d1a23df..fcabdcd 100644
--- a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/OFMetadata.java
+++ b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/OFMetadata.java
@@ -27,6 +27,10 @@
         return new OFMetadata(U64.ofRaw(raw));
     }
 
+    public U64 getValue() {
+        return u64;
+    }
+
     public static OFMetadata read8Bytes(ChannelBuffer cb) {
         return OFMetadata.ofRaw(cb.readLong());
     }
diff --git a/java_gen/pre-written/src/test/java/org/projectfloodlight/protocol/match/MatchFieldIteration10Test.java b/java_gen/pre-written/src/test/java/org/projectfloodlight/protocol/match/MatchFieldIteration10Test.java
new file mode 100644
index 0000000..c6f4471
--- /dev/null
+++ b/java_gen/pre-written/src/test/java/org/projectfloodlight/protocol/match/MatchFieldIteration10Test.java
@@ -0,0 +1,10 @@
+package org.projectfloodlight.protocol.match;
+
+import org.projectfloodlight.openflow.protocol.OFFactories;
+import org.projectfloodlight.openflow.protocol.OFVersion;
+
+public class MatchFieldIteration10Test extends MatchFieldIterationBase {
+    public MatchFieldIteration10Test() {
+        super(OFFactories.getFactory(OFVersion.OF_10));
+    }
+}
diff --git a/java_gen/pre-written/src/test/java/org/projectfloodlight/protocol/match/MatchFieldIteration13Test.java b/java_gen/pre-written/src/test/java/org/projectfloodlight/protocol/match/MatchFieldIteration13Test.java
new file mode 100644
index 0000000..b654a53
--- /dev/null
+++ b/java_gen/pre-written/src/test/java/org/projectfloodlight/protocol/match/MatchFieldIteration13Test.java
@@ -0,0 +1,10 @@
+package org.projectfloodlight.protocol.match;
+
+import org.projectfloodlight.openflow.protocol.OFFactories;
+import org.projectfloodlight.openflow.protocol.OFVersion;
+
+public class MatchFieldIteration13Test extends MatchFieldIterationBase {
+    public MatchFieldIteration13Test() {
+        super(OFFactories.getFactory(OFVersion.OF_13));
+    }
+}
diff --git a/java_gen/pre-written/src/test/java/org/projectfloodlight/protocol/match/MatchFieldIterationBase.java b/java_gen/pre-written/src/test/java/org/projectfloodlight/protocol/match/MatchFieldIterationBase.java
new file mode 100644
index 0000000..9c72e37
--- /dev/null
+++ b/java_gen/pre-written/src/test/java/org/projectfloodlight/protocol/match/MatchFieldIterationBase.java
@@ -0,0 +1,249 @@
+package org.projectfloodlight.protocol.match;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+
+import java.util.Iterator;
+
+import org.junit.Test;
+import org.projectfloodlight.openflow.protocol.OFFactory;
+import org.projectfloodlight.openflow.protocol.OFVersion;
+import org.projectfloodlight.openflow.protocol.match.Match;
+import org.projectfloodlight.openflow.protocol.match.MatchField;
+import org.projectfloodlight.openflow.protocol.match.MatchFields;
+import org.projectfloodlight.openflow.types.ArpOpcode;
+import org.projectfloodlight.openflow.types.EthType;
+import org.projectfloodlight.openflow.types.IPv4Address;
+import org.projectfloodlight.openflow.types.IpProtocol;
+import org.projectfloodlight.openflow.types.MacAddress;
+import org.projectfloodlight.openflow.types.Masked;
+import org.projectfloodlight.openflow.types.OFPort;
+import org.projectfloodlight.openflow.types.TransportPort;
+
+import com.google.common.collect.Iterables;
+
+public class MatchFieldIterationBase {
+
+    private OFFactory factory;
+
+    protected MatchFieldIterationBase(OFFactory factory) {
+        this.factory = factory;
+    }
+    
+    @Test
+    public void iterateEmptyMatch() {
+        Match match = factory.buildMatch().build();
+        Iterator<MatchField<?>> iter = match.getMatchFields().iterator();
+        assertThat(iter.hasNext(), is(false));
+    }
+    
+    @Test
+    public void iterateSingleExactMatchField() {
+        OFPort port5 = OFPort.of(5);
+        Match match = factory.buildMatch()
+                .setExact(MatchField.IN_PORT, port5)
+                .build();
+        Iterator<MatchField<?>> iter = match.getMatchFields().iterator();
+        assertThat(iter.hasNext(), is(true));
+        MatchField<?> matchField = iter.next();
+        assertThat(matchField.id, is(MatchFields.IN_PORT));
+        assertThat(match.isExact(matchField), is(true));
+        @SuppressWarnings("unchecked")
+        MatchField<OFPort> portMatchField = (MatchField<OFPort>) matchField;
+        OFPort port = match.get(portMatchField);
+        assertThat(port, is(port5));
+        assertThat(iter.hasNext(), is(false));
+    }
+    
+    @SuppressWarnings("unchecked")
+    @Test
+    public void iterateExactMatchFields() {
+        OFPort port5 = OFPort.of(5);
+        MacAddress macSrc = MacAddress.of("00:01:02:03:04:05");
+        MacAddress macDst = MacAddress.of("01:01:02:02:03:03");
+        IPv4Address ipSrc = IPv4Address.of("10.192.20.1");
+        IPv4Address ipDst = IPv4Address.of("10.192.20.2");
+        TransportPort tcpSrc = TransportPort.of(100);
+        TransportPort tcpDst = TransportPort.of(200);
+        Match match = factory.buildMatch()
+                .setExact(MatchField.IN_PORT, port5)
+                .setExact(MatchField.ETH_TYPE, EthType.IPv4)
+                .setExact(MatchField.ETH_SRC, macSrc)
+                .setExact(MatchField.ETH_DST, macDst)
+                .setExact(MatchField.IP_PROTO, IpProtocol.TCP)
+                .setExact(MatchField.IPV4_SRC, ipSrc)
+                .setExact(MatchField.IPV4_DST, ipDst)
+                .setExact(MatchField.TCP_SRC, tcpSrc)
+                .setExact(MatchField.TCP_DST, tcpDst)
+                .build();
+        assertThat(Iterables.size(match.getMatchFields()), is(9));
+        for (MatchField<?> matchField: match.getMatchFields()) {
+            switch (matchField.id) {
+            case IN_PORT:
+                OFPort port = match.get((MatchField<OFPort>) matchField);
+                assertThat(port, is(port5));
+                break;
+            case ETH_TYPE:
+                EthType ethType = match.get((MatchField<EthType>) matchField);
+                assertThat(ethType, is(EthType.IPv4));
+                break;
+            case ETH_SRC:
+                MacAddress mac = match.get((MatchField<MacAddress>) matchField);
+                assertThat(mac, is(macSrc));
+                break;
+            case ETH_DST:
+                mac = match.get((MatchField<MacAddress>) matchField);
+                assertThat(mac, is(macDst));
+                break;
+            case IP_PROTO:
+                IpProtocol ipProtocol = match.get((MatchField<IpProtocol>) matchField);
+                assertThat(ipProtocol, is(IpProtocol.TCP));
+                break;
+            case IPV4_SRC:
+                IPv4Address ip = match.get((MatchField<IPv4Address>) matchField);
+                assertThat(ip, is(ipSrc));
+                break;
+            case IPV4_DST:
+                ip = match.get((MatchField<IPv4Address>) matchField);
+                assertThat(ip, is(ipDst));
+                break;
+            case TCP_SRC:
+                TransportPort tcp = match.get((MatchField<TransportPort>) matchField);
+                assertThat(tcp, is(tcpSrc));
+                break;
+            case TCP_DST:
+                tcp = match.get((MatchField<TransportPort>) matchField);
+                assertThat(tcp, is(tcpDst));
+                break;
+            default:
+                fail("Unexpected match field returned from iterator");
+            }
+        }
+    }
+    
+    @SuppressWarnings("unchecked")
+    @Test
+    public void iterateArpFields() {
+        MacAddress macSrc = MacAddress.of("00:01:02:03:04:05");
+        MacAddress macDst = MacAddress.of("01:01:02:02:03:03");
+        IPv4Address ipSrc = IPv4Address.of("10.192.20.1");
+        IPv4Address ipDst = IPv4Address.of("10.192.20.2");
+        OFVersion version = factory.getVersion();
+        boolean supportsArpHardwareAddress = (version != OFVersion.OF_10) &&
+                (version != OFVersion.OF_11) && (version != OFVersion.OF_12);
+        int matchFieldCount = 4;
+        Match.Builder builder = factory.buildMatch();
+        builder.setExact(MatchField.ETH_TYPE, EthType.ARP)
+                .setExact(MatchField.ARP_OP, ArpOpcode.REPLY)
+                .setExact(MatchField.ARP_SPA, ipSrc)
+                .setExact(MatchField.ARP_TPA, ipDst);
+        if (supportsArpHardwareAddress) {
+            builder.setExact(MatchField.ARP_SHA, macSrc);
+            builder.setExact(MatchField.ARP_THA, macDst);
+            matchFieldCount += 2;
+        }
+        Match match = builder.build();
+        assertThat(Iterables.size(match.getMatchFields()), is(matchFieldCount));
+        for (MatchField<?> matchField: match.getMatchFields()) {
+            switch (matchField.id) {
+            case ETH_TYPE:
+                EthType ethType = match.get((MatchField<EthType>) matchField);
+                assertThat(ethType, is(EthType.ARP));
+                break;
+            case ARP_OP:
+                ArpOpcode opcode = match.get((MatchField<ArpOpcode>) matchField);
+                assertThat(opcode, is(ArpOpcode.REPLY));
+                break;
+            case ARP_SHA:
+                MacAddress mac = match.get((MatchField<MacAddress>) matchField);
+                assertThat(mac, is(macSrc));
+                break;
+            case ARP_THA:
+                mac = match.get((MatchField<MacAddress>) matchField);
+                assertThat(mac, is(macDst));
+                break;
+            case ARP_SPA:
+                IPv4Address ip = match.get((MatchField<IPv4Address>) matchField);
+                assertThat(ip, is(ipSrc));
+                break;
+            case ARP_TPA:
+                ip = match.get((MatchField<IPv4Address>) matchField);
+                assertThat(ip, is(ipDst));
+                break;
+            default:
+                fail("Unexpected match field returned from iterator");
+            }
+        }
+    }
+    
+    @SuppressWarnings("unchecked")
+    @Test
+    public void iterateMaskedFields() {
+        MacAddress macSrc = MacAddress.of("01:02:03:04:00:00");
+        MacAddress macSrcMask = MacAddress.of("FF:FF:FF:FF:00:00");
+        MacAddress macDst = MacAddress.of("11:22:33:00:00:00");
+        MacAddress macDstMask = MacAddress.of("FF:FF:FF:00:00:00");
+        IPv4Address ipSrc = IPv4Address.of("10.192.20.0");
+        IPv4Address ipSrcMask = IPv4Address.of("255.255.255.0");
+        IPv4Address ipDst = IPv4Address.of("10.192.20.0");
+        IPv4Address ipDstMask = IPv4Address.of("255.255.255.128");
+        TransportPort tcpSrcMask = TransportPort.of(0x01F0);
+        OFVersion version = factory.getVersion();
+        boolean supportsAllMasks = (version != OFVersion.OF_10) &&
+                (version != OFVersion.OF_11) && (version != OFVersion.OF_12);
+        int matchFieldCount = 4;
+        Match.Builder builder = factory.buildMatch()
+                .setExact(MatchField.ETH_TYPE, EthType.IPv4)
+                .setMasked(MatchField.IPV4_SRC, ipSrc, ipSrcMask)
+                .setMasked(MatchField.IPV4_DST, ipDst, ipDstMask)
+                .setExact(MatchField.IP_PROTO, IpProtocol.TCP);
+        if (supportsAllMasks) {
+            builder.setMasked(MatchField.ETH_SRC, macSrc, macSrcMask);
+            builder.setMasked(MatchField.ETH_DST, macDst, macDstMask);
+            builder.setMasked(MatchField.TCP_SRC, tcpSrcMask, tcpSrcMask);
+            matchFieldCount += 3;
+        }
+        Match match = builder.build();
+        assertThat(Iterables.size(match.getMatchFields()), is(matchFieldCount));
+        for (MatchField<?> matchField: match.getMatchFields()) {
+            switch (matchField.id) {
+            case ETH_TYPE:
+                EthType ethType = match.get((MatchField<EthType>) matchField);
+                assertThat(ethType, is(EthType.IPv4));
+                break;
+            case ETH_SRC:
+                Masked<MacAddress> mac = match.getMasked((MatchField<MacAddress>) matchField);
+                assertThat(mac.getValue(), is(macSrc));
+                assertThat(mac.getMask(), is(macSrcMask));
+                break;
+            case ETH_DST:
+                mac = match.getMasked((MatchField<MacAddress>) matchField);
+                assertThat(mac.getValue(), is(macDst));
+                assertThat(mac.getMask(), is(macDstMask));
+                break;
+            case IP_PROTO:
+                IpProtocol ipProtocol = match.get((MatchField<IpProtocol>) matchField);
+                assertThat(ipProtocol, is(IpProtocol.TCP));
+                break;
+            case IPV4_SRC:
+                Masked<IPv4Address> ip = match.getMasked((MatchField<IPv4Address>) matchField);
+                assertThat(ip.getValue(), is(ipSrc));
+                assertThat(ip.getMask(), is(ipSrcMask));
+                break;
+            case IPV4_DST:
+                ip = match.getMasked((MatchField<IPv4Address>) matchField);
+                assertThat(ip.getValue(), is(ipDst));
+                assertThat(ip.getMask(), is(ipDstMask));
+                break;
+            case TCP_SRC:
+                Masked<TransportPort> tcp = match.getMasked((MatchField<TransportPort>) matchField);
+                assertThat(tcp.getValue(), is(tcpSrcMask));
+                assertThat(tcp.getMask(), is(tcpSrcMask));
+                break;
+            default:
+                fail("Unexpected match field returned from iterator");
+            }
+        }
+    }
+}
diff --git a/java_gen/templates/_imports.java b/java_gen/templates/_imports.java
index 2187bab..cf7334d 100644
--- a/java_gen/templates/_imports.java
+++ b/java_gen/templates/_imports.java
@@ -1,6 +1,7 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.EnumSet;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
 import java.util.Map;
@@ -21,5 +22,6 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Sets;
+import com.google.common.collect.UnmodifiableIterator;
 import com.google.common.hash.Funnel;
 import com.google.common.hash.PrimitiveSink;
diff --git a/java_gen/templates/custom/OFMatchV1Ver10.java b/java_gen/templates/custom/OFMatchV1Ver10.java
index 82ff10f..7b750bb 100644
--- a/java_gen/templates/custom/OFMatchV1Ver10.java
+++ b/java_gen/templates/custom/OFMatchV1Ver10.java
@@ -334,3 +334,77 @@
                 throw new UnsupportedOperationException("OFMatch does not support masked matching on field " + field.getName());
         }
     }
+
+    @Override
+    public Iterable<MatchField<?>> getMatchFields() {
+        ImmutableList.Builder<MatchField<?>> builder = ImmutableList.builder();
+        if ((wildcards & OFPFW_IN_PORT) == 0)
+            builder.add(MatchField.IN_PORT);
+        if ((wildcards & OFPFW_DL_VLAN) == 0)
+            builder.add(MatchField.VLAN_VID);
+        if ((wildcards & OFPFW_DL_SRC) == 0)
+            builder.add(MatchField.ETH_SRC);
+        if ((wildcards & OFPFW_DL_DST) == 0)
+            builder.add(MatchField.ETH_DST);
+        if ((wildcards & OFPFW_DL_TYPE) == 0)
+            builder.add(MatchField.ETH_TYPE);
+        if ((wildcards & OFPFW_NW_PROTO) == 0) {
+            if (ethType == EthType.ARP) {
+                builder.add(MatchField.ARP_OP);
+            } else if (ethType == EthType.IPv4) {
+                builder.add(MatchField.IP_PROTO);
+            } else {
+                throw new UnsupportedOperationException(
+                        "Unsupported Ethertype for matching on network protocol " + ethType);
+            }
+        }
+        if ((wildcards & OFPFW_TP_SRC) == 0) {
+            if (ipProto == IpProtocol.UDP) {
+                builder.add(MatchField.UDP_SRC);
+            } else if (ipProto == IpProtocol.TCP) {
+                builder.add(MatchField.TCP_SRC);
+            } else if (ipProto == IpProtocol.SCTP) {
+                builder.add(MatchField.SCTP_SRC);
+            } else {
+                throw new UnsupportedOperationException(
+                        "Unsupported IP protocol for matching on source port " + ipProto);
+            }
+        }
+        if ((wildcards & OFPFW_TP_DST) == 0) {
+            if (ipProto == IpProtocol.UDP) {
+                builder.add(MatchField.UDP_DST);
+            } else if (ipProto == IpProtocol.TCP) {
+                builder.add(MatchField.TCP_DST);
+            } else if (ipProto == IpProtocol.SCTP) {
+                builder.add(MatchField.SCTP_DST);
+            } else {
+                throw new UnsupportedOperationException(
+                        "Unsupported IP protocol for matching on destination port " + ipProto);
+            }
+        }
+        if (((wildcards & OFPFW_NW_SRC_MASK) >> OFPFW_NW_SRC_SHIFT) < 32) {
+            if (ethType == EthType.ARP) {
+                builder.add(MatchField.ARP_SPA);
+            } else if (ethType == EthType.IPv4) {
+                builder.add(MatchField.IPV4_SRC);
+            } else {
+                throw new UnsupportedOperationException(
+                        "Unsupported Ethertype for matching on source IP " + ethType);
+            }
+        }
+        if (((wildcards & OFPFW_NW_DST_MASK) >> OFPFW_NW_DST_SHIFT) < 32) {
+            if (ethType == EthType.ARP) {
+                builder.add(MatchField.ARP_TPA);
+            } else if (ethType == EthType.IPv4) {
+                builder.add(MatchField.IPV4_DST);
+            } else {
+                throw new UnsupportedOperationException(
+                        "Unsupported Ethertype for matching on destination IP " + ethType);
+            }
+        }
+        if ((wildcards & OFPFW_DL_VLAN_PCP) == 0)
+            builder.add(MatchField.VLAN_PCP);
+        if ((wildcards & OFPFW_NW_TOS) == 0)
+            builder.add(MatchField.IP_DSCP);
+        return builder.build();
+    }
diff --git a/java_gen/templates/custom/OFMatchV2Ver11.java b/java_gen/templates/custom/OFMatchV2Ver11.java
index ec7bfcc..ef79ffb 100644
--- a/java_gen/templates/custom/OFMatchV2Ver11.java
+++ b/java_gen/templates/custom/OFMatchV2Ver11.java
@@ -42,3 +42,8 @@
         // FIXME yotam - please replace with real implementation
         return false;
     }
+
+    @Override
+    public Iterable<MatchField<?>> getMatchFields() {
+        throw new UnsupportedOperationException();
+    }
diff --git a/java_gen/templates/custom/OFMatchV3Ver12.java b/java_gen/templates/custom/OFMatchV3Ver12.java
index a4cc51c..81092c1 100644
--- a/java_gen/templates/custom/OFMatchV3Ver12.java
+++ b/java_gen/templates/custom/OFMatchV3Ver12.java
@@ -106,4 +106,9 @@
         OFOxm<?> oxm = this.oxmList.get(field);
 
         return oxm != null && oxm.isMasked();
-    }
\ No newline at end of file
+    }
+
+    @Override
+    public Iterable<MatchField<?>> getMatchFields() {
+        throw new UnsupportedOperationException();
+    }
diff --git a/java_gen/templates/custom/OFMatchV3Ver13.java b/java_gen/templates/custom/OFMatchV3Ver13.java
index 8955e1e..9bfb234 100644
--- a/java_gen/templates/custom/OFMatchV3Ver13.java
+++ b/java_gen/templates/custom/OFMatchV3Ver13.java
@@ -108,3 +108,31 @@
 
         return oxm != null && oxm.isMasked();
     }
+
+    private class MatchFieldIterator extends UnmodifiableIterator<MatchField<?>> {
+        private Iterator<OFOxm<?>> oxmIterator;
+
+        MatchFieldIterator() {
+            oxmIterator = oxmList.iterator();
+        }
+
+        @Override
+        public boolean hasNext() {
+            return oxmIterator.hasNext();
+        }
+
+        @Override
+        public MatchField<?> next() {
+            OFOxm<?> next = oxmIterator.next();
+            return next.getMatchField();
+        }
+    }
+
+    @Override
+    public Iterable<MatchField<?>> getMatchFields() {
+        return new Iterable<MatchField<?>>() {
+            public Iterator<MatchField<?>> iterator() {
+                return new MatchFieldIterator();
+            }
+        };
+    }