// Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior University
// Copyright (c) 2011, 2012 Open Networking Foundation
// Copyright (c) 2012, 2013 Big Switch Networks, Inc.
// This library was generated by the LoxiGen Compiler.
// See the file LICENSE.txt which should have been included in the source distribution

// Automatically generated by LOXI from template of_class.java
// Do not modify

package org.projectfloodlight.openflow.protocol.ver10;

import org.projectfloodlight.openflow.protocol.*;
import org.projectfloodlight.openflow.protocol.action.*;
import org.projectfloodlight.openflow.protocol.actionid.*;
import org.projectfloodlight.openflow.protocol.bsntlv.*;
import org.projectfloodlight.openflow.protocol.errormsg.*;
import org.projectfloodlight.openflow.protocol.meterband.*;
import org.projectfloodlight.openflow.protocol.instruction.*;
import org.projectfloodlight.openflow.protocol.instructionid.*;
import org.projectfloodlight.openflow.protocol.match.*;
import org.projectfloodlight.openflow.protocol.oxm.*;
import org.projectfloodlight.openflow.protocol.queueprop.*;
import org.projectfloodlight.openflow.types.*;
import org.projectfloodlight.openflow.util.*;
import org.projectfloodlight.openflow.exceptions.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import com.google.common.collect.ImmutableList;
import java.util.Set;
import org.jboss.netty.buffer.ChannelBuffer;
import com.google.common.hash.PrimitiveSink;
import com.google.common.hash.Funnel;

class OFMatchV1Ver10 implements OFMatchV1 {
    private static final Logger logger = LoggerFactory.getLogger(OFMatchV1Ver10.class);
    // version: 1.0
    final static byte WIRE_VERSION = 1;
    final static int LENGTH = 40;

        private final static int DEFAULT_WILDCARDS = OFFlowWildcardsSerializerVer10.ALL_VAL;
        private final static OFPort DEFAULT_IN_PORT = OFPort.ZERO;
        private final static MacAddress DEFAULT_ETH_SRC = MacAddress.NONE;
        private final static MacAddress DEFAULT_ETH_DST = MacAddress.NONE;
        private final static OFVlanVidMatch DEFAULT_VLAN_VID = OFVlanVidMatch.NONE;
        private final static VlanPcp DEFAULT_VLAN_PCP = VlanPcp.NONE;
        private final static EthType DEFAULT_ETH_TYPE = EthType.NONE;
        private final static IpDscp DEFAULT_IP_DSCP = IpDscp.NONE;
        private final static IpProtocol DEFAULT_IP_PROTO = IpProtocol.NONE;
        private final static IPv4Address DEFAULT_IPV4_SRC = IPv4Address.NONE;
        private final static IPv4Address DEFAULT_IPV4_DST = IPv4Address.NONE;
        private final static TransportPort DEFAULT_TCP_SRC = TransportPort.NONE;
        private final static TransportPort DEFAULT_TCP_DST = TransportPort.NONE;

    // OF message fields
    private final int wildcards;
    private final OFPort inPort;
    private final MacAddress ethSrc;
    private final MacAddress ethDst;
    private final OFVlanVidMatch vlanVid;
    private final VlanPcp vlanPcp;
    private final EthType ethType;
    private final IpDscp ipDscp;
    private final IpProtocol ipProto;
    private final IPv4Address ipv4Src;
    private final IPv4Address ipv4Dst;
    private final TransportPort tcpSrc;
    private final TransportPort tcpDst;
//
    // Immutable default instance
    final static OFMatchV1Ver10 DEFAULT = new OFMatchV1Ver10(
        DEFAULT_WILDCARDS, DEFAULT_IN_PORT, DEFAULT_ETH_SRC, DEFAULT_ETH_DST, DEFAULT_VLAN_VID, DEFAULT_VLAN_PCP, DEFAULT_ETH_TYPE, DEFAULT_IP_DSCP, DEFAULT_IP_PROTO, DEFAULT_IPV4_SRC, DEFAULT_IPV4_DST, DEFAULT_TCP_SRC, DEFAULT_TCP_DST
    );

    // package private constructor - used by readers, builders, and factory
    OFMatchV1Ver10(int wildcards, OFPort inPort, MacAddress ethSrc, MacAddress ethDst, OFVlanVidMatch vlanVid, VlanPcp vlanPcp, EthType ethType, IpDscp ipDscp, IpProtocol ipProto, IPv4Address ipv4Src, IPv4Address ipv4Dst, TransportPort tcpSrc, TransportPort tcpDst) {
        this.wildcards = wildcards;
        this.inPort = inPort;
        this.ethSrc = ethSrc;
        this.ethDst = ethDst;
        this.vlanVid = vlanVid;
        this.vlanPcp = vlanPcp;
        this.ethType = ethType;
        this.ipDscp = ipDscp;
        this.ipProto = ipProto;
        this.ipv4Src = ipv4Src;
        this.ipv4Dst = ipv4Dst;
        this.tcpSrc = tcpSrc;
        this.tcpDst = tcpDst;
    }

    // Accessors for OF message fields
    @Override
    public int getWildcards() {
        return wildcards;
    }

    @Override
    public OFPort getInPort() {
        return inPort;
    }

    @Override
    public MacAddress getEthSrc() {
        return ethSrc;
    }

    @Override
    public MacAddress getEthDst() {
        return ethDst;
    }

    @Override
    public OFVlanVidMatch getVlanVid() {
        return vlanVid;
    }

    @Override
    public VlanPcp getVlanPcp() {
        return vlanPcp;
    }

    @Override
    public EthType getEthType() {
        return ethType;
    }

    @Override
    public IpDscp getIpDscp() {
        return ipDscp;
    }

    @Override
    public IpProtocol getIpProto() {
        return ipProto;
    }

    @Override
    public IPv4Address getIpv4Src() {
        return ipv4Src;
    }

    @Override
    public IPv4Address getIpv4Dst() {
        return ipv4Dst;
    }

    @Override
    public TransportPort getTcpSrc() {
        return tcpSrc;
    }

    @Override
    public TransportPort getTcpDst() {
        return tcpDst;
    }

    @Override
    public OFVersion getVersion() {
        return OFVersion.OF_10;
    }


    final public static int OFPFW_ALL = ((1 << 22) - 1);

    final public static int OFPFW_IN_PORT = 1 << 0; /* Switch input port. */
    final public static int OFPFW_DL_VLAN = 1 << 1; /* VLAN id. */
    final public static int OFPFW_DL_SRC = 1 << 2; /* Ethernet source address. */
    final public static int OFPFW_DL_DST = 1 << 3; /*
                                                    * Ethernet destination
                                                    * address.
                                                    */
    final public static int OFPFW_DL_TYPE = 1 << 4; /* Ethernet frame type. */
    final public static int OFPFW_NW_PROTO = 1 << 5; /* IP protocol. */
    final public static int OFPFW_TP_SRC = 1 << 6; /* TCP/UDP source port. */
    final public static int OFPFW_TP_DST = 1 << 7; /* TCP/UDP destination port. */

    /*
     * IP source address wildcard bit count. 0 is exact match, 1 ignores the
     * LSB, 2 ignores the 2 least-significant bits, ..., 32 and higher wildcard
     * the entire field. This is the *opposite* of the usual convention where
     * e.g. /24 indicates that 8 bits (not 24 bits) are wildcarded.
     */
    final public static int OFPFW_NW_SRC_SHIFT = 8;
    final public static int OFPFW_NW_SRC_BITS = 6;
    final public static int OFPFW_NW_SRC_MASK = ((1 << OFPFW_NW_SRC_BITS) - 1) << OFPFW_NW_SRC_SHIFT;
    final public static int OFPFW_NW_SRC_ALL = 32 << OFPFW_NW_SRC_SHIFT;

    /* IP destination address wildcard bit count. Same format as source. */
    final public static int OFPFW_NW_DST_SHIFT = 14;
    final public static int OFPFW_NW_DST_BITS = 6;
    final public static int OFPFW_NW_DST_MASK = ((1 << OFPFW_NW_DST_BITS) - 1) << OFPFW_NW_DST_SHIFT;
    final public static int OFPFW_NW_DST_ALL = 32 << OFPFW_NW_DST_SHIFT;

    final public static int OFPFW_DL_VLAN_PCP = 1 << 20; /* VLAN priority. */
    final public static int OFPFW_NW_TOS = 1 << 21; /* IP ToS (DSCP field, 6bits) */

    @SuppressWarnings("unchecked")
    @Override
    public <F extends OFValueType<F>> F get(MatchField<F> field)
            throws UnsupportedOperationException {
        if (isFullyWildcarded(field))
            return null;
        if (!field.arePrerequisitesOK(this))
            return null;

        Object result;
        switch (field.id) {
            case IN_PORT:
                result = inPort;
                break;
            case ETH_DST:
                result = ethDst;
                break;
            case ETH_SRC:
                result = ethSrc;
                break;
            case ETH_TYPE:
                result = ethType;
                break;
            case VLAN_VID:
                result = vlanVid;
                break;
            case VLAN_PCP:
                result = vlanPcp;
                break;
            case ARP_OP:
                result = ArpOpcode.of(ipProto.getIpProtocolNumber());
                break;
            case ARP_SPA:
                result = ipv4Src;
                break;
            case ARP_TPA:
                result = ipv4Dst;
                break;
            case IP_DSCP:
                result = ipDscp;
                break;
            case IP_PROTO:
                result = ipProto;
                break;
            case IPV4_SRC:
                result = ipv4Src;
                break;
            case IPV4_DST:
                result = ipv4Dst;
                break;
            case TCP_SRC:
                result = tcpSrc;
                break;
            case TCP_DST:
                result = tcpDst;
                break;
            case UDP_SRC:
                result = tcpSrc;
                break;
            case UDP_DST:
                result = tcpDst;
                break;
            case SCTP_SRC:
                result = tcpSrc;
                break;
            case SCTP_DST:
                result = tcpDst;
                break;
            case ICMPV4_TYPE:
                result = tcpSrc;
                break;
            case ICMPV4_CODE:
                result = tcpDst;
                break;
            // NOT SUPPORTED:
            default:
                throw new UnsupportedOperationException("OFMatch does not support matching on field " + field.getName());
        }
        return (F)result;
    }

