java_gen: OFMatch add support for OF1.4 (and fix OF1.2)

pulled the custom Match code from OFMatchV3Ver13 out into generic
templates for OFMatchV3, that's included for OF1[234].
diff --git a/java_gen/templates/custom/OFMatchV3.Builder.java b/java_gen/templates/custom/OFMatchV3.Builder.java
new file mode 100644
index 0000000..54b35ba
--- /dev/null
+++ b/java_gen/templates/custom/OFMatchV3.Builder.java
@@ -0,0 +1,106 @@
+
+    private OFOxmList.Builder oxmListBuilder;
+
+    private void initBuilder() {
+        if (oxmListBuilder != null)
+            return;
+        oxmListBuilder = new OFOxmList.Builder();
+    }
+
+    private void updateOxmList() {
+        this.oxmList = this.oxmListBuilder.build();
+        this.oxmListSet = true;
+    }
+
+    private <F extends OFValueType<F>> OFOxm<F> getOxm(MatchField<F> field) {
+//:: if has_parent:
+        return this.oxmListSet ? this.oxmList.get(field) : parentMessage.oxmList.get(field);
+//:: else:
+        return this.oxmListSet ? this.oxmList.get(field) : null;
+//:: #endif
+    }
+
+    @Override
+    public <F extends OFValueType<F>> F get(MatchField<F> field)
+            throws UnsupportedOperationException {
+        OFOxm<F> value = getOxm(field);
+        if (value == null)
+            return null;
+        return value.getValue();
+    }
+
+    @Override
+    public <F extends OFValueType<F>> Masked<F> getMasked(MatchField<F> field)
+            throws UnsupportedOperationException {
+        OFOxm<F> value = getOxm(field);
+        if (value == null || !value.isMasked())
+            return null;
+        // TODO: If changing OXMs to extend Masked, then use it here
+        return Masked.of(value.getValue(), value.getMask());
+    }
+
+    @Override
+    public boolean supports(MatchField<?> field) {
+        return supportsField(field);
+    }
+
+    @Override
+    public boolean supportsMasked(MatchField<?> field) {
+        return supportsField(field);
+    }
+
+    @Override
+    public boolean isExact(MatchField<?> field) {
+        OFOxm<?> value = getOxm(field);
+        return (value != null && !value.isMasked());
+    }
+
+    @Override
+    public boolean isFullyWildcarded(MatchField<?> field) {
+        OFOxm<?> value = getOxm(field);
+        return (value == null);
+    }
+
+    @Override
+    public boolean isPartiallyMasked(MatchField<?> field) {
+        OFOxm<?> value = getOxm(field);
+        return (value != null && value.isMasked());
+    }
+
+    @Override
+    public <F extends OFValueType<F>> Match.Builder setExact(
+            MatchField<F> field, F value) {
+        initBuilder();
+        OFOxm<F> oxm = OFFactories.getFactory(OFVersion.${version.constant_version}).oxms().fromValue(value, field);
+        this.oxmListBuilder.set(oxm);
+        updateOxmList();
+        return this;
+    }
+
+    @Override
+    public <F extends OFValueType<F>> Match.Builder setMasked(
+            MatchField<F> field, F value, F mask) {
+        initBuilder();
+        OFOxm<F> oxm = OFFactories.getFactory(OFVersion.${version.constant_version}).oxms().fromValueAndMask(value, mask, field);
+        this.oxmListBuilder.set(oxm);
+        updateOxmList();
+        return this;
+    }
+
+    @Override
+    public <F extends OFValueType<F>> Match.Builder setMasked(
+            MatchField<F> field, Masked<F> valueWithMask) {
+        initBuilder();
+        OFOxm<F> oxm = OFFactories.getFactory(OFVersion.${version.constant_version}).oxms().fromMasked(valueWithMask, field);
+        this.oxmListBuilder.set(oxm);
+        updateOxmList();
+        return this;
+    }
+
+    @Override
+    public <F extends OFValueType<F>> Match.Builder wildcard(MatchField<F> field) {
+        initBuilder();
+        this.oxmListBuilder.unset(field);
+        updateOxmList();
+        return this;
+    }
diff --git a/java_gen/templates/custom/OFMatchV3.java b/java_gen/templates/custom/OFMatchV3.java
new file mode 100644
index 0000000..799135c
--- /dev/null
+++ b/java_gen/templates/custom/OFMatchV3.java
@@ -0,0 +1,112 @@
+//:: from generic_utils import OrderedSet
+//:: from java_gen.java_model import model
+    @Override
+    public <F extends OFValueType<F>> F get(MatchField<F> field)
+            throws UnsupportedOperationException {
+        if (!supports(field))
+            throw new UnsupportedOperationException("${msg.name} does not support matching on field " + field.getName());
+
+        OFOxm<F> oxm = this.oxmList.get(field);
+
+        if (oxm == null || !field.arePrerequisitesOK(this))
+            return null;
+
+        return oxm.getValue();
+    }
+
+    @Override
+    public <F extends OFValueType<F>> Masked<F> getMasked(MatchField<F> field)
+            throws UnsupportedOperationException {
+        if (!supportsMasked(field))
+            throw new UnsupportedOperationException("${msg.name} does not support masked matching on field " + field.getName());
+
+        OFOxm<F> oxm = this.oxmList.get(field);
+
+        if (oxm == null || !field.arePrerequisitesOK(this))
+            return null;
+
+        if (oxm.getMask() == null)
+            return null;
+
+        // TODO: Make OfOxm extend Masked and just return the OXM?
+        return Masked.of(oxm.getValue(), oxm.getMask());
+    }
+
+    private static boolean supportsField(MatchField<?> field) {
+        switch (field.id) {
+            //:: for id_constant in sorted(set(id_constant for _, id_constant, _ in model.oxm_map.values())):
+            case ${id_constant}:
+            //:: #endfor
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    @Override
+    public boolean supports(MatchField<?> field) {
+        return supportsField(field);
+    }
+
+    @Override
+    public boolean supportsMasked(MatchField<?> field) {
+        return supportsField(field);
+    }
+
+    @Override
+    public boolean isExact(MatchField<?> field) {
+        if (!supports(field))
+            throw new UnsupportedOperationException("${msg.name} does not support matching on field " + field.getName());
+
+        OFOxm<?> oxm = this.oxmList.get(field);
+
+        return oxm != null && !oxm.isMasked();
+    }
+
+    @Override
+    public boolean isFullyWildcarded(MatchField<?> field) {
+        if (!supports(field))
+            throw new UnsupportedOperationException("${msg.name} does not support matching on field " + field.getName());
+
+        OFOxm<?> oxm = this.oxmList.get(field);
+
+        return oxm == null;
+    }
+
+    @Override
+    public boolean isPartiallyMasked(MatchField<?> field) {
+        if (!supports(field))
+            throw new UnsupportedOperationException("${msg.name} does not support matching on field " + field.getName());
+
+        OFOxm<?> oxm = this.oxmList.get(field);
+
+        return oxm != null && oxm.isMasked();
+    }
+
+    private class MatchFieldIterator extends AbstractIterator<MatchField<?>> {
+        private Iterator<OFOxm<?>> oxmIterator;
+
+        MatchFieldIterator() {
+            oxmIterator = oxmList.iterator();
+        }
+
+        @Override
+        protected MatchField<?> computeNext() {
+            while(oxmIterator.hasNext()) {
+                OFOxm<?> oxm = oxmIterator.next();
+                if(oxm.getMatchField().arePrerequisitesOK(${msg.name}.this))
+                   return oxm.getMatchField();
+            }
+            endOfData();
+            return null;
+        }
+    }
+
+    @Override
+    public Iterable<MatchField<?>> getMatchFields() {
+        return new Iterable<MatchField<?>>() {
+            public Iterator<MatchField<?>> iterator() {
+                return new MatchFieldIterator();
+            }
+        };
+    }
diff --git a/java_gen/templates/custom/OFMatchV3Ver12.Builder.java b/java_gen/templates/custom/OFMatchV3Ver12.Builder.java
index 3fae367..ae2ecaa 100644
--- a/java_gen/templates/custom/OFMatchV3Ver12.Builder.java
+++ b/java_gen/templates/custom/OFMatchV3Ver12.Builder.java
@@ -1,106 +1 @@
-
-    private OFOxmList.Builder oxmListBuilder;
-
-    private synchronized void initBuilder() {
-        if (oxmListBuilder != null)
-            return;
-        oxmListBuilder = new OFOxmList.Builder();
-    }
-
-    private synchronized void updateOxmList() {
-        this.oxmList = this.oxmListBuilder.build();
-        this.oxmListSet = true;
-    }
-
-    private <F extends OFValueType<F>> OFOxm<F> getOxm(MatchField<F> field) {
-//:: if has_parent:
-        return this.oxmListSet ? this.oxmList.get(field) : parentMessage.oxmList.get(field);
-//:: else:
-        return this.oxmListSet ? this.oxmList.get(field) : null;
-//:: #endif
-    }
-
-    @Override
-    public synchronized <F extends OFValueType<F>> F get(MatchField<F> field)
-            throws UnsupportedOperationException {
-        OFOxm<F> value = getOxm(field);
-        if (value == null)
-            return null;
-        return value.getValue();
-    }
-
-    @Override
-    public synchronized <F extends OFValueType<F>> Masked<F> getMasked(MatchField<F> field)
-            throws UnsupportedOperationException {
-        OFOxm<F> value = getOxm(field);
-        if (value == null || !value.isMasked())
-            return null;
-        // TODO: If changing OXMs to extend Masked, then use it here
-        return Masked.of(value.getValue(), value.getMask());
-    }
-
-    @Override
-    public boolean supports(MatchField<?> field) {
-        return supportsField(field);
-    }
-
-    @Override
-    public boolean supportsMasked(MatchField<?> field) {
-        return supportsField(field);
-    }
-
-    @Override
-    public synchronized boolean isExact(MatchField<?> field) {
-        OFOxm<?> value = getOxm(field);
-        return (value != null && !value.isMasked());
-    }
-
-    @Override
-    public synchronized boolean isFullyWildcarded(MatchField<?> field) {
-        OFOxm<?> value = getOxm(field);
-        return (value == null);
-    }
-
-    @Override
-    public synchronized boolean isPartiallyMasked(MatchField<?> field) {
-        OFOxm<?> value = getOxm(field);
-        return (value != null && value.isMasked());
-    }
-
-    @Override
-    public synchronized <F extends OFValueType<F>> Match.Builder setExact(
-            MatchField<F> field, F value) {
-        initBuilder();
-        OFOxm<F> oxm = OFFactories.getFactory(OFVersion.OF_13).oxms().fromValue(value, field);
-        this.oxmListBuilder.set(oxm);
-        updateOxmList();
-        return this;
-    }
-
-    @Override
-    public synchronized <F extends OFValueType<F>> Match.Builder setMasked(
-            MatchField<F> field, F value, F mask) {
-        initBuilder();
-        OFOxm<F> oxm = OFFactories.getFactory(OFVersion.OF_13).oxms().fromValueAndMask(value, mask, field);
-        this.oxmListBuilder.set(oxm);
-        updateOxmList();
-        return this;
-    }
-
-    @Override
-    public synchronized <F extends OFValueType<F>> Match.Builder setMasked(
-            MatchField<F> field, Masked<F> valueWithMask) {
-        initBuilder();
-        OFOxm<F> oxm = OFFactories.getFactory(OFVersion.OF_13).oxms().fromMasked(valueWithMask, field);
-        this.oxmListBuilder.set(oxm);
-        updateOxmList();
-        return this;
-    }
-
-    @Override
-    public synchronized <F extends OFValueType<F>> Match.Builder wildcard(MatchField<F> field) {
-        initBuilder();
-        this.oxmListBuilder.unset(field);
-        updateOxmList();
-        return this;
-    }
+//:: include("custom/OFMatchV3.Builder.java", msg=msg, version=version, has_parent=False)
diff --git a/java_gen/templates/custom/OFMatchV3Ver12.java b/java_gen/templates/custom/OFMatchV3Ver12.java
index 81092c1..225b0dc 100644
--- a/java_gen/templates/custom/OFMatchV3Ver12.java
+++ b/java_gen/templates/custom/OFMatchV3Ver12.java
@@ -1,114 +1 @@
-
-    @Override
-    public <F extends OFValueType<F>> F get(MatchField<F> field)
-            throws UnsupportedOperationException {
-        if (!supports(field))
-            throw new UnsupportedOperationException("OFMatchV3Ver13 does not support matching on field " + field.getName());
-
-        OFOxm<F> oxm = this.oxmList.get(field);
-
-        if (oxm == null || !field.arePrerequisitesOK(this))
-            return null;
-
-        return oxm.getValue();
-    }
-
-    @Override
-    public <F extends OFValueType<F>> Masked<F> getMasked(MatchField<F> field)
-            throws UnsupportedOperationException {
-        if (!supportsMasked(field))
-            throw new UnsupportedOperationException("OFMatchV3Ver13 does not support masked matching on field " + field.getName());
-
-        OFOxm<F> oxm = this.oxmList.get(field);
-
-        if (oxm == null || !field.arePrerequisitesOK(this))
-            return null;
-
-        if (oxm.getMask() == null)
-            return null;
-
-        // TODO: Make OfOxm extend Masked and just return the OXM?
-        return Masked.of(oxm.getValue(), oxm.getMask());
-    }
-
-    private static boolean supportsField(MatchField<?> field) {
-        switch (field.id) {
-            case IN_PORT:
-            case IN_PHY_PORT:
-            case METADATA:
-            case ETH_DST:
-            case ETH_SRC:
-            case ETH_TYPE:
-            case VLAN_VID:
-            case VLAN_PCP:
-            case IP_DSCP:
-            case IP_ECN:
-            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:
-            case ARP_OP:
-            case ARP_SPA:
-            case ARP_TPA:
-            case ARP_SHA:
-            case ARP_THA:
-            case IPV6_SRC:
-            case IPV6_DST:
-            case IPV6_FLABEL:
-                return true;
-            default:
-                return false;
-        }
-    }
-
-    @Override
-    public boolean supports(MatchField<?> field) {
-        return supportsField(field);
-    }
-
-    @Override
-    public boolean supportsMasked(MatchField<?> field) {
-        return supportsField(field);
-    }
-
-    @Override
-    public boolean isExact(MatchField<?> field) {
-        if (!supports(field))
-            throw new UnsupportedOperationException("OFMatchV3Ver13 does not support matching on field " + field.getName());
-
-        OFOxm<?> oxm = this.oxmList.get(field);
-
-        return oxm != null && !oxm.isMasked();
-    }
-
-    @Override
-    public boolean isFullyWildcarded(MatchField<?> field) {
-        if (!supports(field))
-            throw new UnsupportedOperationException("OFMatchV3Ver13 does not support matching on field " + field.getName());
-
-        OFOxm<?> oxm = this.oxmList.get(field);
-
-        return oxm == null;
-    }
-
-    @Override
-    public boolean isPartiallyMasked(MatchField<?> field) {
-        if (!supports(field))
-            throw new UnsupportedOperationException("OFMatchV3Ver13 does not support matching on field " + field.getName());
-
-        OFOxm<?> oxm = this.oxmList.get(field);
-
-        return oxm != null && oxm.isMasked();
-    }
-
-    @Override
-    public Iterable<MatchField<?>> getMatchFields() {
-        throw new UnsupportedOperationException();
-    }
+//:: include("custom/OFMatchV3.java", msg=msg, has_parent=False)
diff --git a/java_gen/templates/custom/OFMatchV3Ver12_toString.java b/java_gen/templates/custom/OFMatchV3Ver12_toString.java
new file mode 120000
index 0000000..4596b8f
--- /dev/null
+++ b/java_gen/templates/custom/OFMatchV3Ver12_toString.java
@@ -0,0 +1 @@
+OFMatchV3Ver13_toString.java
\ No newline at end of file
diff --git a/java_gen/templates/custom/OFMatchV3Ver13.Builder.java b/java_gen/templates/custom/OFMatchV3Ver13.Builder.java
index 79cbdbc..ae2ecaa 100644
--- a/java_gen/templates/custom/OFMatchV3Ver13.Builder.java
+++ b/java_gen/templates/custom/OFMatchV3Ver13.Builder.java
@@ -1,107 +1 @@
-
-    private OFOxmList.Builder oxmListBuilder;
-
-    private void initBuilder() {
-        if (oxmListBuilder != null)
-            return;
-        oxmListBuilder = new OFOxmList.Builder();
-    }
-
-    private void updateOxmList() {
-        this.oxmList = this.oxmListBuilder.build();
-        this.oxmListSet = true;
-    }
-
-    private <F extends OFValueType<F>> OFOxm<F> getOxm(MatchField<F> field) {
-//:: if has_parent:
-        return this.oxmListSet ? this.oxmList.get(field) : parentMessage.oxmList.get(field);
-//:: else:
-        return this.oxmListSet ? this.oxmList.get(field) : null;
-//:: #endif
-    }
-
-    @Override
-    public <F extends OFValueType<F>> F get(MatchField<F> field)
-            throws UnsupportedOperationException {
-        OFOxm<F> value = getOxm(field);
-        if (value == null)
-            return null;
-        return value.getValue();
-    }
-
-    @Override
-    public <F extends OFValueType<F>> Masked<F> getMasked(MatchField<F> field)
-            throws UnsupportedOperationException {
-        OFOxm<F> value = getOxm(field);
-        if (value == null || !value.isMasked())
-            return null;
-        // TODO: If changing OXMs to extend Masked, then use it here
-        return Masked.of(value.getValue(), value.getMask());
-    }
-
-    @Override
-    public boolean supports(MatchField<?> field) {
-        return supportsField(field);
-    }
-
-    @Override
-    public boolean supportsMasked(MatchField<?> field) {
-        return supportsField(field);
-    }
-
-    @Override
-    public boolean isExact(MatchField<?> field) {
-        OFOxm<?> value = getOxm(field);
-        return (value != null && !value.isMasked());
-    }
-
-    @Override
-    public boolean isFullyWildcarded(MatchField<?> field) {
-        OFOxm<?> value = getOxm(field);
-        return (value == null);
-    }
-
-    @Override
-    public boolean isPartiallyMasked(MatchField<?> field) {
-        OFOxm<?> value = getOxm(field);
-        return (value != null && value.isMasked());
-    }
-
-    @Override
-    public <F extends OFValueType<F>> Match.Builder setExact(
-            MatchField<F> field, F value) {
-        initBuilder();
-        OFOxm<F> oxm = OFFactories.getFactory(OFVersion.OF_13).oxms().fromValue(value, field);
-        this.oxmListBuilder.set(oxm);
-        updateOxmList();
-        return this;
-    }
-
-    @Override
-    public <F extends OFValueType<F>> Match.Builder setMasked(
-            MatchField<F> field, F value, F mask) {
-        initBuilder();
-        OFOxm<F> oxm = OFFactories.getFactory(OFVersion.OF_13).oxms().fromValueAndMask(value, mask, field);
-        this.oxmListBuilder.set(oxm);
-        updateOxmList();
-        return this;
-    }
-
-    @Override
-    public <F extends OFValueType<F>> Match.Builder setMasked(
-            MatchField<F> field, Masked<F> valueWithMask) {
-        initBuilder();
-        OFOxm<F> oxm = OFFactories.getFactory(OFVersion.OF_13).oxms().fromMasked(valueWithMask, field);
-        this.oxmListBuilder.set(oxm);
-        updateOxmList();
-        return this;
-    }
-
-    @Override
-    public <F extends OFValueType<F>> Match.Builder wildcard(MatchField<F> field) {
-        initBuilder();
-        this.oxmListBuilder.unset(field);
-        updateOxmList();
-        return this;
-    }
-
+//:: include("custom/OFMatchV3.Builder.java", msg=msg, version=version, has_parent=False)
diff --git a/java_gen/templates/custom/OFMatchV3Ver13.java b/java_gen/templates/custom/OFMatchV3Ver13.java
index dc8e637..225b0dc 100644
--- a/java_gen/templates/custom/OFMatchV3Ver13.java
+++ b/java_gen/templates/custom/OFMatchV3Ver13.java
@@ -1,112 +1 @@
-//:: from generic_utils import OrderedSet
-//:: from java_gen.java_model import model
-    @Override
-    public <F extends OFValueType<F>> F get(MatchField<F> field)
-            throws UnsupportedOperationException {
-        if (!supports(field))
-            throw new UnsupportedOperationException("OFMatchV3Ver13 does not support matching on field " + field.getName());
-
-        OFOxm<F> oxm = this.oxmList.get(field);
-
-        if (oxm == null || !field.arePrerequisitesOK(this))
-            return null;
-
-        return oxm.getValue();
-    }
-
-    @Override
-    public <F extends OFValueType<F>> Masked<F> getMasked(MatchField<F> field)
-            throws UnsupportedOperationException {
-        if (!supportsMasked(field))
-            throw new UnsupportedOperationException("OFMatchV3Ver13 does not support masked matching on field " + field.getName());
-
-        OFOxm<F> oxm = this.oxmList.get(field);
-
-        if (oxm == null || !field.arePrerequisitesOK(this))
-            return null;
-
-        if (oxm.getMask() == null)
-            return null;
-
-        // TODO: Make OfOxm extend Masked and just return the OXM?
-        return Masked.of(oxm.getValue(), oxm.getMask());
-    }
-
-    private static boolean supportsField(MatchField<?> field) {
-        switch (field.id) {
-            //:: for id_constant in sorted(set(id_constant for _, id_constant, _ in model.oxm_map.values())):
-            case ${id_constant}:
-            //:: #endfor
-                return true;
-            default:
-                return false;
-        }
-    }
-
-    @Override
-    public boolean supports(MatchField<?> field) {
-        return supportsField(field);
-    }
-
-    @Override
-    public boolean supportsMasked(MatchField<?> field) {
-        return supportsField(field);
-    }
-
-    @Override
-    public boolean isExact(MatchField<?> field) {
-        if (!supports(field))
-            throw new UnsupportedOperationException("OFMatchV3Ver13 does not support matching on field " + field.getName());
-
-        OFOxm<?> oxm = this.oxmList.get(field);
-
-        return oxm != null && !oxm.isMasked();
-    }
-
-    @Override
-    public boolean isFullyWildcarded(MatchField<?> field) {
-        if (!supports(field))
-            throw new UnsupportedOperationException("OFMatchV3Ver13 does not support matching on field " + field.getName());
-
-        OFOxm<?> oxm = this.oxmList.get(field);
-
-        return oxm == null;
-    }
-
-    @Override
-    public boolean isPartiallyMasked(MatchField<?> field) {
-        if (!supports(field))
-            throw new UnsupportedOperationException("OFMatchV3Ver13 does not support matching on field " + field.getName());
-
-        OFOxm<?> oxm = this.oxmList.get(field);
-
-        return oxm != null && oxm.isMasked();
-    }
-
-    private class MatchFieldIterator extends AbstractIterator<MatchField<?>> {
-        private Iterator<OFOxm<?>> oxmIterator;
-
-        MatchFieldIterator() {
-            oxmIterator = oxmList.iterator();
-        }
-
-        @Override
-        protected MatchField<?> computeNext() {
-            while(oxmIterator.hasNext()) {
-                OFOxm<?> oxm = oxmIterator.next();
-                if(oxm.getMatchField().arePrerequisitesOK(OFMatchV3Ver13.this))
-                   return oxm.getMatchField();
-            }
-            endOfData();
-            return null;
-        }
-    }
-
-    @Override
-    public Iterable<MatchField<?>> getMatchFields() {
-        return new Iterable<MatchField<?>>() {
-            public Iterator<MatchField<?>> iterator() {
-                return new MatchFieldIterator();
-            }
-        };
-    }
+//:: include("custom/OFMatchV3.java", msg=msg, has_parent=False)
diff --git a/java_gen/templates/custom/OFMatchV3Ver14.Builder.java b/java_gen/templates/custom/OFMatchV3Ver14.Builder.java
new file mode 100644
index 0000000..ae2ecaa
--- /dev/null
+++ b/java_gen/templates/custom/OFMatchV3Ver14.Builder.java
@@ -0,0 +1 @@
+//:: include("custom/OFMatchV3.Builder.java", msg=msg, version=version, has_parent=False)
diff --git a/java_gen/templates/custom/OFMatchV3Ver14.java b/java_gen/templates/custom/OFMatchV3Ver14.java
new file mode 120000
index 0000000..916a2be
--- /dev/null
+++ b/java_gen/templates/custom/OFMatchV3Ver14.java
@@ -0,0 +1 @@
+OFMatchV3Ver13.java
\ No newline at end of file
diff --git a/java_gen/templates/custom/OFMatchV3Ver14_toString.java b/java_gen/templates/custom/OFMatchV3Ver14_toString.java
new file mode 120000
index 0000000..4596b8f
--- /dev/null
+++ b/java_gen/templates/custom/OFMatchV3Ver14_toString.java
@@ -0,0 +1 @@
+OFMatchV3Ver13_toString.java
\ No newline at end of file