java_gen: OFOxm: add 'canonical' virtual attribute, test
This enables OFOxm objects to return a 'canonical' representation,
where trivial masks (all-0, all-1) are converted to the main
representation.
diff --git a/java_gen/java_model.py b/java_gen/java_model.py
index c2d43c2..0d1cc87 100644
--- a/java_gen/java_model.py
+++ b/java_gen/java_model.py
@@ -602,6 +602,7 @@
JavaVirtualMember(self, "mask", java_type.generic_t),
JavaVirtualMember(self, "matchField", java_type.make_match_field_jtype("T")),
JavaVirtualMember(self, "masked", java_type.boolean),
+ JavaVirtualMember(self, "canonical", java_type.make_oxm_jtype("T"))
]
elif self.parent_interface and self.parent_interface.startswith("OFOxm"):
field_type = java_type.make_match_field_jtype(model.oxm_map[self.name].type_name) \
@@ -611,6 +612,8 @@
virtual_members += [
JavaVirtualMember(self, "matchField", field_type),
JavaVirtualMember(self, "masked", java_type.boolean),
+ JavaVirtualMember(self, "canonical", java_type.make_oxm_jtype(model.oxm_map[self.name].type_name),
+ custom_template=lambda builder: "OFOxm{}_getCanonical.java".format(".Builder" if builder else "")),
]
if not find(lambda x: x.name == "mask", self.ir_model_members):
virtual_members.append(JavaVirtualMember(self, "mask", find(lambda x: x.name == "value", self.ir_model_members).java_type))
@@ -745,18 +748,21 @@
virtual_members += [
JavaVirtualMember(self, "matchField", java_type.make_match_field_jtype(oxm_entry.type_name), "MatchField.%s" % oxm_entry.value),
JavaVirtualMember(self, "masked", java_type.boolean, "true" if oxm_entry.masked else "false"),
- ]
+ ]
else:
virtual_members += [
JavaVirtualMember(self, "matchField", java_type.make_match_field_jtype(), "null"),
JavaVirtualMember(self, "masked", java_type.boolean, "false"),
- ]
-
+ ]
if not find(lambda m: m.name == "version", self.ir_model_members):
virtual_members.append(JavaVirtualMember(self, "version", java_type.of_version, "OFVersion.%s" % self.version.constant_version))
return tuple(virtual_members)
+ @memoize
+ def member_by_name(self, name):
+ return find(lambda m: m.name == name, self.members)
+
def all_versions(self):
return [ JavaOFVersion(int_version)
for int_version in of_g.unified[self.c_name]
diff --git a/java_gen/java_type.py b/java_gen/java_type.py
index 2772a86..d2e60f7 100644
--- a/java_gen/java_type.py
+++ b/java_gen/java_type.py
@@ -605,6 +605,8 @@
def make_match_field_jtype(sub_type_name="?"):
return JType("MatchField<{}>".format(sub_type_name))
+def make_oxm_jtype(sub_type_name="?"):
+ return JType("OFOxm<{}>".format(sub_type_name))
def list_cname_to_java_name(c_type):
m = re.match(r'list\(of_([a-zA-Z_]+)_t\)', c_type)
@@ -613,7 +615,6 @@
base_name = m.group(1)
return "OF" + name_c_to_caps_camel(base_name)
-
#### main entry point for conversion of LOXI types (c_types) Java types.
# FIXME: This badly needs a refactoring
diff --git a/java_gen/pre-written/src/test/java/org/projectfloodlight/protocol/OFOxmTest.java b/java_gen/pre-written/src/test/java/org/projectfloodlight/protocol/OFOxmTest.java
new file mode 100644
index 0000000..8482886
--- /dev/null
+++ b/java_gen/pre-written/src/test/java/org/projectfloodlight/protocol/OFOxmTest.java
@@ -0,0 +1,62 @@
+package org.projectfloodlight.protocol;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import org.hamcrest.CoreMatchers;
+import org.junit.Before;
+import org.junit.Test;
+import org.projectfloodlight.openflow.protocol.OFFactories;
+import org.projectfloodlight.openflow.protocol.OFVersion;
+import org.projectfloodlight.openflow.protocol.oxm.OFOxm;
+import org.projectfloodlight.openflow.protocol.oxm.OFOxmIpv4Src;
+import org.projectfloodlight.openflow.protocol.oxm.OFOxmIpv4SrcMasked;
+import org.projectfloodlight.openflow.protocol.oxm.OFOxms;
+import org.projectfloodlight.openflow.types.IPv4Address;
+import org.projectfloodlight.openflow.types.IPv4AddressWithMask;
+
+public class OFOxmTest {
+ private OFOxms oxms;
+
+ @Before
+ public void setup() {
+ oxms = OFFactories.getFactory(OFVersion.OF_13).oxms();
+ }
+
+ @Test
+ public void testGetCanonicalFullMask() {
+ IPv4AddressWithMask empty = IPv4AddressWithMask.of("0.0.0.0/0");
+ assertEquals(IPv4Address.FULL_MASK, empty.getMask());
+ OFOxmIpv4SrcMasked ipv4SrcMasked = oxms.ipv4SrcMasked(empty.getValue(), empty.getMask());
+ // canonicalize should remove /0
+ assertNull(ipv4SrcMasked.getCanonical());
+ }
+
+ @Test
+ public void testGetCanonicalNoMask() {
+ IPv4AddressWithMask fullIp = IPv4AddressWithMask.of("1.2.3.4/32");
+ assertEquals(IPv4Address.NO_MASK, fullIp.getMask());
+ OFOxmIpv4SrcMasked ipv4SrcMasked = oxms.ipv4SrcMasked(fullIp.getValue(), fullIp.getMask());
+ assertTrue(ipv4SrcMasked.isMasked());
+ assertEquals(IPv4Address.NO_MASK, ipv4SrcMasked.getMask());
+
+ // canonicalize should convert the masked oxm to the non-masked one
+ OFOxm<IPv4Address> canonical = ipv4SrcMasked.getCanonical();
+ assertThat(canonical, CoreMatchers.instanceOf(OFOxmIpv4Src.class));
+ assertFalse(canonical.isMasked());
+ }
+
+ @Test
+ public void testGetCanonicalNormalMask() {
+ IPv4AddressWithMask ip = IPv4AddressWithMask.of("1.2.3.0/24");
+ OFOxmIpv4SrcMasked ipv4SrcMasked = oxms.ipv4SrcMasked(ip.getValue(), ip.getMask());
+ assertTrue(ipv4SrcMasked.isMasked());
+
+ // canonicalize should convert the masked oxm to the non-masked one
+ OFOxm<IPv4Address> canonical = ipv4SrcMasked.getCanonical();
+ assertEquals(ipv4SrcMasked, canonical);
+ }
+}
diff --git a/java_gen/templates/custom/OFOxm_getCanonical.java b/java_gen/templates/custom/OFOxm_getCanonical.java
new file mode 100644
index 0000000..6681870
--- /dev/null
+++ b/java_gen/templates/custom/OFOxm_getCanonical.java
@@ -0,0 +1,17 @@
+//:: import re
+ public ${prop.java_type.public_type} getCanonical() {
+ //:: if not msg.member_by_name("masked").value == "true":
+ // exact match OXM is always canonical
+ return this;
+ //:: else:
+ //:: mask_type = msg.member_by_name("mask").java_type.public_type
+ if (${mask_type}.NO_MASK.equals(mask)) {
+ //:: unmasked = re.sub(r'(.*)Masked(Ver.*)', r'\1\2', msg.name)
+ return new ${unmasked}(value);
+ } else if(${mask_type}.FULL_MASK.equals(mask)) {
+ return null;
+ } else {
+ return this;
+ }
+ //:: #endif
+ }