    @SuppressWarnings("unchecked")
    @Override
    public <F extends OFValueType<F>> Masked<F> getMasked(MatchField<F> field)
            throws UnsupportedOperationException {
        if (!isPartiallyMasked(field))
            return null;
        if (!field.arePrerequisitesOK(this))
            return null;
        Object result;
        switch (field.id) {
            case ARP_SPA:
            case IPV4_SRC:
                int srcBitMask = (-1) << (32 - getIpv4SrcCidrMaskLen());
                result = IPv4AddressWithMask.of(ipv4Src, IPv4Address.of(srcBitMask));
                break;
            case ARP_TPA:
            case IPV4_DST:
                int dstBitMask = (-1) << (32 - getIpv4DstCidrMaskLen());

                result = IPv4AddressWithMask.of(ipv4Dst, IPv4Address.of(dstBitMask));
                break;
            default:
                throw new UnsupportedOperationException("OFMatch does not support masked matching on field " + field.getName());
        }
        return (Masked<F>)result;
    }

    @Override
    public boolean supports(MatchField<?> field) {
        switch (field.id) {
            case IN_PORT:
            case ETH_DST:
            case ETH_SRC:
            case ETH_TYPE:
            case VLAN_VID:
            case VLAN_PCP:
            case ARP_OP:
            case ARP_SPA:
            case ARP_TPA:
            case IP_DSCP:
            case IP_PROTO:
            case IPV4_SRC:
            case IPV4_DST:
            case TCP_SRC:
            case TCP_DST:
            case UDP_SRC:
            case UDP_DST:
            case SCTP_SRC:
            case SCTP_DST:
            case ICMPV4_TYPE:
            case ICMPV4_CODE:
                return true;
            default:
                return false;
        }
    }

    @Override
    public boolean supportsMasked(MatchField<?> field) {
        switch (field.id) {
            case ARP_SPA:
            case ARP_TPA:
            case IPV4_SRC:
            case IPV4_DST:
                return true;
            default:
                return false;
        }
    }

    @Override
    public boolean isExact(MatchField<?> field) {
        if (!field.arePrerequisitesOK(this))
            return false;

        switch (field.id) {
            case IN_PORT:
                return (this.wildcards & OFPFW_IN_PORT) == 0;
            case ETH_DST:
                return (this.wildcards & OFPFW_DL_DST) == 0;
            case ETH_SRC:
                return (this.wildcards & OFPFW_DL_SRC) == 0;
            case ETH_TYPE:
                return (this.wildcards & OFPFW_DL_TYPE) == 0;
            case VLAN_VID:
                return (this.wildcards & OFPFW_DL_VLAN) == 0;
            case VLAN_PCP:
                return (this.wildcards & OFPFW_DL_VLAN_PCP) == 0;
            case ARP_OP:
                return (this.wildcards & OFPFW_NW_PROTO) == 0;
            case ARP_SPA:
                return this.getIpv4SrcCidrMaskLen() >= 32;
            case ARP_TPA:
                return this.getIpv4DstCidrMaskLen() >= 32;
            case IP_DSCP:
                return (this.wildcards & OFPFW_NW_TOS) == 0;
            case IP_PROTO:
                return (this.wildcards & OFPFW_NW_PROTO) == 0;
            case IPV4_SRC:
                return this.getIpv4SrcCidrMaskLen() >= 32;
            case IPV4_DST:
                return this.getIpv4DstCidrMaskLen() >= 32;
            case TCP_SRC:
                return (this.wildcards & OFPFW_TP_SRC) == 0;
            case TCP_DST:
                return (this.wildcards & OFPFW_TP_DST) == 0;
            case UDP_SRC:
                return (this.wildcards & OFPFW_TP_SRC) == 0;
            case UDP_DST:
                return (this.wildcards & OFPFW_TP_DST) == 0;
            case SCTP_SRC:
                return (this.wildcards & OFPFW_TP_SRC) == 0;
            case SCTP_DST:
                return (this.wildcards & OFPFW_TP_DST) == 0;
            case ICMPV4_TYPE:
                return (this.wildcards & OFPFW_TP_SRC) == 0;
            case ICMPV4_CODE:
                return (this.wildcards & OFPFW_TP_DST) == 0;
            default:
                throw new UnsupportedOperationException("OFMatch does not support matching on field " + field.getName());
        }
    }

    /**
     * Parse this match's wildcard fields and return the number of significant
     * bits in the IP destination field. NOTE: this returns the number of bits
     * that are fixed, i.e., like CIDR, not the number of bits that are free
     * like OpenFlow encodes.
     *
     * @return A number between 0 (matches all IPs) and 32 (exact match)
     */
    public int getIpv4DstCidrMaskLen() {
        return Math.max(32 - ((wildcards & OFPFW_NW_DST_MASK) >> OFPFW_NW_DST_SHIFT),
                        0);
    }

    /**
     * Parse this match's wildcard fields and return the number of significant
     * bits in the IP destination field. NOTE: this returns the number of bits
     * that are fixed, i.e., like CIDR, not the number of bits that are free
     * like OpenFlow encodes.
     *
     * @return A number between 0 (matches all IPs) and 32 (exact match)
     */
    public int getIpv4SrcCidrMaskLen() {
        return Math.max(32 - ((wildcards & OFPFW_NW_SRC_MASK) >> OFPFW_NW_SRC_SHIFT),
                        0);
    }


    @Override
    public boolean isFullyWildcarded(MatchField<?> field) {
        if (!field.arePrerequisitesOK(this))
            return true;

        switch (field.id) {
            case IN_PORT:
                return (this.wildcards & OFPFW_IN_PORT) != 0;
            case ETH_DST:
                return (this.wildcards & OFPFW_DL_DST) != 0;
            case ETH_SRC:
                return (this.wildcards & OFPFW_DL_SRC) != 0;
            case ETH_TYPE:
                return (this.wildcards & OFPFW_DL_TYPE) != 0;
            case VLAN_VID:
                return (this.wildcards & OFPFW_DL_VLAN) != 0;
            case VLAN_PCP:
                return (this.wildcards & OFPFW_DL_VLAN_PCP) != 0;
            case ARP_OP:
                return (this.wildcards & OFPFW_NW_PROTO) != 0;
            case ARP_SPA:
                return this.getIpv4SrcCidrMaskLen() <= 0;
            case ARP_TPA:
                return this.getIpv4DstCidrMaskLen() <= 0;
            case IP_DSCP:
                return (this.wildcards & OFPFW_NW_TOS) != 0;
            case IP_PROTO:
                return (this.wildcards & OFPFW_NW_PROTO) != 0;
            case TCP_SRC:
                return (this.wildcards & OFPFW_TP_SRC) != 0;
            case TCP_DST:
                return (this.wildcards & OFPFW_TP_DST) != 0;
            case UDP_SRC:
                return (this.wildcards & OFPFW_TP_SRC) != 0;
            case UDP_DST:
                return (this.wildcards & OFPFW_TP_DST) != 0;
            case SCTP_SRC:
                return (this.wildcards & OFPFW_TP_SRC) != 0;
            case SCTP_DST:
                return (this.wildcards & OFPFW_TP_DST) != 0;
            case ICMPV4_TYPE:
                return (this.wildcards & OFPFW_TP_SRC) != 0;
            case ICMPV4_CODE:
                return (this.wildcards & OFPFW_TP_DST) != 0;
            case IPV4_SRC:
                return this.getIpv4SrcCidrMaskLen() <= 0;
            case IPV4_DST:
                return this.getIpv4DstCidrMaskLen() <= 0;
            default:
                throw new UnsupportedOperationException("OFMatch does not support matching on field " + field.getName());
        }
    }

    @Override
    public boolean isPartiallyMasked(MatchField<?> field) {
        if (!field.arePrerequisitesOK(this))
            return false;

        switch (field.id) {
            case ARP_SPA:
            case IPV4_SRC:
                int srcCidrLen = getIpv4SrcCidrMaskLen();
                return srcCidrLen > 0 && srcCidrLen < 32;
            case ARP_TPA:
            case IPV4_DST:
                int dstCidrLen = getIpv4DstCidrMaskLen();
                return dstCidrLen > 0 && dstCidrLen < 32;
            default:
                return false;
        }
    }

    @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();
    }

    public OFMatchV1.Builder createBuilder() {
        return new BuilderWithParent(this);
    }

    static class BuilderWithParent implements OFMatchV1.Builder {
        final OFMatchV1Ver10 parentMessage;

        // OF message fields
        private boolean wildcardsSet;
        private int wildcards;
        private boolean inPortSet;
        private OFPort inPort;
        private boolean ethSrcSet;
        private MacAddress ethSrc;
        private boolean ethDstSet;
        private MacAddress ethDst;
        private boolean vlanVidSet;
        private OFVlanVidMatch vlanVid;
        private boolean vlanPcpSet;
        private VlanPcp vlanPcp;
        private boolean ethTypeSet;
        private EthType ethType;
        private boolean ipDscpSet;
        private IpDscp ipDscp;
        private boolean ipProtoSet;
        private IpProtocol ipProto;
        private boolean ipv4SrcSet;
        private IPv4Address ipv4Src;
        private boolean ipv4DstSet;
        private IPv4Address ipv4Dst;
        private boolean tcpSrcSet;
        private TransportPort tcpSrc;
        private boolean tcpDstSet;
        private TransportPort tcpDst;

        BuilderWithParent(OFMatchV1Ver10 parentMessage) {
            this.parentMessage = parentMessage;
        }

    @Override
    public int getWildcards() {
        return wildcards;
    }

    @Override
    public OFMatchV1.Builder setWildcards(int wildcards) {
        this.wildcards = wildcards;
        this.wildcardsSet = true;
        return this;
    }
    @Override
    public OFPort getInPort() {
        return inPort;
    }

    @Override
    public OFMatchV1.Builder setInPort(OFPort inPort) {
        this.inPort = inPort;
        this.inPortSet = true;
        return this;
    }
    @Override
    public MacAddress getEthSrc() {
        return ethSrc;
    }

    @Override
    public OFMatchV1.Builder setEthSrc(MacAddress ethSrc) {
        this.ethSrc = ethSrc;
        this.ethSrcSet = true;
        return this;
    }
    @Override
    public MacAddress getEthDst() {
        return ethDst;
    }

    @Override
    public OFMatchV1.Builder setEthDst(MacAddress ethDst) {
        this.ethDst = ethDst;
        this.ethDstSet = true;
        return this;
    }
    @Override
    public OFVlanVidMatch getVlanVid() {
        return vlanVid;
    }

    @Override
    public OFMatchV1.Builder setVlanVid(OFVlanVidMatch vlanVid) {
        this.vlanVid = vlanVid;
        this.vlanVidSet = true;
        return this;
    }
    @Override
    public VlanPcp getVlanPcp() {
        return vlanPcp;
    }

    @Override
    public OFMatchV1.Builder setVlanPcp(VlanPcp vlanPcp) {
        this.vlanPcp = vlanPcp;
        this.vlanPcpSet = true;
        return this;
    }
    @Override
    public EthType getEthType() {
        return ethType;
    }

    @Override
    public OFMatchV1.Builder setEthType(EthType ethType) {
        this.ethType = ethType;
        this.ethTypeSet = true;
        return this;
    }
    @Override
    public IpDscp getIpDscp() {
        return ipDscp;
    }

    @Override
    public OFMatchV1.Builder setIpDscp(IpDscp ipDscp) {
        this.ipDscp = ipDscp;
        this.ipDscpSet = true;
        return this;
    }
    @Override
    public IpProtocol getIpProto() {
        return ipProto;
    }

    @Override
    public OFMatchV1.Builder setIpProto(IpProtocol ipProto) {
        this.ipProto = ipProto;
        this.ipProtoSet = true;
        return this;
    }
    @Override
    public IPv4Address getIpv4Src() {
        return ipv4Src;
    }

    @Override
    public OFMatchV1.Builder setIpv4Src(IPv4Address ipv4Src) {
        this.ipv4Src = ipv4Src;
        this.ipv4SrcSet = true;
        return this;
    }
    @Override
    public IPv4Address getIpv4Dst() {
        return ipv4Dst;
    }

    @Override
    public OFMatchV1.Builder setIpv4Dst(IPv4Address ipv4Dst) {
        this.ipv4Dst = ipv4Dst;
        this.ipv4DstSet = true;
        return this;
    }
    @Override
    public TransportPort getTcpSrc() {
        return tcpSrc;
    }

    @Override
    public OFMatchV1.Builder setTcpSrc(TransportPort tcpSrc) {
        this.tcpSrc = tcpSrc;
        this.tcpSrcSet = true;
        return this;
    }
    @Override
    public TransportPort getTcpDst() {
        return tcpDst;
    }

    @Override
    public OFMatchV1.Builder setTcpDst(TransportPort tcpDst) {
        this.tcpDst = tcpDst;
        this.tcpDstSet = true;
        return this;
    }
    @Override
    public OFVersion getVersion() {
        return OFVersion.OF_10;
    }



        @Override
        public OFMatchV1 build() {
                int wildcards = this.wildcardsSet ? this.wildcards : parentMessage.wildcards;
                OFPort inPort = this.inPortSet ? this.inPort : parentMessage.inPort;
                if(inPort == null)
                    throw new NullPointerException("Property inPort must not be null");
                MacAddress ethSrc = this.ethSrcSet ? this.ethSrc : parentMessage.ethSrc;
                if(ethSrc == null)
                    throw new NullPointerException("Property ethSrc must not be null");
                MacAddress ethDst = this.ethDstSet ? this.ethDst : parentMessage.ethDst;
                if(ethDst == null)
                    throw new NullPointerException("Property ethDst must not be null");
                OFVlanVidMatch vlanVid = this.vlanVidSet ? this.vlanVid : parentMessage.vlanVid;
                if(vlanVid == null)
                    throw new NullPointerException("Property vlanVid must not be null");
                VlanPcp vlanPcp = this.vlanPcpSet ? this.vlanPcp : parentMessage.vlanPcp;
                if(vlanPcp == null)
                    throw new NullPointerException("Property vlanPcp must not be null");
                EthType ethType = this.ethTypeSet ? this.ethType : parentMessage.ethType;
                if(ethType == null)
                    throw new NullPointerException("Property ethType must not be null");
                IpDscp ipDscp = this.ipDscpSet ? this.ipDscp : parentMessage.ipDscp;
                if(ipDscp == null)
                    throw new NullPointerException("Property ipDscp must not be null");
                IpProtocol ipProto = this.ipProtoSet ? this.ipProto : parentMessage.ipProto;
                if(ipProto == null)
                    throw new NullPointerException("Property ipProto must not be null");
                IPv4Address ipv4Src = this.ipv4SrcSet ? this.ipv4Src : parentMessage.ipv4Src;
                if(ipv4Src == null)
                    throw new NullPointerException("Property ipv4Src must not be null");
                IPv4Address ipv4Dst = this.ipv4DstSet ? this.ipv4Dst : parentMessage.ipv4Dst;
                if(ipv4Dst == null)
                    throw new NullPointerException("Property ipv4Dst must not be null");
                TransportPort tcpSrc = this.tcpSrcSet ? this.tcpSrc : parentMessage.tcpSrc;
                if(tcpSrc == null)
                    throw new NullPointerException("Property tcpSrc must not be null");
                TransportPort tcpDst = this.tcpDstSet ? this.tcpDst : parentMessage.tcpDst;
                if(tcpDst == null)
                    throw new NullPointerException("Property tcpDst must not be null");

                //
            // normalize match fields according to current OpenVSwitch behavior. When prerequisites for a field are not met
            // e.g., eth_type is not set to 0x800, OVS sets the value of corresponding ignored fields (e.g.,
            // ip_src, tcp_dst) to 0, and sets the wildcard bit to 1.
            if(ethType.equals(EthType.IPv4)) {
                // IP
                if(ipProto.equals(IpProtocol.TCP) || ipProto.equals(IpProtocol.UDP) || ipProto.equals(IpProtocol.ICMP)) {
                    // fully speced, wildcards and all values are fine
                    // normalize 32-63 ipv4 src 'mask' to a full bitmask
                    if((wildcards & OFPFW_NW_SRC_ALL) != 0)
                        wildcards |= OFPFW_NW_SRC_MASK;

                    // normalize 32-63 ipv4 dst 'mask' to a full bitmask
                    if((wildcards & OFPFW_NW_DST_ALL) != 0)
                        wildcards |= OFPFW_NW_DST_MASK;

                } else {
                    // normalize 32-63 ipv4 src 'mask' to a full bitmask
                    if((wildcards & OFPFW_NW_SRC_ALL) != 0)
                        wildcards |= OFPFW_NW_SRC_MASK;

                    // normalize 32-63 ipv4 dst 'mask' to a full bitmask
                    if((wildcards & OFPFW_NW_DST_ALL) != 0)
                        wildcards |= OFPFW_NW_DST_MASK;

                    // not TCP/UDP/ICMP -> Clear TP wildcards for the wire
                    wildcards |= (OFPFW_TP_SRC | OFPFW_TP_DST);
                    tcpSrc = TransportPort.NONE;
                    tcpDst = TransportPort.NONE;
                }
            } else if (ethType.equals(EthType.ARP)) {
                // normalize 32-63 ipv4 src 'mask' to a full bitmask
                if((wildcards & OFPFW_NW_SRC_ALL) != 0)
                    wildcards |= OFPFW_NW_SRC_MASK;

                // normalize 32-63 ipv4 dst 'mask' to a full bitmask
                if((wildcards & OFPFW_NW_DST_ALL) != 0)
                    wildcards |= OFPFW_NW_DST_MASK;

                // ARP: clear NW_TOS / TP wildcards for the wire
                wildcards |= ( OFPFW_NW_TOS | OFPFW_TP_SRC | OFPFW_TP_DST);
                ipDscp = IpDscp.NONE;
                tcpSrc = TransportPort.NONE;
                tcpDst = TransportPort.NONE;
            } else {
                // not even IP. Clear NW/TP wildcards for the wire
                wildcards |= ( OFPFW_NW_TOS | OFPFW_NW_PROTO | OFPFW_NW_SRC_MASK | OFPFW_NW_DST_MASK | OFPFW_TP_SRC | OFPFW_TP_DST);
                ipDscp = IpDscp.NONE;
                ipProto = IpProtocol.NONE;
                ipv4Src = IPv4Address.NONE;
                ipv4Dst = IPv4Address.NONE;
                tcpSrc = TransportPort.NONE;
                tcpDst = TransportPort.NONE;
            }
                return new OFMatchV1Ver10(
                    wildcards,
                    inPort,
                    ethSrc,
                    ethDst,
                    vlanVid,
                    vlanPcp,
                    ethType,
                    ipDscp,
                    ipProto,
                    ipv4Src,
                    ipv4Dst,
                    tcpSrc,
                    tcpDst
                );
        }
        @SuppressWarnings("unchecked")
        @Override
        public <F extends OFValueType<F>> F get(MatchField<F> field)
                throws UnsupportedOperationException {
            if (isFullyWildcarded(field))
                return null;

            Object result;
            switch (field.id) {
                case IN_PORT:
                    result = inPort;
                    break;
                case ETH_DST:
                    result = ethDst;
                    break;
                case ETH_SRC:
                    result = ethSrc;
                    break;
                case ETH_TYPE:
                    result = ethType;
                    break;
                case VLAN_VID:
                    result = vlanVid;
                    break;
                case VLAN_PCP:
                    result = vlanPcp;
                    break;
                case ARP_OP:
                    result = ArpOpcode.of(ipProto.getIpProtocolNumber());
                    break;
                case ARP_SPA:
                    result = ipv4Src;
                    break;
                case ARP_TPA:
                    result = ipv4Dst;
                    break;
                case IP_DSCP:
                    result = ipDscp;
                    break;
                case IP_PROTO:
                    result = ipProto;
                    break;
                case IPV4_SRC:
                    result = ipv4Src;
                    break;
                case IPV4_DST:
                    result = ipv4Dst;
                    break;
                case TCP_SRC:
                    result = tcpSrc;
                    break;
                case TCP_DST:
                    result = tcpDst;
                    break;
                case UDP_SRC:
                    result = tcpSrc;
                    break;
                case UDP_DST:
                    result = tcpDst;
                    break;
                case SCTP_SRC:
                    result = tcpSrc;
                    break;
                case SCTP_DST:
                    result = tcpDst;
                    break;
                case ICMPV4_TYPE:
                    result = tcpSrc;
                    break;
                case ICMPV4_CODE:
                    result = tcpDst;
                    break;
                // NOT SUPPORTED:
                default:
                    throw new UnsupportedOperationException("OFMatch does not support matching on field " + field.getName());
            }
            return (F)result;
        }

        @SuppressWarnings("unchecked")
        @Override
        public <F extends OFValueType<F>> Masked<F> getMasked(MatchField<F> field)
                throws UnsupportedOperationException {
            if (!isPartiallyMasked(field))
                return null;
            Object result;
            switch (field.id) {
                case IPV4_SRC:
                case ARP_SPA:
                    int srcBitMask = (-1) << (32 - getIpv4SrcCidrMaskLen());
                    result = IPv4AddressWithMask.of(ipv4Src, IPv4Address.of(srcBitMask));
                    break;
                case IPV4_DST:
                case ARP_TPA:
                    int dstMaskedBits = Math.min(32, (wildcards & OFPFW_NW_DST_MASK) >> OFPFW_NW_DST_SHIFT);
                    int dstBitMask = (-1) << (32 - getIpv4DstCidrMaskLen());

                    result = IPv4AddressWithMask.of(ipv4Dst, IPv4Address.of(dstBitMask));
                    break;
                default:
                    throw new UnsupportedOperationException("OFMatch does not support masked matching on field " + field.getName());
            }
            return (Masked<F>)result;
        }

        @Override
        public boolean supports(MatchField<?> field) {
            switch (field.id) {
                case IN_PORT:
                case ETH_DST:
                case ETH_SRC:
                case ETH_TYPE:
                case VLAN_VID:
                case VLAN_PCP:
                case ARP_OP:
                case ARP_SPA:
                case ARP_TPA:
                case IP_DSCP:
                case IP_PROTO:
                case IPV4_SRC:
                case IPV4_DST:
                case TCP_SRC:
                case TCP_DST:
                case UDP_SRC:
                case UDP_DST:
                case SCTP_SRC:
                case SCTP_DST:
                case ICMPV4_TYPE:
                case ICMPV4_CODE:
                    return true;
                default:
                    return false;
            }
        }

        @Override
        public boolean supportsMasked(MatchField<?> field) {
            switch (field.id) {
                case ARP_SPA:
                case ARP_TPA:
                case IPV4_SRC:
                case IPV4_DST:
                    return true;
                default:
                    return false;
            }
        }

        @Override
        public boolean isExact(MatchField<?> field) {
            switch (field.id) {
                case IN_PORT:
                    return (this.wildcards & OFPFW_IN_PORT) == 0;
                case ETH_DST:
                    return (this.wildcards & OFPFW_DL_DST) == 0;
                case ETH_SRC:
                    return (this.wildcards & OFPFW_DL_SRC) == 0;
                case ETH_TYPE:
                    return (this.wildcards & OFPFW_DL_TYPE) == 0;
                case VLAN_VID:
                    return (this.wildcards & OFPFW_DL_VLAN) == 0;
                case VLAN_PCP:
                    return (this.wildcards & OFPFW_DL_VLAN_PCP) == 0;
                case ARP_OP:
                    return (this.wildcards & OFPFW_NW_PROTO) == 0;
                case ARP_SPA:
                    return this.getIpv4SrcCidrMaskLen() >= 32;
                case ARP_TPA:
                    return this.getIpv4DstCidrMaskLen() >= 32;
                case IP_DSCP:
                    return (this.wildcards & OFPFW_NW_TOS) == 0;
                case IP_PROTO:
                    return (this.wildcards & OFPFW_NW_PROTO) == 0;
                case IPV4_SRC:
                    return this.getIpv4SrcCidrMaskLen() >= 32;
                case IPV4_DST:
                    return this.getIpv4DstCidrMaskLen() >= 32;
                case TCP_SRC:
                    return (this.wildcards & OFPFW_TP_SRC) == 0;
                case TCP_DST:
                    return (this.wildcards & OFPFW_TP_DST) == 0;
                case UDP_SRC:
                    return (this.wildcards & OFPFW_TP_SRC) == 0;
                case UDP_DST:
                    return (this.wildcards & OFPFW_TP_DST) == 0;
                case SCTP_SRC:
                    return (this.wildcards & OFPFW_TP_SRC) == 0;
                case SCTP_DST:
                    return (this.wildcards & OFPFW_TP_DST) == 0;
                case ICMPV4_TYPE:
                    return (this.wildcards & OFPFW_TP_SRC) == 0;
                case ICMPV4_CODE:
                    return (this.wildcards & OFPFW_TP_DST) == 0;
                default:
                    throw new UnsupportedOperationException("OFMatch does not support matching on field " + field.getName());
            }
        }

        /**
         * Parse this match's wildcard fields and return the number of significant
         * bits in the IP destination field. NOTE: this returns the number of bits
         * that are fixed, i.e., like CIDR, not the number of bits that are free
         * like OpenFlow encodes.
         *
         * @return A number between 0 (matches all IPs) and 32 (exact match)
         */
        public int getIpv4DstCidrMaskLen() {
            return Math.max(32 - ((wildcards & OFPFW_NW_DST_MASK) >> OFPFW_NW_DST_SHIFT),
                            0);
        }

        /**
         * Parse this match's wildcard fields and return the number of significant
         * bits in the IP destination field. NOTE: this returns the number of bits
         * that are fixed, i.e., like CIDR, not the number of bits that are free
         * like OpenFlow encodes.
         *
         * @return A number between 0 (matches all IPs) and 32 (exact match)
         */
        public int getIpv4SrcCidrMaskLen() {
            return Math.max(32 - ((wildcards & OFPFW_NW_SRC_MASK) >> OFPFW_NW_SRC_SHIFT),
                            0);
        }


        @Override
        public boolean isFullyWildcarded(MatchField<?> field) {
            switch (field.id) {
                case IN_PORT:
                    return (this.wildcards & OFPFW_IN_PORT) != 0;
                case ETH_DST:
                    return (this.wildcards & OFPFW_DL_DST) != 0;
                case ETH_SRC:
                    return (this.wildcards & OFPFW_DL_SRC) != 0;
                case ETH_TYPE:
                    return (this.wildcards & OFPFW_DL_TYPE) != 0;
                case VLAN_VID:
                    return (this.wildcards & OFPFW_DL_VLAN) != 0;
                case VLAN_PCP:
                    return (this.wildcards & OFPFW_DL_VLAN_PCP) != 0;
                case ARP_OP:
                    return (this.wildcards & OFPFW_NW_PROTO) != 0;
                case ARP_SPA:
                    return this.getIpv4SrcCidrMaskLen() <= 0;
                case ARP_TPA:
                    return this.getIpv4DstCidrMaskLen() <= 0;
                case IP_DSCP:
                    return (this.wildcards & OFPFW_NW_TOS) != 0;
                case IP_PROTO:
                    return (this.wildcards & OFPFW_NW_PROTO) != 0;
                case TCP_SRC:
                    return (this.wildcards & OFPFW_TP_SRC) != 0;
                case TCP_DST:
                    return (this.wildcards & OFPFW_TP_DST) != 0;
                case UDP_SRC:
                    return (this.wildcards & OFPFW_TP_SRC) != 0;
                case UDP_DST:
                    return (this.wildcards & OFPFW_TP_DST) != 0;
                case SCTP_SRC:
                    return (this.wildcards & OFPFW_TP_SRC) != 0;
                case SCTP_DST:
                    return (this.wildcards & OFPFW_TP_DST) != 0;
                case ICMPV4_TYPE:
                    return (this.wildcards & OFPFW_TP_SRC) != 0;
                case ICMPV4_CODE:
                    return (this.wildcards & OFPFW_TP_DST) != 0;
                case IPV4_SRC:
                    return this.getIpv4SrcCidrMaskLen() <= 0;
                case IPV4_DST:
                    return this.getIpv4DstCidrMaskLen() <= 0;
                default:
                    throw new UnsupportedOperationException("OFMatch does not support matching on field " + field.getName());
            }
        }

        @Override
        public boolean isPartiallyMasked(MatchField<?> field) {
            switch (field.id) {
                case ARP_SPA:
                case IPV4_SRC:
                    int srcCidrLen = getIpv4SrcCidrMaskLen();
                    return srcCidrLen > 0 && srcCidrLen < 32;
                case ARP_TPA:
                case IPV4_DST:
                    int dstCidrLen = getIpv4DstCidrMaskLen();
                    return dstCidrLen > 0 && dstCidrLen < 32;
                default:
                    throw new UnsupportedOperationException("OFMatch does not support masked matching on field " + field.getName());
            }
        }

        private final void initWildcards() {
            if(!wildcardsSet) {
                wildcards = parentMessage.wildcards;
                wildcardsSet = true;
            }
        }

        @Override
        public <F extends OFValueType<F>> Match.Builder setExact(MatchField<F> field,
                F value) {
            initWildcards();
            Object val = value;
            switch (field.id) {
                case ETH_DST:
                    setEthDst((MacAddress) value);
                    wildcards &= ~OFPFW_DL_DST;
                    break;
                case ETH_SRC:
                    setEthSrc((MacAddress) value);
                    wildcards &= ~OFPFW_DL_SRC;
                    break;
                case ETH_TYPE:
                    setEthType((EthType) value);
                    wildcards &= ~OFPFW_DL_TYPE;
                    break;
                case ICMPV4_CODE:
                    setTcpDst((TransportPort) value);
                    wildcards &= ~OFPFW_TP_DST;
                    break;
                case ICMPV4_TYPE:
                    setTcpSrc((TransportPort) value);
                    wildcards &= ~OFPFW_TP_SRC;
                    break;
                case IN_PORT:
                    setInPort((OFPort) value);
                    wildcards &= ~OFPFW_IN_PORT;
                    break;
                case ARP_OP:
                    setIpProto(IpProtocol.of((short)((ArpOpcode)value).getOpcode()));
                    wildcards &= ~OFPFW_NW_PROTO;
                    break;
                case ARP_TPA:
                case IPV4_DST:
                    setIpv4Dst((IPv4Address) value);
                    wildcards &= ~OFPFW_NW_DST_MASK;
                    break;
                case ARP_SPA:
                case IPV4_SRC:
                    setIpv4Src((IPv4Address) value);
                    wildcards &= ~OFPFW_NW_SRC_MASK;
                    break;
                case IP_DSCP:
                    setIpDscp((IpDscp) value);
                    wildcards &= ~OFPFW_NW_TOS;
                    break;
                case IP_PROTO:
                    setIpProto((IpProtocol) value);
                    wildcards &= ~OFPFW_NW_PROTO;
                    break;
                case SCTP_DST:
                    setTcpDst((TransportPort) value);
                    wildcards &= ~OFPFW_TP_DST;
                    break;
                case SCTP_SRC:
                    setTcpSrc((TransportPort) value);
                    wildcards &= ~OFPFW_TP_SRC;
                    break;
                case TCP_DST:
                    setTcpDst((TransportPort) value);
                    wildcards &= ~OFPFW_TP_DST;
                    break;
                case TCP_SRC:
                    setTcpSrc((TransportPort) value);
                    wildcards &= ~OFPFW_TP_SRC;
                    break;
                case UDP_DST:
                    setTcpDst((TransportPort) value);
                    wildcards &= ~OFPFW_TP_DST;
                    break;
                case UDP_SRC:
                    setTcpSrc((TransportPort) value);
                    wildcards &= ~OFPFW_TP_SRC;
                    break;
                case VLAN_PCP:
                    setVlanPcp((VlanPcp) value);
                    wildcards &= ~OFPFW_DL_VLAN_PCP;
                    break;
                case VLAN_VID:
                    setVlanVid((OFVlanVidMatch) value);
                    wildcards &= ~OFPFW_DL_VLAN;
                    break;
                default:
                    throw new UnsupportedOperationException(
                            "OFMatch does not support matching on field " + field.getName());
            }
            return this;
        }

        @Override
        public <F extends OFValueType<F>> Match.Builder setMasked(MatchField<F> field,
                F value, F mask) {
            initWildcards();
            switch (field.id) {
                case ARP_SPA:
                case ARP_TPA:
                case IPV4_DST:
                case IPV4_SRC:
                    Object valObj = value;
                    Object masObj = mask;
                    IPv4Address ip = ((IPv4Address)valObj);
                    int maskval = ((IPv4Address)masObj).getInt();
                    if (Integer.bitCount(~maskval + 1) != 1)
                        throw new UnsupportedOperationException("OFMatch only supports CIDR masks for IPv4");
                    int maskLen = 32 - Integer.bitCount(maskval);
                    switch(field.id) {
                        case ARP_TPA:
                        case IPV4_DST:
                            setIpv4Dst(ip);
                            wildcards = (wildcards &~OFPFW_NW_DST_MASK) | (maskLen << OFPFW_NW_DST_SHIFT);
                            break;
                        case ARP_SPA:
                        case IPV4_SRC:
                            setIpv4Src(ip);
                            wildcards = (wildcards &~OFPFW_NW_SRC_MASK) | (maskLen << OFPFW_NW_SRC_SHIFT);
                            break;
                        default:
                            // Cannot really get here
                            break;
                    }
                    break;
                default:
                    throw new UnsupportedOperationException("OFMatch does not support masked matching on field " + field.getName());
            }
            return this;
        }

        @Override
        public <F extends OFValueType<F>> Match.Builder setMasked(MatchField<F> field, Masked<F> valueWithMask)
                                                                       throws UnsupportedOperationException {
            return this.setMasked(field, valueWithMask.getValue(), valueWithMask.getMask());
        }

        @Override
        public <F extends OFValueType<F>> Match.Builder wildcard(MatchField<F> field) {
            initWildcards();
            switch (field.id) {
                case ETH_DST:
                    setEthDst(MacAddress.NONE);
                    wildcards |= OFPFW_DL_DST;
                    break;
                case ETH_SRC:
                    setEthSrc(MacAddress.NONE);
                    wildcards |= OFPFW_DL_SRC;
                    break;
                case ETH_TYPE:
                    setEthType(EthType.NONE);
                    wildcards |= OFPFW_DL_TYPE;
                    break;
                case ICMPV4_CODE:
                case TCP_DST:
                case UDP_DST:
                case SCTP_DST:
                    setTcpDst(TransportPort.NONE);
                    wildcards |= OFPFW_TP_DST;
                    break;
                case ICMPV4_TYPE:
                case TCP_SRC:
                case UDP_SRC:
                case SCTP_SRC:
                    setTcpSrc(TransportPort.NONE);
                    wildcards |= OFPFW_TP_SRC;
                    break;
                case IN_PORT:
                    setInPort(OFPort.of(0)); // NOTE: not 'NONE' -- that is 0xFF for ports
                    wildcards |= OFPFW_IN_PORT;
                    break;
                case ARP_TPA:
                case IPV4_DST:
                    setIpv4Dst(IPv4Address.NONE);
                    wildcards |= OFPFW_NW_DST_MASK;
                    break;
                case ARP_SPA:
                case IPV4_SRC:
                    setIpv4Src(IPv4Address.NONE);
                    wildcards |= OFPFW_NW_SRC_MASK;
                    break;
                case IP_DSCP:
                    setIpDscp(IpDscp.NONE);
                    wildcards |= OFPFW_NW_TOS;
                    break;
                case IP_PROTO:
                    setIpProto(IpProtocol.NONE);
                    wildcards |= OFPFW_NW_PROTO;
                    break;
                case VLAN_PCP:
                    setVlanPcp(VlanPcp.NONE);
                    wildcards |= OFPFW_DL_VLAN_PCP;
                    break;
                case VLAN_VID:
                    setVlanVid(OFVlanVidMatch.NONE);
                    wildcards |= OFPFW_DL_VLAN;
                    break;
                default:
                    throw new UnsupportedOperationException("OFMatch does not support matching on field " + field.getName());
            }
            return this;
        }

    }

    static class Builder implements OFMatchV1.Builder {
        // OF message fields
        private boolean wildcardsSet;
        private int wildcards;
        private boolean inPortSet;
        private OFPort inPort;
        private boolean ethSrcSet;
        private MacAddress ethSrc;
        private boolean ethDstSet;
        private MacAddress ethDst;
        private boolean vlanVidSet;
        private OFVlanVidMatch vlanVid;
        private boolean vlanPcpSet;
        private VlanPcp vlanPcp;
        private boolean ethTypeSet;
        private EthType ethType;
        private boolean ipDscpSet;
        private IpDscp ipDscp;
        private boolean ipProtoSet;
        private IpProtocol ipProto;
        private boolean ipv4SrcSet;
        private IPv4Address ipv4Src;
        private boolean ipv4DstSet;
        private IPv4Address ipv4Dst;
        private boolean tcpSrcSet;
        private TransportPort tcpSrc;
        private boolean tcpDstSet;
        private TransportPort tcpDst;

    @Override
    public int getWildcards() {
        return wildcards;
    }

    @Override
    public OFMatchV1.Builder setWildcards(int wildcards) {
        this.wildcards = wildcards;
        this.wildcardsSet = true;
        return this;
    }
    @Override
    public OFPort getInPort() {
        return inPort;
    }

    @Override
    public OFMatchV1.Builder setInPort(OFPort inPort) {
        this.inPort = inPort;
        this.inPortSet = true;
        return this;
    }
    @Override
    public MacAddress getEthSrc() {
        return ethSrc;
    }

    @Override
    public OFMatchV1.Builder setEthSrc(MacAddress ethSrc) {
        this.ethSrc = ethSrc;
        this.ethSrcSet = true;
        return this;
    }
    @Override
    public MacAddress getEthDst() {
        return ethDst;
    }

    @Override
    public OFMatchV1.Builder setEthDst(MacAddress ethDst) {
        this.ethDst = ethDst;
        this.ethDstSet = true;
        return this;
    }
    @Override
    public OFVlanVidMatch getVlanVid() {
        return vlanVid;
    }

    @Override
    public OFMatchV1.Builder setVlanVid(OFVlanVidMatch vlanVid) {
        this.vlanVid = vlanVid;
        this.vlanVidSet = true;
        return this;
    }
    @Override
    public VlanPcp getVlanPcp() {
        return vlanPcp;
    }

    @Override
    public OFMatchV1.Builder setVlanPcp(VlanPcp vlanPcp) {
        this.vlanPcp = vlanPcp;
        this.vlanPcpSet = true;
        return this;
    }
    @Override
    public EthType getEthType() {
        return ethType;
    }

    @Override
    public OFMatchV1.Builder setEthType(EthType ethType) {
        this.ethType = ethType;
        this.ethTypeSet = true;
        return this;
    }
    @Override
    public IpDscp getIpDscp() {
        return ipDscp;
    }

    @Override
    public OFMatchV1.Builder setIpDscp(IpDscp ipDscp) {
        this.ipDscp = ipDscp;
        this.ipDscpSet = true;
        return this;
    }
    @Override
    public IpProtocol getIpProto() {
        return ipProto;
    }

    @Override
    public OFMatchV1.Builder setIpProto(IpProtocol ipProto) {
        this.ipProto = ipProto;
        this.ipProtoSet = true;
        return this;
    }
    @Override
    public IPv4Address getIpv4Src() {
        return ipv4Src;
    }

    @Override
    public OFMatchV1.Builder setIpv4Src(IPv4Address ipv4Src) {
        this.ipv4Src = ipv4Src;
        this.ipv4SrcSet = true;
        return this;
    }
    @Override
    public IPv4Address getIpv4Dst() {
        return ipv4Dst;
    }

    @Override
    public OFMatchV1.Builder setIpv4Dst(IPv4Address ipv4Dst) {
        this.ipv4Dst = ipv4Dst;
        this.ipv4DstSet = true;
        return this;
    }
    @Override
    public TransportPort getTcpSrc() {
        return tcpSrc;
    }

    @Override
    public OFMatchV1.Builder setTcpSrc(TransportPort tcpSrc) {
        this.tcpSrc = tcpSrc;
        this.tcpSrcSet = true;
        return this;
    }
    @Override
    public TransportPort getTcpDst() {
        return tcpDst;
    }

    @Override
    public OFMatchV1.Builder setTcpDst(TransportPort tcpDst) {
        this.tcpDst = tcpDst;
        this.tcpDstSet = true;
        return this;
    }
    @Override
    public OFVersion getVersion() {
        return OFVersion.OF_10;
    }

//
        @Override
        public OFMatchV1 build() {
            int wildcards = this.wildcardsSet ? this.wildcards : DEFAULT_WILDCARDS;
            OFPort inPort = this.inPortSet ? this.inPort : DEFAULT_IN_PORT;
            if(inPort == null)
                throw new NullPointerException("Property inPort must not be null");
            MacAddress ethSrc = this.ethSrcSet ? this.ethSrc : DEFAULT_ETH_SRC;
            if(ethSrc == null)
                throw new NullPointerException("Property ethSrc must not be null");
            MacAddress ethDst = this.ethDstSet ? this.ethDst : DEFAULT_ETH_DST;
            if(ethDst == null)
                throw new NullPointerException("Property ethDst must not be null");
            OFVlanVidMatch vlanVid = this.vlanVidSet ? this.vlanVid : DEFAULT_VLAN_VID;
            if(vlanVid == null)
                throw new NullPointerException("Property vlanVid must not be null");
            VlanPcp vlanPcp = this.vlanPcpSet ? this.vlanPcp : DEFAULT_VLAN_PCP;
            if(vlanPcp == null)
                throw new NullPointerException("Property vlanPcp must not be null");
            EthType ethType = this.ethTypeSet ? this.ethType : DEFAULT_ETH_TYPE;
            if(ethType == null)
                throw new NullPointerException("Property ethType must not be null");
            IpDscp ipDscp = this.ipDscpSet ? this.ipDscp : DEFAULT_IP_DSCP;
            if(ipDscp == null)
                throw new NullPointerException("Property ipDscp must not be null");
            IpProtocol ipProto = this.ipProtoSet ? this.ipProto : DEFAULT_IP_PROTO;
            if(ipProto == null)
                throw new NullPointerException("Property ipProto must not be null");
            IPv4Address ipv4Src = this.ipv4SrcSet ? this.ipv4Src : DEFAULT_IPV4_SRC;
            if(ipv4Src == null)
                throw new NullPointerException("Property ipv4Src must not be null");
            IPv4Address ipv4Dst = this.ipv4DstSet ? this.ipv4Dst : DEFAULT_IPV4_DST;
            if(ipv4Dst == null)
                throw new NullPointerException("Property ipv4Dst must not be null");
            TransportPort tcpSrc = this.tcpSrcSet ? this.tcpSrc : DEFAULT_TCP_SRC;
            if(tcpSrc == null)
                throw new NullPointerException("Property tcpSrc must not be null");
            TransportPort tcpDst = this.tcpDstSet ? this.tcpDst : DEFAULT_TCP_DST;
            if(tcpDst == null)
                throw new NullPointerException("Property tcpDst must not be null");

            // normalize match fields according to current OpenVSwitch behavior. When prerequisites for a field are not met
            // e.g., eth_type is not set to 0x800, OVS sets the value of corresponding ignored fields (e.g.,
            // ip_src, tcp_dst) to 0, and sets the wildcard bit to 1.
            if(ethType.equals(EthType.IPv4)) {
                // IP
                if(ipProto.equals(IpProtocol.TCP) || ipProto.equals(IpProtocol.UDP) || ipProto.equals(IpProtocol.ICMP)) {
                    // fully speced, wildcards and all values are fine
                    // normalize 32-63 ipv4 src 'mask' to a full bitmask
                    if((wildcards & OFPFW_NW_SRC_ALL) != 0)
                        wildcards |= OFPFW_NW_SRC_MASK;

                    // normalize 32-63 ipv4 dst 'mask' to a full bitmask
                    if((wildcards & OFPFW_NW_DST_ALL) != 0)
                        wildcards |= OFPFW_NW_DST_MASK;

                } else {
                    // normalize 32-63 ipv4 src 'mask' to a full bitmask
                    if((wildcards & OFPFW_NW_SRC_ALL) != 0)
                        wildcards |= OFPFW_NW_SRC_MASK;

                    // normalize 32-63 ipv4 dst 'mask' to a full bitmask
                    if((wildcards & OFPFW_NW_DST_ALL) != 0)
                        wildcards |= OFPFW_NW_DST_MASK;

                    // not TCP/UDP/ICMP -> Clear TP wildcards for the wire
                    wildcards |= (OFPFW_TP_SRC | OFPFW_TP_DST);
                    tcpSrc = TransportPort.NONE;
                    tcpDst = TransportPort.NONE;
                }
            } else if (ethType.equals(EthType.ARP)) {
                // normalize 32-63 ipv4 src 'mask' to a full bitmask
                if((wildcards & OFPFW_NW_SRC_ALL) != 0)
                    wildcards |= OFPFW_NW_SRC_MASK;

                // normalize 32-63 ipv4 dst 'mask' to a full bitmask
                if((wildcards & OFPFW_NW_DST_ALL) != 0)
                    wildcards |= OFPFW_NW_DST_MASK;

                // ARP: clear NW_TOS / TP wildcards for the wire
                wildcards |= ( OFPFW_NW_TOS | OFPFW_TP_SRC | OFPFW_TP_DST);
                ipDscp = IpDscp.NONE;
                tcpSrc = TransportPort.NONE;
                tcpDst = TransportPort.NONE;
            } else {
                // not even IP. Clear NW/TP wildcards for the wire
                wildcards |= ( OFPFW_NW_TOS | OFPFW_NW_PROTO | OFPFW_NW_SRC_MASK | OFPFW_NW_DST_MASK | OFPFW_TP_SRC | OFPFW_TP_DST);
                ipDscp = IpDscp.NONE;
                ipProto = IpProtocol.NONE;
                ipv4Src = IPv4Address.NONE;
                ipv4Dst = IPv4Address.NONE;
                tcpSrc = TransportPort.NONE;
                tcpDst = TransportPort.NONE;
            }

            return new OFMatchV1Ver10(
                    wildcards,
                    inPort,
                    ethSrc,
                    ethDst,
                    vlanVid,
                    vlanPcp,
                    ethType,
                    ipDscp,
                    ipProto,
                    ipv4Src,
                    ipv4Dst,
                    tcpSrc,
                    tcpDst
                );
        }
        @SuppressWarnings("unchecked")
        @Override
        public <F extends OFValueType<F>> F get(MatchField<F> field)
                throws UnsupportedOperationException {
            if (isFullyWildcarded(field))
                return null;

            Object result;
            switch (field.id) {
                case IN_PORT:
                    result = inPort;
                    break;
                case ETH_DST:
                    result = ethDst;
                    break;
                case ETH_SRC:
                    result = ethSrc;
                    break;
                case ETH_TYPE:
                    result = ethType;
                    break;
                case VLAN_VID:
                    result = vlanVid;
                    break;
                case VLAN_PCP:
                    result = vlanPcp;
                    break;
                case ARP_OP:
                    result = ArpOpcode.of(ipProto.getIpProtocolNumber());
                    break;
                case ARP_SPA:
                    result = ipv4Src;
                    break;
                case ARP_TPA:
                    result = ipv4Dst;
                    break;
                case IP_DSCP:
                    result = ipDscp;
                    break;
                case IP_PROTO:
                    result = ipProto;
                    break;
                case IPV4_SRC:
                    result = ipv4Src;
                    break;
                case IPV4_DST:
                    result = ipv4Dst;
                    break;
                case TCP_SRC:
                    result = tcpSrc;
                    break;
                case TCP_DST:
                    result = tcpDst;
                    break;
                case UDP_SRC:
                    result = tcpSrc;
                    break;
                case UDP_DST:
                    result = tcpDst;
                    break;
                case SCTP_SRC:
                    result = tcpSrc;
                    break;
                case SCTP_DST:
                    result = tcpDst;
                    break;
                case ICMPV4_TYPE:
                    result = tcpSrc;
                    break;
                case ICMPV4_CODE:
                    result = tcpDst;
                    break;
                // NOT SUPPORTED:
                default:
                    throw new UnsupportedOperationException("OFMatch does not support matching on field " + field.getName());
            }
            return (F)result;
        }

        @SuppressWarnings("unchecked")
        @Override
        public <F extends OFValueType<F>> Masked<F> getMasked(MatchField<F> field)
                throws UnsupportedOperationException {
            if (!isPartiallyMasked(field))
                return null;
            Object result;
            switch (field.id) {
                case IPV4_SRC:
                case ARP_SPA:
                    int srcBitMask = (-1) << (32 - getIpv4SrcCidrMaskLen());
                    result = IPv4AddressWithMask.of(ipv4Src, IPv4Address.of(srcBitMask));
                    break;
                case IPV4_DST:
                case ARP_TPA:
                    int dstMaskedBits = Math.min(32, (wildcards & OFPFW_NW_DST_MASK) >> OFPFW_NW_DST_SHIFT);
                    int dstBitMask = (-1) << (32 - getIpv4DstCidrMaskLen());

                    result = IPv4AddressWithMask.of(ipv4Dst, IPv4Address.of(dstBitMask));
                    break;
                default:
                    throw new UnsupportedOperationException("OFMatch does not support masked matching on field " + field.getName());
            }
            return (Masked<F>)result;
        }

        @Override
        public boolean supports(MatchField<?> field) {
            switch (field.id) {
                case IN_PORT:
                case ETH_DST:
                case ETH_SRC:
                case ETH_TYPE:
                case VLAN_VID:
                case VLAN_PCP:
                case ARP_OP:
                case ARP_SPA:
                case ARP_TPA:
                case IP_DSCP:
                case IP_PROTO:
                case IPV4_SRC:
                case IPV4_DST:
                case TCP_SRC:
                case TCP_DST:
                case UDP_SRC:
                case UDP_DST:
                case SCTP_SRC:
                case SCTP_DST:
                case ICMPV4_TYPE:
                case ICMPV4_CODE:
                    return true;
                default:
                    return false;
            }
        }

        @Override
        public boolean supportsMasked(MatchField<?> field) {
            switch (field.id) {
                case ARP_SPA:
                case ARP_TPA:
                case IPV4_SRC:
                case IPV4_DST:
                    return true;
                default:
                    return false;
            }
        }

        @Override
        public boolean isExact(MatchField<?> field) {
            switch (field.id) {
                case IN_PORT:
                    return (this.wildcards & OFPFW_IN_PORT) == 0;
                case ETH_DST:
                    return (this.wildcards & OFPFW_DL_DST) == 0;
                case ETH_SRC:
                    return (this.wildcards & OFPFW_DL_SRC) == 0;
                case ETH_TYPE:
                    return (this.wildcards & OFPFW_DL_TYPE) == 0;
                case VLAN_VID:
                    return (this.wildcards & OFPFW_DL_VLAN) == 0;
                case VLAN_PCP:
                    return (this.wildcards & OFPFW_DL_VLAN_PCP) == 0;
                case ARP_OP:
                    return (this.wildcards & OFPFW_NW_PROTO) == 0;
                case ARP_SPA:
                    return this.getIpv4SrcCidrMaskLen() >= 32;
                case ARP_TPA:
                    return this.getIpv4DstCidrMaskLen() >= 32;
                case IP_DSCP:
                    return (this.wildcards & OFPFW_NW_TOS) == 0;
                case IP_PROTO:
                    return (this.wildcards & OFPFW_NW_PROTO) == 0;
                case IPV4_SRC:
                    return this.getIpv4SrcCidrMaskLen() >= 32;
                case IPV4_DST:
                    return this.getIpv4DstCidrMaskLen() >= 32;
                case TCP_SRC:
                    return (this.wildcards & OFPFW_TP_SRC) == 0;
                case TCP_DST:
                    return (this.wildcards & OFPFW_TP_DST) == 0;
                case UDP_SRC:
                    return (this.wildcards & OFPFW_TP_SRC) == 0;
                case UDP_DST:
                    return (this.wildcards & OFPFW_TP_DST) == 0;
                case SCTP_SRC:
                    return (this.wildcards & OFPFW_TP_SRC) == 0;
                case SCTP_DST:
                    return (this.wildcards & OFPFW_TP_DST) == 0;
                case ICMPV4_TYPE:
                    return (this.wildcards & OFPFW_TP_SRC) == 0;
                case ICMPV4_CODE:
                    return (this.wildcards & OFPFW_TP_DST) == 0;
                default:
                    throw new UnsupportedOperationException("OFMatch does not support matching on field " + field.getName());
            }
        }

        /**
         * Parse this match's wildcard fields and return the number of significant
         * bits in the IP destination field. NOTE: this returns the number of bits
         * that are fixed, i.e., like CIDR, not the number of bits that are free
         * like OpenFlow encodes.
         *
         * @return A number between 0 (matches all IPs) and 32 (exact match)
         */
        public int getIpv4DstCidrMaskLen() {
            return Math.max(32 - ((wildcards & OFPFW_NW_DST_MASK) >> OFPFW_NW_DST_SHIFT),
                            0);
        }

        /**
         * Parse this match's wildcard fields and return the number of significant
         * bits in the IP destination field. NOTE: this returns the number of bits
         * that are fixed, i.e., like CIDR, not the number of bits that are free
         * like OpenFlow encodes.
         *
         * @return A number between 0 (matches all IPs) and 32 (exact match)
         */
        public int getIpv4SrcCidrMaskLen() {
            return Math.max(32 - ((wildcards & OFPFW_NW_SRC_MASK) >> OFPFW_NW_SRC_SHIFT),
                            0);
        }


        @Override
        public boolean isFullyWildcarded(MatchField<?> field) {
            switch (field.id) {
                case IN_PORT:
                    return (this.wildcards & OFPFW_IN_PORT) != 0;
                case ETH_DST:
                    return (this.wildcards & OFPFW_DL_DST) != 0;
                case ETH_SRC:
                    return (this.wildcards & OFPFW_DL_SRC) != 0;
                case ETH_TYPE:
                    return (this.wildcards & OFPFW_DL_TYPE) != 0;
                case VLAN_VID:
                    return (this.wildcards & OFPFW_DL_VLAN) != 0;
                case VLAN_PCP:
                    return (this.wildcards & OFPFW_DL_VLAN_PCP) != 0;
                case ARP_OP:
                    return (this.wildcards & OFPFW_NW_PROTO) != 0;
                case ARP_SPA:
                    return this.getIpv4SrcCidrMaskLen() <= 0;
                case ARP_TPA:
                    return this.getIpv4DstCidrMaskLen() <= 0;
                case IP_DSCP:
                    return (this.wildcards & OFPFW_NW_TOS) != 0;
                case IP_PROTO:
                    return (this.wildcards & OFPFW_NW_PROTO) != 0;
                case TCP_SRC:
                    return (this.wildcards & OFPFW_TP_SRC) != 0;
                case TCP_DST:
                    return (this.wildcards & OFPFW_TP_DST) != 0;
                case UDP_SRC:
                    return (this.wildcards & OFPFW_TP_SRC) != 0;
                case UDP_DST:
                    return (this.wildcards & OFPFW_TP_DST) != 0;
                case SCTP_SRC:
                    return (this.wildcards & OFPFW_TP_SRC) != 0;
                case SCTP_DST:
                    return (this.wildcards & OFPFW_TP_DST) != 0;
                case ICMPV4_TYPE:
                    return (this.wildcards & OFPFW_TP_SRC) != 0;
                case ICMPV4_CODE:
                    return (this.wildcards & OFPFW_TP_DST) != 0;
                case IPV4_SRC:
                    return this.getIpv4SrcCidrMaskLen() <= 0;
                case IPV4_DST:
                    return this.getIpv4DstCidrMaskLen() <= 0;
                default:
                    throw new UnsupportedOperationException("OFMatch does not support matching on field " + field.getName());
            }
        }

        @Override
        public boolean isPartiallyMasked(MatchField<?> field) {
            switch (field.id) {
                case ARP_SPA:
                case IPV4_SRC:
                    int srcCidrLen = getIpv4SrcCidrMaskLen();
                    return srcCidrLen > 0 && srcCidrLen < 32;
                case ARP_TPA:
                case IPV4_DST:
                    int dstCidrLen = getIpv4DstCidrMaskLen();
                    return dstCidrLen > 0 && dstCidrLen < 32;
                default:
                    throw new UnsupportedOperationException("OFMatch does not support masked matching on field " + field.getName());
            }
        }

        private final void initWildcards() {
            if(!wildcardsSet) {
                wildcards = OFPFW_ALL;
                wildcardsSet = true;
            }
        }

        @Override
        public <F extends OFValueType<F>> Match.Builder setExact(MatchField<F> field,
                F value) {
            initWildcards();
            Object val = value;
            switch (field.id) {
                case ETH_DST:
                    setEthDst((MacAddress) value);
                    wildcards &= ~OFPFW_DL_DST;
                    break;
                case ETH_SRC:
                    setEthSrc((MacAddress) value);
                    wildcards &= ~OFPFW_DL_SRC;
                    break;
                case ETH_TYPE:
                    setEthType((EthType) value);
                    wildcards &= ~OFPFW_DL_TYPE;
                    break;
                case ICMPV4_CODE:
                    setTcpDst((TransportPort) value);
                    wildcards &= ~OFPFW_TP_DST;
                    break;
                case ICMPV4_TYPE:
                    setTcpSrc((TransportPort) value);
                    wildcards &= ~OFPFW_TP_SRC;
                    break;
                case IN_PORT:
                    setInPort((OFPort) value);
                    wildcards &= ~OFPFW_IN_PORT;
                    break;
                case ARP_OP:
                    setIpProto(IpProtocol.of((short)((ArpOpcode)value).getOpcode()));
                    wildcards &= ~OFPFW_NW_PROTO;
                    break;
                case ARP_TPA:
                case IPV4_DST:
                    setIpv4Dst((IPv4Address) value);
                    wildcards &= ~OFPFW_NW_DST_MASK;
                    break;
                case ARP_SPA:
                case IPV4_SRC:
                    setIpv4Src((IPv4Address) value);
                    wildcards &= ~OFPFW_NW_SRC_MASK;
                    break;
                case IP_DSCP:
                    setIpDscp((IpDscp) value);
                    wildcards &= ~OFPFW_NW_TOS;
                    break;
                case IP_PROTO:
                    setIpProto((IpProtocol) value);
                    wildcards &= ~OFPFW_NW_PROTO;
                    break;
                case SCTP_DST:
                    setTcpDst((TransportPort) value);
                    wildcards &= ~OFPFW_TP_DST;
                    break;
                case SCTP_SRC:
                    setTcpSrc((TransportPort) value);
                    wildcards &= ~OFPFW_TP_SRC;
                    break;
                case TCP_DST:
                    setTcpDst((TransportPort) value);
                    wildcards &= ~OFPFW_TP_DST;
                    break;
                case TCP_SRC:
                    setTcpSrc((TransportPort) value);
                    wildcards &= ~OFPFW_TP_SRC;
                    break;
                case UDP_DST:
                    setTcpDst((TransportPort) value);
                    wildcards &= ~OFPFW_TP_DST;
                    break;
                case UDP_SRC:
                    setTcpSrc((TransportPort) value);
                    wildcards &= ~OFPFW_TP_SRC;
                    break;
                case VLAN_PCP:
                    setVlanPcp((VlanPcp) value);
                    wildcards &= ~OFPFW_DL_VLAN_PCP;
                    break;
                case VLAN_VID:
                    setVlanVid((OFVlanVidMatch) value);
                    wildcards &= ~OFPFW_DL_VLAN;
                    break;
                default:
                    throw new UnsupportedOperationException(
                            "OFMatch does not support matching on field " + field.getName());
            }
            return this;
        }

        @Override
        public <F extends OFValueType<F>> Match.Builder setMasked(MatchField<F> field,
                F value, F mask) {
            initWildcards();
            switch (field.id) {
                case ARP_SPA:
                case ARP_TPA:
                case IPV4_DST:
                case IPV4_SRC:
                    Object valObj = value;
                    Object masObj = mask;
                    IPv4Address ip = ((IPv4Address)valObj);
                    int maskval = ((IPv4Address)masObj).getInt();
                    if (Integer.bitCount(~maskval + 1) != 1)
                        throw new UnsupportedOperationException("OFMatch only supports CIDR masks for IPv4");
                    int maskLen = 32 - Integer.bitCount(maskval);
                    switch(field.id) {
                        case ARP_TPA:
                        case IPV4_DST:
                            setIpv4Dst(ip);
                            wildcards = (wildcards &~OFPFW_NW_DST_MASK) | (maskLen << OFPFW_NW_DST_SHIFT);
                            break;
                        case ARP_SPA:
                        case IPV4_SRC:
                            setIpv4Src(ip);
                            wildcards = (wildcards &~OFPFW_NW_SRC_MASK) | (maskLen << OFPFW_NW_SRC_SHIFT);
                            break;
                        default:
                            // Cannot really get here
                            break;
                    }
                    break;
                default:
                    throw new UnsupportedOperationException("OFMatch does not support masked matching on field " + field.getName());
            }
            return this;
        }

        @Override
        public <F extends OFValueType<F>> Match.Builder setMasked(MatchField<F> field, Masked<F> valueWithMask)
                                                                       throws UnsupportedOperationException {
            return this.setMasked(field, valueWithMask.getValue(), valueWithMask.getMask());
        }

        @Override
        public <F extends OFValueType<F>> Match.Builder wildcard(MatchField<F> field) {
            initWildcards();
            switch (field.id) {
                case ETH_DST:
                    setEthDst(MacAddress.NONE);
                    wildcards |= OFPFW_DL_DST;
                    break;
                case ETH_SRC:
                    setEthSrc(MacAddress.NONE);
                    wildcards |= OFPFW_DL_SRC;
                    break;
                case ETH_TYPE:
                    setEthType(EthType.NONE);
                    wildcards |= OFPFW_DL_TYPE;
                    break;
                case ICMPV4_CODE:
                case TCP_DST:
                case UDP_DST:
                case SCTP_DST:
                    setTcpDst(TransportPort.NONE);
                    wildcards |= OFPFW_TP_DST;
                    break;
                case ICMPV4_TYPE:
                case TCP_SRC:
                case UDP_SRC:
                case SCTP_SRC:
                    setTcpSrc(TransportPort.NONE);
                    wildcards |= OFPFW_TP_SRC;
                    break;
                case IN_PORT:
                    setInPort(OFPort.of(0)); // NOTE: not 'NONE' -- that is 0xFF for ports
                    wildcards |= OFPFW_IN_PORT;
                    break;
                case ARP_TPA:
                case IPV4_DST:
                    setIpv4Dst(IPv4Address.NONE);
                    wildcards |= OFPFW_NW_DST_MASK;
                    break;
                case ARP_SPA:
                case IPV4_SRC:
                    setIpv4Src(IPv4Address.NONE);
                    wildcards |= OFPFW_NW_SRC_MASK;
                    break;
                case IP_DSCP:
                    setIpDscp(IpDscp.NONE);
                    wildcards |= OFPFW_NW_TOS;
                    break;
                case IP_PROTO:
                    setIpProto(IpProtocol.NONE);
                    wildcards |= OFPFW_NW_PROTO;
                    break;
                case VLAN_PCP:
                    setVlanPcp(VlanPcp.NONE);
                    wildcards |= OFPFW_DL_VLAN_PCP;
                    break;
                case VLAN_VID:
                    setVlanVid(OFVlanVidMatch.NONE);
                    wildcards |= OFPFW_DL_VLAN;
                    break;
                default:
                    throw new UnsupportedOperationException("OFMatch does not support matching on field " + field.getName());
            }
            return this;
        }

    }


    final static Reader READER = new Reader();
    static class Reader implements OFMessageReader<OFMatchV1> {
        @Override
        public OFMatchV1 readFrom(ChannelBuffer bb) throws OFParseError {
            int wildcards = bb.readInt();
            OFPort inPort = OFPort.read2Bytes(bb);
            MacAddress ethSrc = MacAddress.read6Bytes(bb);
            MacAddress ethDst = MacAddress.read6Bytes(bb);
            OFVlanVidMatch vlanVid = OFVlanVidMatch.read2BytesOF10(bb);
            VlanPcp vlanPcp = VlanPcp.readByte(bb);
            // pad: 1 bytes
            bb.skipBytes(1);
            EthType ethType = EthType.read2Bytes(bb);
            IpDscp ipDscp = IpDscp.readByte(bb);
            IpProtocol ipProto = IpProtocol.readByte(bb);
            // pad: 2 bytes
            bb.skipBytes(2);
            IPv4Address ipv4Src = IPv4Address.read4Bytes(bb);
            IPv4Address ipv4Dst = IPv4Address.read4Bytes(bb);
            TransportPort tcpSrc = TransportPort.read2Bytes(bb);
            TransportPort tcpDst = TransportPort.read2Bytes(bb);

            // normalize match fields according to current OpenVSwitch behavior. When prerequisites for a field are not met
            // e.g., eth_type is not set to 0x800, OVS sets the value of corresponding ignored fields (e.g.,
            // ip_src, tcp_dst) to 0, and sets the wildcard bit to 1.
            if(ethType.equals(EthType.IPv4)) {
                // IP
                if(ipProto.equals(IpProtocol.TCP) || ipProto.equals(IpProtocol.UDP) || ipProto.equals(IpProtocol.ICMP)) {
                    // fully speced, wildcards and all values are fine
                    // normalize 32-63 ipv4 src 'mask' to a full bitmask
                    if((wildcards & OFPFW_NW_SRC_ALL) != 0)
                        wildcards |= OFPFW_NW_SRC_MASK;

                    // normalize 32-63 ipv4 dst 'mask' to a full bitmask
                    if((wildcards & OFPFW_NW_DST_ALL) != 0)
                        wildcards |= OFPFW_NW_DST_MASK;

                } else {
                    // normalize 32-63 ipv4 src 'mask' to a full bitmask
                    if((wildcards & OFPFW_NW_SRC_ALL) != 0)
                        wildcards |= OFPFW_NW_SRC_MASK;

                    // normalize 32-63 ipv4 dst 'mask' to a full bitmask
                    if((wildcards & OFPFW_NW_DST_ALL) != 0)
                        wildcards |= OFPFW_NW_DST_MASK;

                    // not TCP/UDP/ICMP -> Clear TP wildcards for the wire
                    wildcards |= (OFPFW_TP_SRC | OFPFW_TP_DST);
                    tcpSrc = TransportPort.NONE;
                    tcpDst = TransportPort.NONE;
                }
            } else if (ethType.equals(EthType.ARP)) {
                // normalize 32-63 ipv4 src 'mask' to a full bitmask
                if((wildcards & OFPFW_NW_SRC_ALL) != 0)
                    wildcards |= OFPFW_NW_SRC_MASK;

                // normalize 32-63 ipv4 dst 'mask' to a full bitmask
                if((wildcards & OFPFW_NW_DST_ALL) != 0)
                    wildcards |= OFPFW_NW_DST_MASK;

                // ARP: clear NW_TOS / TP wildcards for the wire
                wildcards |= ( OFPFW_NW_TOS | OFPFW_TP_SRC | OFPFW_TP_DST);
                ipDscp = IpDscp.NONE;
                tcpSrc = TransportPort.NONE;
                tcpDst = TransportPort.NONE;
            } else {
                // not even IP. Clear NW/TP wildcards for the wire
                wildcards |= ( OFPFW_NW_TOS | OFPFW_NW_PROTO | OFPFW_NW_SRC_MASK | OFPFW_NW_DST_MASK | OFPFW_TP_SRC | OFPFW_TP_DST);
                ipDscp = IpDscp.NONE;
                ipProto = IpProtocol.NONE;
                ipv4Src = IPv4Address.NONE;
                ipv4Dst = IPv4Address.NONE;
                tcpSrc = TransportPort.NONE;
                tcpDst = TransportPort.NONE;
            }
            OFMatchV1Ver10 matchV1Ver10 = new OFMatchV1Ver10(
                    wildcards,
                      inPort,
                      ethSrc,
                      ethDst,
                      vlanVid,
                      vlanPcp,
                      ethType,
                      ipDscp,
                      ipProto,
                      ipv4Src,
                      ipv4Dst,
                      tcpSrc,
                      tcpDst
                    );
            if(logger.isTraceEnabled())
                logger.trace("readFrom - read={}", matchV1Ver10);
            return matchV1Ver10;
        }
    }

    public void putTo(PrimitiveSink sink) {
        FUNNEL.funnel(this, sink);
    }

    final static OFMatchV1Ver10Funnel FUNNEL = new OFMatchV1Ver10Funnel();
    static class OFMatchV1Ver10Funnel implements Funnel<OFMatchV1Ver10> {
        private static final long serialVersionUID = 1L;
        @Override
        public void funnel(OFMatchV1Ver10 message, PrimitiveSink sink) {
            sink.putInt(message.wildcards);
            message.inPort.putTo(sink);
            message.ethSrc.putTo(sink);
            message.ethDst.putTo(sink);
            message.vlanVid.putTo(sink);
            message.vlanPcp.putTo(sink);
            // skip pad (1 bytes)
            message.ethType.putTo(sink);
            message.ipDscp.putTo(sink);
            message.ipProto.putTo(sink);
            // skip pad (2 bytes)
            message.ipv4Src.putTo(sink);
            message.ipv4Dst.putTo(sink);
            message.tcpSrc.putTo(sink);
            message.tcpDst.putTo(sink);
        }
    }


    public void writeTo(ChannelBuffer bb) {
        WRITER.write(bb, this);
    }

    final static Writer WRITER = new Writer();
    static class Writer implements OFMessageWriter<OFMatchV1Ver10> {
        @Override
        public void write(ChannelBuffer bb, OFMatchV1Ver10 message) {
            bb.writeInt(message.wildcards);
            message.inPort.write2Bytes(bb);
            message.ethSrc.write6Bytes(bb);
            message.ethDst.write6Bytes(bb);
            message.vlanVid.write2BytesOF10(bb);
            message.vlanPcp.writeByte(bb);
            // pad: 1 bytes
            bb.writeZero(1);
            message.ethType.write2Bytes(bb);
            message.ipDscp.writeByte(bb);
            message.ipProto.writeByte(bb);
            // pad: 2 bytes
            bb.writeZero(2);
            message.ipv4Src.write4Bytes(bb);
            message.ipv4Dst.write4Bytes(bb);
            message.tcpSrc.write2Bytes(bb);
            message.tcpDst.write2Bytes(bb);


        }
    }

    @Override
    public String toString() {
        StringBuilder b = new StringBuilder("OFMatchV1Ver10(");
        boolean first = true;
        for(MatchField<?> field : getMatchFields()) {
            if(first)
                first = false;
            else
                b.append(", ");
            String name = field.getName();
            b.append(name).append('=').append(this.get(field));
            if(isPartiallyMasked(field)) {
                b.append('/').append(this.getMasked(field).getMask());
            }
        }
        b.append(")");
        return b.toString();
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        OFMatchV1Ver10 other = (OFMatchV1Ver10) obj;

        if( wildcards != other.wildcards)
            return false;
        if (inPort == null) {
            if (other.inPort != null)
                return false;
        } else if (!inPort.equals(other.inPort))
            return false;
        if (ethSrc == null) {
            if (other.ethSrc != null)
                return false;
        } else if (!ethSrc.equals(other.ethSrc))
            return false;
        if (ethDst == null) {
            if (other.ethDst != null)
                return false;
        } else if (!ethDst.equals(other.ethDst))
            return false;
        if (vlanVid == null) {
            if (other.vlanVid != null)
                return false;
        } else if (!vlanVid.equals(other.vlanVid))
            return false;
        if (vlanPcp == null) {
            if (other.vlanPcp != null)
                return false;
        } else if (!vlanPcp.equals(other.vlanPcp))
            return false;
        if (ethType == null) {
            if (other.ethType != null)
                return false;
        } else if (!ethType.equals(other.ethType))
            return false;
        if (ipDscp == null) {
            if (other.ipDscp != null)
                return false;
        } else if (!ipDscp.equals(other.ipDscp))
            return false;
        if (ipProto == null) {
            if (other.ipProto != null)
                return false;
        } else if (!ipProto.equals(other.ipProto))
            return false;
        if (ipv4Src == null) {
            if (other.ipv4Src != null)
                return false;
        } else if (!ipv4Src.equals(other.ipv4Src))
            return false;
        if (ipv4Dst == null) {
            if (other.ipv4Dst != null)
                return false;
        } else if (!ipv4Dst.equals(other.ipv4Dst))
            return false;
        if (tcpSrc == null) {
            if (other.tcpSrc != null)
                return false;
        } else if (!tcpSrc.equals(other.tcpSrc))
            return false;
        if (tcpDst == null) {
            if (other.tcpDst != null)
                return false;
        } else if (!tcpDst.equals(other.tcpDst))
            return false;
        return true;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;

        result = prime * result + wildcards;
        result = prime * result + ((inPort == null) ? 0 : inPort.hashCode());
        result = prime * result + ((ethSrc == null) ? 0 : ethSrc.hashCode());
        result = prime * result + ((ethDst == null) ? 0 : ethDst.hashCode());
        result = prime * result + ((vlanVid == null) ? 0 : vlanVid.hashCode());
        result = prime * result + ((vlanPcp == null) ? 0 : vlanPcp.hashCode());
        result = prime * result + ((ethType == null) ? 0 : ethType.hashCode());
        result = prime * result + ((ipDscp == null) ? 0 : ipDscp.hashCode());
        result = prime * result + ((ipProto == null) ? 0 : ipProto.hashCode());
        result = prime * result + ((ipv4Src == null) ? 0 : ipv4Src.hashCode());
        result = prime * result + ((ipv4Dst == null) ? 0 : ipv4Dst.hashCode());
        result = prime * result + ((tcpSrc == null) ? 0 : tcpSrc.hashCode());
        result = prime * result + ((tcpDst == null) ? 0 : tcpDst.hashCode());
        return result;
    }

}
