blob: 96ba163438ee18ee474d5021631117a5bb311b81 [file] [log] [blame]
Andreas Wundsam27303462013-07-16 12:52:35 -07001# Copyright 2013, Big Switch Networks, Inc.
2#
3# LoxiGen is licensed under the Eclipse Public License, version 1.0 (EPL), with
4# the following special exception:
5#
6# LOXI Exception
7#
8# As a special exception to the terms of the EPL, you may distribute libraries
9# generated by LoxiGen (LoxiGen Libraries) under the terms of your choice, provided
10# that copyright and licensing notices generated by LoxiGen are not altered or removed
11# from the LoxiGen Libraries and the notice provided below is (i) included in
12# the LoxiGen Libraries, if distributed in source code form and (ii) included in any
13# documentation for the LoxiGen Libraries, if distributed in binary form.
14#
15# Notice: "Copyright 2013, Big Switch Networks, Inc. This library was generated by the LoxiGen Compiler."
16#
17# You may not use this file except in compliance with the EPL or LOXI Exception. You may obtain
18# a copy of the EPL at:
19#
20# http://www.eclipse.org/legal/epl-v10.html
21#
22# Unless required by applicable law or agreed to in writing, software
23# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
24# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
25# EPL for the specific language governing permissions and limitations
26# under the EPL.
27
Andreas Wundsam40e14f72013-05-06 14:49:08 -070028# Prototype of an Intermediate Object model for the java code generator
29# A lot of this stuff could/should probably be merged with the python utilities
30
Andreas Wundsam27303462013-07-16 12:52:35 -070031import collections
Andreas Wundsam5204de22013-07-30 11:34:45 -070032from collections import namedtuple, defaultdict, OrderedDict
Andreas Wundsam27303462013-07-16 12:52:35 -070033import logging
Andreas Wundsam40e14f72013-05-06 14:49:08 -070034import os
35import pdb
36import re
37
Andreas Wundsam5204de22013-07-30 11:34:45 -070038from generic_utils import find, memoize, OrderedSet, OrderedDefaultDict
Andreas Wundsam27303462013-07-16 12:52:35 -070039import of_g
40from loxi_ir import *
Andreas Wundsam40e14f72013-05-06 14:49:08 -070041import loxi_front_end.type_maps as type_maps
Andreas Wundsam5204de22013-07-30 11:34:45 -070042import loxi_utils.loxi_utils as loxi_utils
Andreas Wundsam40e14f72013-05-06 14:49:08 -070043import py_gen.util as py_utils
Andreas Wundsam5204de22013-07-30 11:34:45 -070044import test_data
Andreas Wundsam40e14f72013-05-06 14:49:08 -070045
Andreas Wundsam27303462013-07-16 12:52:35 -070046import java_gen.java_type as java_type
Andreas Wundsame0d52be2013-08-22 07:52:13 -070047from java_gen.java_type import erase_type_annotation
Andreas Wundsam40e14f72013-05-06 14:49:08 -070048
Rob Vaterlaus4d311942013-09-24 13:41:44 -070049# Key is modified name of error code; value is OFErrorType value string
50error_type_map = {}
51
52def adjust_ir():
53 """
54 For Java we change of_error_message to combine the 16-bit type and code
55 fields into a single 32-bit code field and we combine the per-error-type
56 code enums into a single ofp_error_code enum. This enables the generated
57 OFErrorMsg class to have a getCode method that can return all supported
58 error codes. Otherwise we'd need to do something like having per-error-type
59 subclasses of OFErrorMsg that had a getCode that returned the different
60 error codes for each error type, which would be less convenient for clients
61 and would also entail changing the LOXI OF input files and impacting other
62 language backends.
63 """
64 for version in of_g.target_version_list:
65 of_protocol = of_g.ir[version]
66 error_type = find(lambda e: e.name == "ofp_error_type", of_protocol.enums)
67 if error_type == None:
68 raise Exception("ofp_error_type enum not found; OF version: " + str(version))
69 error_code_entries = []
70 # For each error type value look up the corresponding error code enum.
71 # Add those values to the combined entries for the new ofp_error_code
72 # enum. The name of the new value is formed by concatenating the name
73 # of the error type value with the name of the old error code value.
74 for error_type_entry in error_type.entries:
75 # Strip off the OFPxx prefix
76 prefix_length = error_type_entry.name.find('_')
77 if prefix_length < 0:
78 raise Exception("OFPET prefix not found for ofp_error_type value " + error_type_entry.name + "; OF version: " + str(version))
79 error_type_entry_name = error_type_entry.name[prefix_length+1:]
80 if error_type_entry_name == "EXPERIMENTER":
81 # There isn't an error code enum defined for the experimenter error type
82 # FIXME: Need to add support for the message ofp_error_experimenter_msg format
83 continue
84 # The per-error-type code enums follow a naming conventions where
85 # the middle part of the enum name is the same as the name of the
86 # error type value (except lower-case).
87 error_code_enum_name = "ofp_" + error_type_entry_name.lower() + "_code"
88 # Look up the error code enum from the IR
89 error_code_enum = None
90 for i, enum in enumerate(of_protocol.enums):
91 if enum.name == error_code_enum_name:
92 error_code_enum = enum
93 # We don't want to generate code for the per-error-type
94 # enum so remove it from the IR
95 del of_protocol.enums[i]
96 break
97 if error_code_enum == None:
98 raise Exception("Error code enum not found: " + error_code_enum_name + "; OF version: " + str(version))
99 for error_code_entry in error_code_enum.entries:
100 # Strip off the prefix from the entry name
101 prefix_length = error_code_entry.name.find('_')
102 if prefix_length < 0:
103 raise Exception("Prefix not found for error code value " + error_code_entry.name + "; OF version: " + str(version))
104 error_code_entry_name = error_code_entry.name[prefix_length+1:]
105 # Combine the entry type name and the error code name
106 error_code_entry_name = error_type_entry_name + "_" + error_code_entry_name
107 # Combine the entry type value and the error code value
108 error_code_entry_value = (error_type_entry.value << 16) + error_code_entry.value
109 # Add the enum entry to the combined ofp_error_code
110 # Note that the "OFPEC" prefix is arbitrary. It will be stripped
111 # off again during Java code generation, but there needs to be
112 # some/any prefix
113 error_code_entries.append(OFEnumEntry("OFPEC_" + error_code_entry_name, error_code_entry_value, {}))
114 error_type_map[error_code_entry_name] = error_type_entry_name
115 # We've collected all of the entries. Now we can add the enum to the IR
116 of_protocol.enums.append(OFEnum("ofp_error_code", error_code_entries, {'wire_type': 'uint32_t'}))
117
118 # We also need to patch the of_error_msg class to combine the 16-bit error
119 # type and code fields into a single 32-bit error code field
120 error_msg = find(lambda c: c.name == "of_error_msg", of_protocol.classes)
121 if error_msg == None:
122 raise Exception("of_error_msg class not found; OF version: " + str(version))
123 err_type_index = None
124 for i, member in enumerate(error_msg.members):
125 if member.name == "err_type":
126 # Keep track of the error type index so we can remove it once
127 # we've finished iterating
128 err_type_index = i
129 elif member.name == 'code':
130 # Change the code to be a 32-bit ofp_error_code enum value
131 error_msg.members[i] = OFDataMember("code", "ofp_error_code")
132 if err_type_index == None:
133 raise Exception("err_type member of of_error_msg not found; OF version: " + str(version))
134 del error_msg.members[err_type_index]
135
136
137def gen_error_type(enum_entry):
138 return "OFErrorType." + error_type_map[enum_entry.name]
139
Andreas Wundsam27303462013-07-16 12:52:35 -0700140class JavaModel(object):
Andreas Wundsamf1949682013-09-23 14:48:31 -0700141 # registry for enums that should not be generated
142 # set(${java_enum_name})
Andreas Wundsam55b71ce2013-10-01 19:26:46 -0700143 enum_blacklist = set(("OFDefinitions", "OFPortNo", "OFVlanId"))
Andreas Wundsamf1949682013-09-23 14:48:31 -0700144 # registry for enum *entry* that should not be generated
145 # map: ${java_enum_name} -> set(${java_entry_entry_name})
Andreas Wundsam43526532013-08-01 22:03:50 -0700146 enum_entry_blacklist = defaultdict(lambda: set(), OFFlowWildcards=set([ "NW_DST_BITS", "NW_SRC_BITS", "NW_SRC_SHIFT", "NW_DST_SHIFT" ]))
Andreas Wundsamf1949682013-09-23 14:48:31 -0700147
148 # registry of interfaces that should not be generated
149 # set(java_names)
Andreas Wundsambe168f72013-08-03 22:49:35 -0700150 # OFUint structs are there for god-knows what in loci. We certainly don't need them.
151 interface_blacklist = set( ("OFUint8", "OFUint32",))
Andreas Wundsamf1949682013-09-23 14:48:31 -0700152 # registry of interface properties that should not be generated
153 # map: $java_type -> set(java_name_property)
Andreas Wundsamb1da3f62013-09-04 18:46:03 -0700154 read_blacklist = defaultdict(lambda: set(), OFExperimenter=set(('data','subtype')), OFActionExperimenter=set(('data',)))
Andreas Wundsamf1949682013-09-23 14:48:31 -0700155 # map: $java_type -> set(java_name_property)
Andreas Wundsamb1da3f62013-09-04 18:46:03 -0700156 write_blacklist = defaultdict(lambda: set(), OFOxm=set(('typeLen',)), OFAction=set(('type',)), OFInstruction=set(('type',)), OFFlowMod=set(('command', )), OFExperimenter=set(('data','subtype')), OFActionExperimenter=set(('data',)))
Andreas Wundsamf1949682013-09-23 14:48:31 -0700157 # interfaces that are virtual
Andreas Wundsam001b1822013-08-02 22:25:55 -0700158 virtual_interfaces = set(['OFOxm', 'OFInstruction', 'OFFlowMod', 'OFBsnVport' ])
Andreas Wundsam27303462013-07-16 12:52:35 -0700159
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700160 OxmMapEntry = namedtuple("OxmMapEntry", ["type_name", "value", "masked" ])
Yotam Harchole5d92972013-08-22 14:18:36 -0700161 oxm_map = { "OFOxmInPort": OxmMapEntry("OFPort", "IN_PORT", False),
162 "OFOxmInPortMasked": OxmMapEntry("OFPort", "IN_PORT", True),
163 "OFOxmInPhyPort": OxmMapEntry("OFPort", "IN_PHY_PORT", False),
164 "OFOxmInPhyPortMasked": OxmMapEntry("OFPort", "IN_PHY_PORT", True),
165 "OFOxmMetadata": OxmMapEntry("OFMetadata", "METADATA", False),
166 "OFOxmMetadataMasked": OxmMapEntry("OFMetadata", "METADATA", True),
167 "OFOxmEthDst": OxmMapEntry("MacAddress", "ETH_DST", False),
168 "OFOxmEthDstMasked": OxmMapEntry("MacAddress", "ETH_DST", True),
169 "OFOxmEthSrc": OxmMapEntry("MacAddress", "ETH_SRC", False),
170 "OFOxmEthSrcMasked": OxmMapEntry("MacAddress", "ETH_SRC", True),
171 "OFOxmEthType": OxmMapEntry("EthType", "ETH_TYPE", False),
172 "OFOxmEthTypeMasked": OxmMapEntry("EthType", "ETH_TYPE", True),
173 "OFOxmVlanVid": OxmMapEntry("VlanVid", "VLAN_VID", False),
174 "OFOxmVlanVidMasked": OxmMapEntry("VlanVid", "VLAN_VID", True),
175 "OFOxmVlanPcp": OxmMapEntry("VlanPcp", "VLAN_PCP", False),
176 "OFOxmVlanPcpMasked": OxmMapEntry("VlanPcp", "VLAN_PCP", True),
177 "OFOxmIpDscp": OxmMapEntry("IpDscp", "IP_DSCP", False),
178 "OFOxmIpDscpMasked": OxmMapEntry("IpDscp", "IP_DSCP", True),
179 "OFOxmIpEcn": OxmMapEntry("IpEcn", "IP_ECN", False),
180 "OFOxmIpEcnMasked": OxmMapEntry("IpEcn", "IP_ECN", True),
181 "OFOxmIpProto": OxmMapEntry("IpProtocol", "IP_PROTO", False),
182 "OFOxmIpProtoMasked": OxmMapEntry("IpProtocol", "IP_PROTO", True),
Yotam Harchola289d552013-09-16 10:10:40 -0700183 "OFOxmIpv4Src": OxmMapEntry("IPv4Address", "IPV4_SRC", False),
184 "OFOxmIpv4SrcMasked": OxmMapEntry("IPv4Address", "IPV4_SRC", True),
185 "OFOxmIpv4Dst": OxmMapEntry("IPv4Address", "IPV4_DST", False),
186 "OFOxmIpv4DstMasked": OxmMapEntry("IPv4Address", "IPV4_DST", True),
Yotam Harchole5d92972013-08-22 14:18:36 -0700187 "OFOxmTcpSrc": OxmMapEntry("TransportPort", "TCP_SRC", False),
188 "OFOxmTcpSrcMasked": OxmMapEntry("TransportPort", "TCP_SRC", True),
189 "OFOxmTcpDst": OxmMapEntry("TransportPort", "TCP_DST", False),
190 "OFOxmTcpDstMasked": OxmMapEntry("TransportPort", "TCP_DST", True),
191 "OFOxmUdpSrc": OxmMapEntry("TransportPort", "UDP_SRC", False),
192 "OFOxmUdpSrcMasked": OxmMapEntry("TransportPort", "UDP_SRC", True),
193 "OFOxmUdpDst": OxmMapEntry("TransportPort", "UDP_DST", False),
194 "OFOxmUdpDstMasked": OxmMapEntry("TransportPort", "UDP_DST", True),
195 "OFOxmSctpSrc": OxmMapEntry("TransportPort", "SCTP_SRC", False),
196 "OFOxmSctpSrcMasked": OxmMapEntry("TransportPort", "SCTP_SRC", True),
197 "OFOxmSctpDst": OxmMapEntry("TransportPort", "SCTP_DST", False),
198 "OFOxmSctpDstMasked": OxmMapEntry("TransportPort", "SCTP_DST", True),
199 "OFOxmIcmpv4Type": OxmMapEntry("ICMPv4Type", "ICMPV4_TYPE", False),
200 "OFOxmIcmpv4TypeMasked": OxmMapEntry("ICMPv4Type", "ICMPV4_TYPE", True),
201 "OFOxmIcmpv4Code": OxmMapEntry("ICMPv4Code", "ICMPV4_CODE", False),
202 "OFOxmIcmpv4CodeMasked": OxmMapEntry("ICMPv4Code", "ICMPV4_CODE", True),
203 "OFOxmArpOp": OxmMapEntry("ArpOpcode", "ARP_OP", False),
204 "OFOxmArpOpMasked": OxmMapEntry("ArpOpcode", "ARP_OP", True),
Yotam Harchola289d552013-09-16 10:10:40 -0700205 "OFOxmArpSpa": OxmMapEntry("IPv4Address", "ARP_SPA", False),
206 "OFOxmArpSpaMasked": OxmMapEntry("IPv4Address", "ARP_SPA", True),
207 "OFOxmArpTpa": OxmMapEntry("IPv4Address", "ARP_TPA", False),
208 "OFOxmArpTpaMasked": OxmMapEntry("IPv4Address", "ARP_TPA", True),
Yotam Harchole5d92972013-08-22 14:18:36 -0700209 "OFOxmArpSha": OxmMapEntry("MacAddress", "ARP_SHA", False),
210 "OFOxmArpShaMasked": OxmMapEntry("MacAddress", "ARP_SHA", True),
211 "OFOxmArpTha": OxmMapEntry("MacAddress", "ARP_THA", False),
212 "OFOxmArpThaMasked": OxmMapEntry("MacAddress", "ARP_THA", True),
Yotam Harchola289d552013-09-16 10:10:40 -0700213 "OFOxmIpv6Src": OxmMapEntry("IPv6Address", "IPV6_SRC", False),
214 "OFOxmIpv6SrcMasked": OxmMapEntry("IPv6Address", "IPV6_SRC", True),
215 "OFOxmIpv6Dst": OxmMapEntry("IPv6Address", "IPV6_DST", False),
216 "OFOxmIpv6DstMasked": OxmMapEntry("IPv6Address", "IPV6_DST", True),
Yotam Harchole5d92972013-08-22 14:18:36 -0700217 "OFOxmIpv6Flabel": OxmMapEntry("IPv6FlowLabel", "IPV6_FLABEL", False),
Yotam Harchola86e4252013-09-06 15:36:28 -0700218 "OFOxmIpv6FlabelMasked": OxmMapEntry("IPv6FlowLabel", "IPV6_FLABEL", True),
219 "OFOxmIcmpv6Type": OxmMapEntry("U8", "ICMPV6_TYPE", False),
220 "OFOxmIcmpv6TypeMasked": OxmMapEntry("U8", "ICMPV6_TYPE", True),
221 "OFOxmIcmpv6Code": OxmMapEntry("U8", "ICMPV6_CODE", False),
222 "OFOxmIcmpv6CodeMasked": OxmMapEntry("U8", "ICMPV6_CODE", True),
Yotam Harchola289d552013-09-16 10:10:40 -0700223 "OFOxmIpv6NdTarget": OxmMapEntry("IPv6Address", "IPV6_ND_TARGET", False),
224 "OFOxmIpv6NdTargetMasked": OxmMapEntry("IPv6Address", "IPV6_ND_TARGET", True),
Yotam Harchola86e4252013-09-06 15:36:28 -0700225 "OFOxmIpv6NdSll": OxmMapEntry("MacAddress", "IPV6_ND_SLL", False),
226 "OFOxmIpv6NdSllMasked": OxmMapEntry("MacAddress", "IPV6_ND_SLL", True),
227 "OFOxmIpv6NdTll": OxmMapEntry("MacAddress", "IPV6_ND_TLL", False),
228 "OFOxmIpv6NdTllMasked": OxmMapEntry("MacAddress", "IPV6_ND_TLL", True),
229 "OFOxmMplsLabel": OxmMapEntry("U32", "MPLS_LABEL", False),
230 "OFOxmMplsLabelMasked": OxmMapEntry("U32", "MPLS_LABEL", True),
231 "OFOxmMplsTc": OxmMapEntry("U8", "MPLS_TC", False),
Yotam Harchola11f38b2013-09-26 15:38:17 -0700232 "OFOxmMplsTcMasked": OxmMapEntry("U8", "MPLS_TC", True),
Yotam Harchol2c535582013-10-01 15:50:20 -0700233 "OFOxmBsnInPorts128": OxmMapEntry("OFBitMask128", "BSN_IN_PORTS_128", False),
234 "OFOxmBsnInPorts128Masked": OxmMapEntry("OFBitMask128", "BSN_IN_PORTS_128", True)
Yotam Harchola86e4252013-09-06 15:36:28 -0700235 }
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700236
Andreas Wundsamf1949682013-09-23 14:48:31 -0700237 # Registry of nullable properties:
238 # ${java_class_name} -> set(${java_property_name})
239 nullable_map = defaultdict(lambda: set(),
240 )
241
242 # represents a subgroup of a bitmask enum that is actualy a normal enumerable within a masked part of the enum
243 # e.g., the flags STP.* in OF1.0 port state are bit mask entries, but instead enumerables according to the mask "STP_MASK"
244 # name: a name for the group
245 # mask: java name of the enum entry that defines the mask
246 # members: set of names of the members of the group
Andreas Wundsamc1acfcc2013-09-20 13:10:20 -0700247 MaskedEnumGroup = namedtuple("MaskedEnumGroup", ("name", "mask", "members"))
248
Andreas Wundsamf1949682013-09-23 14:48:31 -0700249 # registry of MaskedEnumGroups (see above).
250 # map: ${java_enum_name}: tuple(MaskedEnumGroup)
Andreas Wundsamc1acfcc2013-09-20 13:10:20 -0700251 masked_enum_groups = defaultdict(lambda: (),
Andreas Wundsamaebe5182013-09-24 13:01:07 -0700252 OFPortState = (MaskedEnumGroup("stp_flags", mask="STP_MASK", members=set(("STP_LISTEN", "STP_LEARN", "STP_FORWARD", "STP_BLOCK"))), ),
253 OFConfigFlags = (
254 MaskedEnumGroup("frag_flags", mask="FRAG_MASK", members=set(("FRAG_NORMAL", "FRAG_DROP", "FRAG_REASM"))),
Andreas Wundsam376cef52013-09-24 13:52:18 -0700255 ),
256 OFTableConfig = (
257 MaskedEnumGroup("table_miss_flags", mask="TABLE_MISS_MASK", members=set(("TABLE_MISS_CONTROLLER", "TABLE_MISS_CONTINUE", "TABLE_MISS_DROP"))),
258 ),
Andreas Wundsamc1acfcc2013-09-20 13:10:20 -0700259 )
260
Andreas Wundsamf1949682013-09-23 14:48:31 -0700261 # represents a metadata property associated with an EnumClass
262 # name:
Rob Vaterlaus6035bf52013-09-17 19:32:38 -0700263 class OFEnumPropertyMetadata(namedtuple("OFEnumPropertyMetadata", ("name", "type", "value"))):
Andreas Wundsamf1949682013-09-23 14:48:31 -0700264 """
265 represents a metadata property associated with an Enum Class
266 @param name name of metadata property
267 @param type java_type instance describing the type
268 @value: Generator function f(entry) that generates the value
269 """
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -0700270 @property
271 def variable_name(self):
272 return self.name[0].lower() + self.name[1:]
273
274 @property
275 def getter_name(self):
Rob Vaterlausbda9ce62013-09-17 14:45:48 -0700276 prefix = "is" if self.type == java_type.boolean else "get"
277 return prefix+self.name
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -0700278
Andreas Wundsamf1949682013-09-23 14:48:31 -0700279 """ Metadata container. """
Rob Vaterlaus6035bf52013-09-17 19:32:38 -0700280 OFEnumMetadata = namedtuple("OFEnumMetadata", ("properties", "to_string"))
281
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -0700282 def gen_port_speed(enum_entry):
Andreas Wundsamf1949682013-09-23 14:48:31 -0700283 """ Generator function for OFortFeatures.PortSpeed"""
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -0700284 splits = enum_entry.name.split("_")
285 if len(splits)>=2:
286 m = re.match(r'\d+[MGTP]B', splits[1])
287 if m:
288 return "PortSpeed.SPEED_{}".format(splits[1])
289 return "PortSpeed.SPEED_NONE";
290
Rob Vaterlausbda9ce62013-09-17 14:45:48 -0700291 def gen_stp_state(enum_entry):
Andreas Wundsamf1949682013-09-23 14:48:31 -0700292 """ Generator function for OFPortState.StpState"""
Rob Vaterlausbda9ce62013-09-17 14:45:48 -0700293 splits = enum_entry.name.split("_")
294 if len(splits)>=1:
295 if splits[0] == "STP":
296 return "true"
297 return "false"
298
Andreas Wundsamf1949682013-09-23 14:48:31 -0700299 # registry for metadata properties for enums
300 # map: ${java_enum_name}: OFEnumMetadata
Rob Vaterlaus6035bf52013-09-17 19:32:38 -0700301 enum_metadata_map = defaultdict(lambda: JavaModel.OFEnumMetadata((), None),
Rob Vaterlausa049dee2013-09-18 16:18:37 -0700302 OFPortFeatures = OFEnumMetadata((OFEnumPropertyMetadata("PortSpeed", java_type.port_speed, gen_port_speed),), None),
303 OFPortState = OFEnumMetadata((OFEnumPropertyMetadata("StpState", java_type.boolean, gen_stp_state),), None),
Rob Vaterlaus4d311942013-09-24 13:41:44 -0700304 OFErrorCode = OFEnumMetadata((OFEnumPropertyMetadata("ErrorType", java_type.error_type, gen_error_type),), None),
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -0700305 )
306
Andreas Wundsam27303462013-07-16 12:52:35 -0700307 @property
308 @memoize
309 def versions(self):
310 return OrderedSet( JavaOFVersion(raw_version) for raw_version in of_g.target_version_list )
311
312 @property
313 @memoize
314 def interfaces(self):
Andreas Wundsam5204de22013-07-30 11:34:45 -0700315 version_map_per_class = collections.OrderedDict()
Andreas Wundsam27303462013-07-16 12:52:35 -0700316
317 for raw_version, of_protocol in of_g.ir.items():
318 jversion = JavaOFVersion(of_protocol.wire_version)
319
320 for of_class in of_protocol.classes:
Andreas Wundsam5204de22013-07-30 11:34:45 -0700321 if not of_class.name in version_map_per_class:
322 version_map_per_class[of_class.name] = collections.OrderedDict()
323
Andreas Wundsam27303462013-07-16 12:52:35 -0700324 version_map_per_class[of_class.name][jversion] = of_class
325
326 interfaces = []
327 for class_name, version_map in version_map_per_class.items():
328 interfaces.append(JavaOFInterface(class_name, version_map))
329
Andreas Wundsambe168f72013-08-03 22:49:35 -0700330 interfaces = [ i for i in interfaces if i.name not in self.interface_blacklist ]
331
Andreas Wundsam27303462013-07-16 12:52:35 -0700332 return interfaces
333
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700334 @memoize
335 def interface_by_name(self, name):
336 return find(lambda i: erase_type_annotation(i.name) == erase_type_annotation(name), self.interfaces)
337
Andreas Wundsam27303462013-07-16 12:52:35 -0700338 @property
339 @memoize
Andreas Wundsam001b1822013-08-02 22:25:55 -0700340 def all_classes(self):
341 return [clazz for interface in self.interfaces for clazz in interface.versioned_classes]
342
343 @property
344 @memoize
Andreas Wundsam27303462013-07-16 12:52:35 -0700345 def enums(self):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700346 name_version_enum_map = OrderedDefaultDict(lambda: OrderedDict())
Andreas Wundsam27303462013-07-16 12:52:35 -0700347
348 for version in self.versions:
349 of_protocol = of_g.ir[version.int_version]
350 for enum in of_protocol.enums:
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700351 name_version_enum_map[enum.name][version] = enum
Andreas Wundsam27303462013-07-16 12:52:35 -0700352
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700353 enums = [ JavaEnum(name, version_enum_map) for name, version_enum_map,
354 in name_version_enum_map.items() ]
Andreas Wundsam27303462013-07-16 12:52:35 -0700355
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700356 # inelegant - need java name here
357 enums = [ enum for enum in enums if enum.name not in self.enum_blacklist ]
Andreas Wundsam27303462013-07-16 12:52:35 -0700358 return enums
359
360 @memoize
361 def enum_by_name(self, name):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700362 res = find(lambda e: e.name == name, self.enums)
363 if not res:
Andreas Wundsam27303462013-07-16 12:52:35 -0700364 raise KeyError("Could not find enum with name %s" % name)
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700365 return res
Andreas Wundsam27303462013-07-16 12:52:35 -0700366
Andreas Wundsam5204de22013-07-30 11:34:45 -0700367 @property
368 @memoize
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700369 def of_factories(self):
Yotam Harchol791e4882013-09-05 16:32:56 -0700370 prefix = "org.projectfloodlight.openflow.protocol"
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700371
372 factories = OrderedDict()
373
374 sub_factory_classes = ("OFAction", "OFInstruction", "OFMeterBand", "OFOxm", "OFQueueProp")
375 for base_class in sub_factory_classes:
376 package = base_class[2:].lower()
377 remove_prefix = base_class[2].lower() + base_class[3:]
378
379 # HACK need to have a better way to deal with parameterized base classes
380 annotated_base_class = base_class + "<?>" if base_class == "OFOxm" else base_class
381
382 factories[base_class] = OFFactory(package="%s.%s" % (prefix, package),
Andreas Wundsamcb17b232013-09-28 19:05:36 -0700383 name=base_class + "s", members=[], remove_prefix=remove_prefix, base_class=annotated_base_class, sub_factories={}, xid_generator=False)
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700384
385 factories[""] = OFFactory(
386 package=prefix,
Andreas Wundsam5204de22013-07-30 11:34:45 -0700387 name="OFFactory",
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700388 remove_prefix="",
389 members=[], base_class="OFMessage", sub_factories=OrderedDict(
Andreas Wundsamcb17b232013-09-28 19:05:36 -0700390 ("{}{}s".format(n[2].lower(), n[3:]), "{}s".format(n)) for n in sub_factory_classes ),
391 xid_generator=True)
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700392
393 for i in self.interfaces:
394 for n, factory in factories.items():
395 if n == "":
396 factory.members.append(i)
397 break
398 else:
399 super_class = self.interface_by_name(n)
400 if i.is_instance_of(super_class):
401 factory.members.append(i)
402 break
403 return factories.values()
Yotam Harchol595c6442013-09-27 16:29:08 -0700404
405 @memoize
406 def factory_of(self, interface):
407 for factory in self.of_factories:
408 if interface in factory.members:
409 return factory
410 return None
Andreas Wundsam5204de22013-07-30 11:34:45 -0700411
412 def generate_class(self, clazz):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700413 """ return wether or not to generate implementation class clazz.
414 Now true for everything except OFTableModVer10.
415 @param clazz JavaOFClass instance
416 """
Andreas Wundsam43526532013-08-01 22:03:50 -0700417 if clazz.interface.name.startswith("OFMatchV"):
418 return True
Andreas Wundsam001b1822013-08-02 22:25:55 -0700419 elif clazz.name == "OFTableModVer10":
420 # tablemod ver 10 is a hack and has no oftype defined
421 return False
Andreas Wundsam5204de22013-07-30 11:34:45 -0700422 if loxi_utils.class_is_message(clazz.interface.c_name):
423 return True
424 if loxi_utils.class_is_oxm(clazz.interface.c_name):
425 return True
Andreas Wundsam43526532013-08-01 22:03:50 -0700426 if loxi_utils.class_is_action(clazz.interface.c_name):
427 return True
428 if loxi_utils.class_is_instruction(clazz.interface.c_name):
429 return True
Andreas Wundsam5204de22013-07-30 11:34:45 -0700430 else:
Andreas Wundsam001b1822013-08-02 22:25:55 -0700431 return True
Andreas Wundsam5204de22013-07-30 11:34:45 -0700432
433
Andreas Wundsamcb17b232013-09-28 19:05:36 -0700434class OFFactory(namedtuple("OFFactory", ("package", "name", "members", "remove_prefix", "base_class", "sub_factories", "xid_generator"))):
Andreas Wundsam5204de22013-07-30 11:34:45 -0700435 @property
436 def factory_classes(self):
437 return [ OFFactoryClass(
Yotam Harchol791e4882013-09-05 16:32:56 -0700438 package="org.projectfloodlight.openflow.protocol.ver{}".format(version.of_version),
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700439 name="{}Ver{}".format(self.name, version.of_version),
Andreas Wundsam5204de22013-07-30 11:34:45 -0700440 interface=self,
441 version=version
442 ) for version in model.versions ]
443
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700444 def method_name(self, member, builder=True):
445 n = member.variable_name
446 if n.startswith(self.remove_prefix):
447 n = n[len(self.remove_prefix):]
448 n = n[0].lower() + n[1:]
449 if builder:
450 return "build" + n[0].upper() + n[1:]
451 else:
452 return n
Yotam Harchol595c6442013-09-27 16:29:08 -0700453
454 def of_version(self, version):
455 for fc in self.factory_classes:
456 if fc.version == version:
457 return fc
458 return None
Andreas Wundsam5204de22013-07-30 11:34:45 -0700459
460OFGenericClass = namedtuple("OFGenericClass", ("package", "name"))
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700461class OFFactoryClass(namedtuple("OFFactoryClass", ("package", "name", "interface", "version"))):
462 @property
463 def base_class(self):
464 return self.interface.base_class
465
466 @property
467 def versioned_base_class(self):
468 base_class_interface = model.interface_by_name(self.interface.base_class)
469 if base_class_interface and base_class_interface.has_version(self.version):
470 return base_class_interface.versioned_class(self.version)
471 else:
472 return None
Andreas Wundsam5204de22013-07-30 11:34:45 -0700473
Andreas Wundsam27303462013-07-16 12:52:35 -0700474model = JavaModel()
475
476#######################################################################
477### OFVersion
478#######################################################################
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700479
480class JavaOFVersion(object):
481 """ Models a version of OpenFlow. contains methods to convert the internal
482 Loxi version to a java constant / a string """
483 def __init__(self, int_version):
484 self.int_version = int(int_version)
485
486 @property
487 def of_version(self):
488 return "1" + str(int(self.int_version) - 1)
489
490 @property
491 def constant_version(self):
492 return "OF_" + self.of_version
493
Andreas Wundsam27303462013-07-16 12:52:35 -0700494 def __repr__(self):
495 return "JavaOFVersion(%d)" % self.int_version
496
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700497 def __str__(self):
498 return of_g.param_version_names[self.int_version]
499
Andreas Wundsam27303462013-07-16 12:52:35 -0700500 def __hash__(self):
501 return hash(self.int_version)
502
503 def __eq__(self, other):
504 if other is None or type(self) != type(other):
505 return False
506 return (self.int_version,) == (other.int_version,)
507
508#######################################################################
509### Interface
510#######################################################################
511
512class JavaOFInterface(object):
513 """ Models an OpenFlow Message class for the purpose of the java class.
514 Version agnostic, in contrast to the loxi_ir python model.
515 """
516 def __init__(self, c_name, version_map):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700517 """"
518 @param c_name: loxi style name (e.g., of_flow_add)
519 @param version_map map of { JavaOFVersion: OFClass (from loxi_ir) }
520 """
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700521 self.c_name = c_name
Andreas Wundsam27303462013-07-16 12:52:35 -0700522 self.version_map = version_map
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700523 # name: the Java Type name, e.g., OFFlowAdd
Andreas Wundsam001b1822013-08-02 22:25:55 -0700524 self.name = java_type.name_c_to_caps_camel(c_name) if c_name != "of_header" else "OFMessage"
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700525 # variable_name name to use for variables of this type. i.e., flowAdd
Andreas Wundsam5204de22013-07-30 11:34:45 -0700526 self.variable_name = self.name[2].lower() + self.name[3:]
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700527 self.title_name = self.variable_name[0].upper() + self.variable_name[1:]
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700528 # name for use in constants: FLOW_ADD
Andreas Wundsam27303462013-07-16 12:52:35 -0700529 self.constant_name = c_name.upper().replace("OF_", "")
530
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700531 pck_suffix, parent_interface, self.type_annotation = self.class_info()
Yotam Harchol791e4882013-09-05 16:32:56 -0700532 self.package = "org.projectfloodlight.openflow.protocol.%s" % pck_suffix if pck_suffix else "org.projectfloodlight.openflow.protocol"
Andreas Wundsam27303462013-07-16 12:52:35 -0700533 if self.name != parent_interface:
534 self.parent_interface = parent_interface
535 else:
536 self.parent_interface = None
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700537
Andreas Wundsama705c6b2013-09-04 11:21:09 -0700538 @property
539 @memoize
540 def all_parent_interfaces(self):
541 return [ "OFObject" ] + \
542 ([ self.parent_interface ] if self.parent_interface else [] )+ \
543 self.additional_parent_interfaces
544 @property
545 @memoize
546 def additional_parent_interfaces(self):
547 if loxi_utils.class_is_message(self.c_name) and not self.is_virtual:
548 m = re.match(r'(.*)Request$', self.name)
549 if m:
550 reply_name = m.group(1) + "Reply"
551 if model.interface_by_name(reply_name):
552 return ["OFRequest<%s>" % reply_name ]
553 return []
554
555
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700556 def is_instance_of(self, other_class):
557 if self == other_class:
558 return True
559 parent = self.super_class
560 if parent is None:
561 return False
562 else:
563 return parent.is_instance_of(other_class)
564
565 @property
566 def super_class(self):
567 if not self.parent_interface:
568 return None
569 else:
570 return model.interface_by_name(self.parent_interface)
571
572
573 def inherited_declaration(self, type_spec="?"):
574 if self.type_annotation:
575 return "%s<%s>" % (self.name, type_spec)
576 else:
577 return "%s" % self.name
578
579 @property
580 def type_variable(self):
581 if self.type_annotation:
582 return "<T>"
583 else:
584 return "";
585
Andreas Wundsam27303462013-07-16 12:52:35 -0700586 def class_info(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700587 """ return tuple of (package_prefix, parent_class) for the current JavaOFInterface"""
588 # FIXME: This duplicates inheritance information that is now available in the loxi_ir
589 # model (note, that the loxi model is on versioned classes). Should check/infer the
590 # inheritance information from the versioned lox_ir classes.
Andreas Wundsamd6b0cb02013-09-28 18:55:56 -0700591 if re.match(r'OFStatsRequest$', self.name):
592 return ("", "OFMessage", "T extends OFStatsReply")
593 elif re.match(r'OF.+StatsRequest$', self.name):
594 return ("", "OFStatsRequest<{}>".format(re.sub(r'Request$', 'Reply', self.name)), None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700595 elif re.match(r'OF.+StatsReply$', self.name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700596 return ("", "OFStatsReply", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700597 elif re.match(r'OFFlow(Add|Modify(Strict)?|Delete(Strict)?)$', self.name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700598 return ("", "OFFlowMod", None)
Andreas Wundsama705c6b2013-09-04 11:21:09 -0700599 elif loxi_utils.class_is_message(self.c_name) and re.match(r'OFBsn.+$', self.name) and self.name != "OFBsnHeader":
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700600 return ("", "OFBsnHeader", None)
Andreas Wundsama705c6b2013-09-04 11:21:09 -0700601 elif loxi_utils.class_is_message(self.c_name) and re.match(r'OFNicira.+$', self.name) and self.name != "OFNiciraHeader":
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700602 return ("", "OFNiciraHeader", None)
Andreas Wundsamb1da3f62013-09-04 18:46:03 -0700603 elif self.name == "OFBsnHeader" or self.name =="OFNiciraHeader":
604 return ("", "OFExperimenter", None)
Andreas Wundsam43526532013-08-01 22:03:50 -0700605 elif re.match(r'OFMatch.*', self.name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700606 return ("", "Match", None)
Andreas Wundsam5204de22013-07-30 11:34:45 -0700607 elif loxi_utils.class_is_message(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700608 return ("", "OFMessage", None)
Andreas Wundsam5204de22013-07-30 11:34:45 -0700609 elif loxi_utils.class_is_action(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700610 if re.match(r'OFActionBsn.+', self.name):
611 return ("action", "OFActionBsn", None)
612 elif re.match(r'OFActionNicira.+', self.name):
613 return ("action", "OFActionNicira", None)
Andreas Wundsamb1da3f62013-09-04 18:46:03 -0700614 elif self.name == "OFActionBsn" or self.name == "OFActionNicira":
615 return ("action", "OFActionExperimenter", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700616 else:
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700617 return ("action", "OFAction", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700618 elif re.match(r'OFBsnVport.+$', self.name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700619 return ("", "OFBsnVport", None)
620 elif self.name == "OFOxm":
621 return ("oxm", None, "T extends OFValueType<T>")
Andreas Wundsam5204de22013-07-30 11:34:45 -0700622 elif loxi_utils.class_is_oxm(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700623 if self.name in model.oxm_map:
624 return ("oxm", "OFOxm<%s>" % model.oxm_map[self.name].type_name, None)
625 else:
626 return ("oxm", "OFOxm", None)
Andreas Wundsam5204de22013-07-30 11:34:45 -0700627 elif loxi_utils.class_is_instruction(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700628 return ("instruction", "OFInstruction", None)
Andreas Wundsam5204de22013-07-30 11:34:45 -0700629 elif loxi_utils.class_is_meter_band(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700630 return ("meterband", "OFMeterBand", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700631 elif loxi_utils.class_is_queue_prop(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700632 return ("queueprop", "OFQueueProp", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700633 elif loxi_utils.class_is_hello_elem(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700634 return ("", "OFHelloElem", None)
Andreas Wundsam27303462013-07-16 12:52:35 -0700635 else:
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700636 return ("", None, None)
637
638 @property
639 @memoize
640 def writeable_members(self):
641 return [ m for m in self.members if m.is_writeable ]
Andreas Wundsam27303462013-07-16 12:52:35 -0700642
Andreas Wundsamcb17b232013-09-28 19:05:36 -0700643 @memoize
644 def member_by_name(self, name):
645 return find(lambda m: m.name == name, self.members)
646
Andreas Wundsam27303462013-07-16 12:52:35 -0700647 @property
648 @memoize
649 def members(self):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700650 return self.ir_model_members + self.virtual_members
651
652 @property
653 @memoize
654 def ir_model_members(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700655 """return a list of all members to be exposed by this interface. Corresponds to
656 the union of the members of the vesioned classes without length, fieldlength
657 and pads (those are handled automatically during (de)serialization and not exposed"""
Andreas Wundsam27303462013-07-16 12:52:35 -0700658 all_versions = []
659 member_map = collections.OrderedDict()
660
661 for (version, of_class) in self.version_map.items():
662 for of_member in of_class.members:
663 if isinstance(of_member, OFLengthMember) or \
664 isinstance(of_member, OFFieldLengthMember) or \
665 isinstance(of_member, OFPadMember):
666 continue
667 if of_member.name not in member_map:
668 member_map[of_member.name] = JavaMember.for_of_member(self, of_member)
669
Andreas Wundsamb1da3f62013-09-04 18:46:03 -0700670 return tuple(m for m in member_map.values() if m.name not in model.read_blacklist[self.name])
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700671
672 @property
673 def virtual_members(self):
Andreas Wundsama0981022013-10-02 18:15:06 -0700674 virtual_members = []
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700675 if self.name == "OFOxm":
Andreas Wundsama0981022013-10-02 18:15:06 -0700676 virtual_members += [
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700677 JavaVirtualMember(self, "value", java_type.generic_t),
678 JavaVirtualMember(self, "mask", java_type.generic_t),
679 JavaVirtualMember(self, "matchField", java_type.make_match_field_jtype("T")),
680 JavaVirtualMember(self, "masked", java_type.boolean),
Andreas Wundsama0981022013-10-02 18:15:06 -0700681 ]
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700682 elif self.parent_interface and self.parent_interface.startswith("OFOxm"):
683 field_type = java_type.make_match_field_jtype(model.oxm_map[self.name].type_name) \
684 if self.name in model.oxm_map \
685 else java_type.make_match_field_jtype()
686
Andreas Wundsama0981022013-10-02 18:15:06 -0700687 virtual_members += [
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700688 JavaVirtualMember(self, "matchField", field_type),
689 JavaVirtualMember(self, "masked", java_type.boolean),
Andreas Wundsama0981022013-10-02 18:15:06 -0700690 ]
691 if not find(lambda x: x.name == "mask", self.ir_model_members):
692 virtual_members.append(JavaVirtualMember(self, "mask", find(lambda x: x.name == "value", self.ir_model_members).java_type))
693
694 if not find(lambda m: m.name == "version", self.ir_model_members):
695 virtual_members.append(JavaVirtualMember(self, "version", java_type.of_version))
696
697 return tuple(virtual_members)
Andreas Wundsam27303462013-07-16 12:52:35 -0700698
699 @property
700 @memoize
701 def is_virtual(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700702 """ Is this interface virtual. If so, do not generate a builder interface """
Andreas Wundsam001b1822013-08-02 22:25:55 -0700703 return self.name in model.virtual_interfaces or all(ir_class.virtual for ir_class in self.version_map.values())
Andreas Wundsam27303462013-07-16 12:52:35 -0700704
705 @property
Andreas Wundsam5204de22013-07-30 11:34:45 -0700706 def is_universal(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700707 """ Is this interface universal, i.e., does it exist in all OF versions? """
Andreas Wundsam5204de22013-07-30 11:34:45 -0700708 return len(self.all_versions) == len(model.versions)
709
710 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700711 @memoize
712 def all_versions(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700713 """ return list of all versions that this interface exists in """
Andreas Wundsam27303462013-07-16 12:52:35 -0700714 return self.version_map.keys()
715
Andreas Wundsam5204de22013-07-30 11:34:45 -0700716 def has_version(self, version):
717 return version in self.version_map
718
Andreas Wundsam27303462013-07-16 12:52:35 -0700719 def versioned_class(self, version):
720 return JavaOFClass(self, version, self.version_map[version])
721
722 @property
723 @memoize
724 def versioned_classes(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700725 return [ self.versioned_class(version) for version in self.all_versions ]
726
727#######################################################################
728### (Versioned) Classes
729#######################################################################
730
731class JavaOFClass(object):
732 """ Models an OpenFlow Message class for the purpose of the java class.
Andreas Wundsamd40ddbf2013-07-25 15:40:47 -0700733 Version specific child of a JavaOFInterface
Andreas Wundsam27303462013-07-16 12:52:35 -0700734 """
735 def __init__(self, interface, version, ir_class):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700736 """
737 @param interface JavaOFInterface instance of the parent interface
738 @param version JavaOFVersion
739 @param ir_class OFClass from loxi_ir
740 """
Andreas Wundsam27303462013-07-16 12:52:35 -0700741 self.interface = interface
742 self.ir_class = ir_class
743 self.c_name = self.ir_class.name
744 self.version = version
745 self.constant_name = self.c_name.upper().replace("OF_", "")
Yotam Harchol791e4882013-09-05 16:32:56 -0700746 self.package = "org.projectfloodlight.openflow.protocol.ver%s" % version.of_version
Andreas Wundsame916d6f2013-07-30 11:33:58 -0700747 self.generated = False
748
749 @property
750 @memoize
751 def unit_test(self):
Yotam Harchol466b3212013-08-15 12:14:46 -0700752 return JavaUnitTestSet(self)
Andreas Wundsam27303462013-07-16 12:52:35 -0700753
754 @property
755 def name(self):
756 return "%sVer%s" % (self.interface.name, self.version.of_version)
757
758 @property
Andreas Wundsam5204de22013-07-30 11:34:45 -0700759 def variable_name(self):
760 return self.name[3:]
761
762 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700763 def length(self):
764 if self.is_fixed_length:
765 return self.min_length
766 else:
767 raise Exception("No fixed length for class %s, version %s" % (self.name, self.version))
768
769 @property
770 def min_length(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700771 """ @return the minimum wire length of an instance of this class in bytes """
Andreas Wundsam27303462013-07-16 12:52:35 -0700772 id_tuple = (self.ir_class.name, self.version.int_version)
773 return of_g.base_length[id_tuple] if id_tuple in of_g.base_length else -1
774
775 @property
776 def is_fixed_length(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700777 """ true iff this class serializes to a fixed length on the wire """
Andreas Wundsambbf85e12013-09-13 14:18:01 -0700778 return (self.ir_class.name, self.version.int_version) in of_g.is_fixed_length and \
779 not self.is_virtual
Andreas Wundsam27303462013-07-16 12:52:35 -0700780
781 def all_properties(self):
782 return self.interface.members
783
784 def get_member(self, name):
785 for m in self.members:
786 if m.name == name:
787 return m
788
789 @property
790 @memoize
791 def data_members(self):
792 return [ prop for prop in self.members if prop.is_data ]
793
794 @property
795 @memoize
796 def fixed_value_members(self):
797 return [ prop for prop in self.members if prop.is_fixed_value ]
798
799 @property
800 @memoize
801 def public_members(self):
802 return [ prop for prop in self.members if prop.is_public ]
803
804 @property
805 @memoize
806 def members(self):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700807 return self.ir_model_members + self.virtual_members
808
809 @property
Andreas Wundsama0981022013-10-02 18:15:06 -0700810 @memoize
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700811 def ir_model_members(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700812 members = [ JavaMember.for_of_member(self, of_member) for of_member in self.ir_class.members ]
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700813 return tuple(members)
814
815 @property
816 def virtual_members(self):
Andreas Wundsama0981022013-10-02 18:15:06 -0700817 virtual_members = []
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700818 if self.interface.parent_interface and self.interface.parent_interface.startswith("OFOxm"):
819 if self.interface.name in model.oxm_map:
820 oxm_entry = model.oxm_map[self.interface.name]
Andreas Wundsama0981022013-10-02 18:15:06 -0700821 virtual_members += [
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700822 JavaVirtualMember(self, "matchField", java_type.make_match_field_jtype(oxm_entry.type_name), "MatchField.%s" % oxm_entry.value),
823 JavaVirtualMember(self, "masked", java_type.boolean, "true" if oxm_entry.masked else "false"),
Andreas Wundsama0981022013-10-02 18:15:06 -0700824 ]
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700825 else:
Andreas Wundsama0981022013-10-02 18:15:06 -0700826 virtual_members += [
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700827 JavaVirtualMember(self, "matchField", java_type.make_match_field_jtype(), "null"),
828 JavaVirtualMember(self, "masked", java_type.boolean, "false"),
Andreas Wundsama0981022013-10-02 18:15:06 -0700829 ]
830
831 if not find(lambda m: m.name == "version", self.ir_model_members):
832 virtual_members.append(JavaVirtualMember(self, "version", java_type.of_version, "OFVersion.%s" % self.version.constant_version))
833
834 return tuple(virtual_members)
Andreas Wundsam27303462013-07-16 12:52:35 -0700835
836 def all_versions(self):
837 return [ JavaOFVersion(int_version)
838 for int_version in of_g.unified[self.c_name]
839 if int_version != 'union' and int_version != 'object_id' ]
840
841 def version_is_inherited(self, version):
842 return 'use_version' in of_g.unified[self.ir_class.name][version.int_version]
843
844 def inherited_from(self, version):
845 return JavaOFVersion(of_g.unified[self.ir_class.name][version.int_version]['use_version'])
846
847 @property
848 def is_virtual(self):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700849 return self.ir_class.virtual # type_maps.class_is_virtual(self.c_name) or self.ir_class.virtual
850
851 @property
852 def discriminator(self):
853 return find(lambda m: isinstance(m, OFDiscriminatorMember), self.ir_class.members)
Andreas Wundsam27303462013-07-16 12:52:35 -0700854
855 @property
856 def is_extension(self):
857 return type_maps.message_is_extension(self.c_name, -1)
858
Andreas Wundsam758c9cc2013-08-01 22:16:06 -0700859 @property
860 def align(self):
861 return int(self.ir_class.params['align']) if 'align' in self.ir_class.params else 0
862
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700863 @property
864 @memoize
865 def superclass(self):
866 return find(lambda c: c.version == self.version and c.c_name == self.ir_class.superclass, model.all_classes)
867
868 @property
869 @memoize
870 def subclasses(self):
871 return [ c for c in model.all_classes if c.version == self.version and c.ir_class.superclass == self.c_name ]
872
Andreas Wundsam27303462013-07-16 12:52:35 -0700873#######################################################################
874### Member
875#######################################################################
876
877
878class JavaMember(object):
879 """ Models a property (member) of an openflow class. """
880 def __init__(self, msg, name, java_type, member):
881 self.msg = msg
882 self.name = name
883 self.java_type = java_type
884 self.member = member
885 self.c_name = self.member.name if(hasattr(self.member, "name")) else ""
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700886
887 @property
888 def title_name(self):
889 return self.name[0].upper() + self.name[1:]
890
891 @property
892 def constant_name(self):
893 return self.c_name.upper()
894
895 @property
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700896 def getter_name(self):
897 return ("is" if self.java_type.public_type == "boolean" else "get") + self.title_name
898
899 @property
900 def setter_name(self):
901 return "set" + self.title_name
902
903 @property
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700904 def default_name(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700905 if self.is_fixed_value:
906 return self.constant_name
907 else:
908 return "DEFAULT_"+self.constant_name
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700909
910 @property
911 def default_value(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700912 if self.is_fixed_value:
913 return self.enum_value
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700914 else:
Andreas Wundsam142dd2a2013-09-23 14:47:37 -0700915 default = self.java_type.default_op(self.msg.version)
916 if default == "null" and not self.is_nullable:
917 return None
918 else:
919 return default
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700920
921 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700922 def enum_value(self):
923 if self.name == "version":
924 return "OFVersion.%s" % self.msg.version.constant_version
925
926 java_type = self.java_type.public_type;
927 try:
928 global model
929 enum = model.enum_by_name(java_type)
930 entry = enum.entry_by_version_value(self.msg.version, self.value)
931 return "%s.%s" % ( enum.name, entry.name)
932 except KeyError, e:
933 print e.message
934 return self.value
935
936 @property
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700937 def is_pad(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700938 return isinstance(self.member, OFPadMember)
939
940 def is_type_value(self, version=None):
941 if(version==None):
942 return any(self.is_type_value(version) for version in self.msg.all_versions)
943 try:
944 return self.c_name in get_type_values(self.msg.c_name, version.int_version)
945 except:
946 return False
947
948 @property
949 def is_field_length_value(self):
950 return isinstance(self.member, OFFieldLengthMember)
951
952 @property
Andreas Wundsam001b1822013-08-02 22:25:55 -0700953 def is_discriminator(self):
954 return isinstance(self.member, OFDiscriminatorMember)
955
956 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700957 def is_length_value(self):
958 return isinstance(self.member, OFLengthMember)
959
960 @property
961 def is_public(self):
962 return not (self.is_pad or self.is_length_value)
963
964 @property
965 def is_data(self):
966 return isinstance(self.member, OFDataMember) and self.name != "version"
967
968 @property
969 def is_fixed_value(self):
970 return hasattr(self.member, "value") or self.name == "version" \
971 or ( self.name == "length" and self.msg.is_fixed_length) \
972 or ( self.name == "len" and self.msg.is_fixed_length)
973
974 @property
975 def value(self):
976 if self.name == "version":
977 return self.msg.version.int_version
978 elif self.name == "length" or self.name == "len":
979 return self.msg.length
Andreas Wundsam27303462013-07-16 12:52:35 -0700980 else:
Andreas Wundsam001b1822013-08-02 22:25:55 -0700981 return self.java_type.format_value(self.member.value)
982
983 @property
984 def priv_value(self):
985 if self.name == "version":
Andreas Wundsam22ba3af2013-10-04 16:00:30 -0700986 return self.java_type.format_value(self.msg.version.int_version, pub_type=False)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700987 elif self.name == "length" or self.name == "len":
Andreas Wundsam22ba3af2013-10-04 16:00:30 -0700988 return self.java_type.format_value(self.msg.length, pub_type=False)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700989 else:
990 return self.java_type.format_value(self.member.value, pub_type=False)
991
Andreas Wundsam27303462013-07-16 12:52:35 -0700992
993 @property
994 def is_writeable(self):
995 return self.is_data and not self.name in model.write_blacklist[self.msg.name]
996
997 def get_type_value_info(self, version):
998 return get_type_values(msg.c_name, version.int_version)[self.c_name]
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700999
1000 @property
1001 def length(self):
Andreas Wundsam27303462013-07-16 12:52:35 -07001002 if hasattr(self.member, "length"):
1003 return self.member.length
1004 else:
Andreas Wundsam5204de22013-07-30 11:34:45 -07001005 count, base = loxi_utils.type_dec_to_count_base(self.member.type)
Andreas Wundsam27303462013-07-16 12:52:35 -07001006 return of_g.of_base_types[base]['bytes'] * count
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001007
1008 @staticmethod
Andreas Wundsam27303462013-07-16 12:52:35 -07001009 def for_of_member(java_class, member):
1010 if isinstance(member, OFPadMember):
1011 return JavaMember(None, "", None, member)
1012 else:
1013 if member.name == 'len':
1014 name = 'length'
Andreas Wundsam2be7da52013-08-22 07:34:25 -07001015 elif member.name == 'value_mask':
1016 name = 'mask'
Andreas Wundsam27303462013-07-16 12:52:35 -07001017 else:
1018 name = java_type.name_c_to_camel(member.name)
1019 j_type = java_type.convert_to_jtype(java_class.c_name, member.name, member.oftype)
1020 return JavaMember(java_class, name, j_type, member)
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001021
1022 @property
1023 def is_universal(self):
Andreas Wundsam27303462013-07-16 12:52:35 -07001024 if not self.msg.c_name in of_g.unified:
1025 print("%s not self.unified" % self.msg.c_name)
1026 return False
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001027 for version in of_g.unified[self.msg.c_name]:
1028 if version == 'union' or version =='object_id':
1029 continue
1030 if 'use_version' in of_g.unified[self.msg.c_name][version]:
1031 continue
1032
Andreas Wundsam27303462013-07-16 12:52:35 -07001033 if not self.member.name in (f['name'] for f in of_g.unified[self.msg.c_name][version]['members']):
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001034 return False
1035 return True
1036
Andreas Wundsam2be7da52013-08-22 07:34:25 -07001037 @property
1038 def is_virtual(self):
1039 return False
1040
Andreas Wundsam27303462013-07-16 12:52:35 -07001041 def __hash__(self):
1042 return hash(self.name)
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001043
Andreas Wundsam27303462013-07-16 12:52:35 -07001044 def __eq__(self, other):
1045 if other is None or type(self) != type(other):
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001046 return False
Andreas Wundsam27303462013-07-16 12:52:35 -07001047 return (self.name,) == (other.name,)
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001048
Andreas Wundsamf1949682013-09-23 14:48:31 -07001049 @property
1050 def is_nullable(self):
1051 return self.name in model.nullable_map[self.msg.name]
1052
1053
Andreas Wundsam2be7da52013-08-22 07:34:25 -07001054class JavaVirtualMember(JavaMember):
1055 """ Models a virtual property (member) of an openflow class that is not backed by a loxi ir member """
1056 def __init__(self, msg, name, java_type, value=None):
1057 JavaMember.__init__(self, msg, name, java_type, member=None)
1058 self._value = value
1059
1060 @property
1061 def is_fixed_value(self):
1062 return True
1063
1064 @property
1065 def value(self):
1066 return self._value
1067
1068 @property
1069 def priv_value(self):
1070 return self._value
1071
1072
1073 @property
1074 def is_universal(self):
1075 return True
1076
1077 @property
1078 def is_virtual(self):
1079 return True
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001080
1081#######################################################################
1082### Unit Test
1083#######################################################################
1084
Yotam Harchol466b3212013-08-15 12:14:46 -07001085class JavaUnitTestSet(object):
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001086 def __init__(self, java_class):
1087 self.java_class = java_class
Yotam Harchol466b3212013-08-15 12:14:46 -07001088 first_data_file_name = "of{version}/{name}.data".format(version=java_class.version.of_version,
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001089 name=java_class.c_name[3:])
Yotam Harchol466b3212013-08-15 12:14:46 -07001090 data_file_template = "of{version}/{name}.".format(version=java_class.version.of_version,
1091 name=java_class.c_name[3:]) + "{i}.data"
1092 test_class_name = self.java_class.name + "Test"
1093 self.test_units = []
1094 if test_data.exists(first_data_file_name):
1095 self.test_units.append(JavaUnitTest(java_class, first_data_file_name, test_class_name))
1096 i = 1
1097 while test_data.exists(data_file_template.format(i=i)):
1098 self.test_units.append(JavaUnitTest(java_class, data_file_template.format(i=i), test_class_name + str(i)))
1099 i = i + 1
Andreas Wundsamf1949682013-09-23 14:48:31 -07001100
Yotam Harchol466b3212013-08-15 12:14:46 -07001101 @property
1102 def package(self):
1103 return self.java_class.package
1104
1105 @property
1106 def has_test_data(self):
1107 return len(self.test_units) > 0
1108
1109 @property
1110 def length(self):
1111 return len(self.test_units)
Andreas Wundsamf1949682013-09-23 14:48:31 -07001112
Yotam Harchol466b3212013-08-15 12:14:46 -07001113 def get_test_unit(self, i):
1114 return self.test_units[i]
1115
1116
1117class JavaUnitTest(object):
1118 def __init__(self, java_class, file_name=None, test_class_name=None):
1119 self.java_class = java_class
1120 if file_name is None:
1121 self.data_file_name = "of{version}/{name}.data".format(version=java_class.version.of_version,
1122 name=java_class.c_name[3:])
1123 else:
1124 self.data_file_name = file_name
1125 if test_class_name is None:
1126 self.test_class_name = self.java_class.name + "Test"
1127 else:
1128 self.test_class_name = test_class_name
Andreas Wundsamf1949682013-09-23 14:48:31 -07001129
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001130 @property
1131 def package(self):
1132 return self.java_class.package
1133
1134 @property
1135 def name(self):
Yotam Harchol466b3212013-08-15 12:14:46 -07001136 return self.test_class_name
Yotam Harchol595c6442013-09-27 16:29:08 -07001137
1138 @property
1139 def interface(self):
1140 return self.java_class.interface
1141
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001142 @property
1143 def has_test_data(self):
1144 return test_data.exists(self.data_file_name)
1145
1146 @property
1147 @memoize
1148 def test_data(self):
1149 return test_data.read(self.data_file_name)
1150
1151
Andreas Wundsam27303462013-07-16 12:52:35 -07001152#######################################################################
1153### Enums
1154#######################################################################
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001155
Andreas Wundsam27303462013-07-16 12:52:35 -07001156class JavaEnum(object):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001157 def __init__(self, c_name, version_enum_map):
Andreas Wundsam27303462013-07-16 12:52:35 -07001158 self.c_name = c_name
Andreas Wundsambe168f72013-08-03 22:49:35 -07001159
1160 if c_name == "of_stats_types":
1161 self.name = "OFStatsType"
1162 else:
1163 self.name = "OF" + java_type.name_c_to_caps_camel("_".join(c_name.split("_")[1:]))
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001164
Andreas Wundsam27303462013-07-16 12:52:35 -07001165 # Port_features has constants that start with digits
1166 self.name_prefix = "PF_" if self.name == "OFPortFeatures" else ""
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001167
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001168 self.version_enums = version_enum_map
1169
1170 entry_name_version_value_map = OrderedDefaultDict(lambda: OrderedDict())
1171 for version, ir_enum in version_enum_map.items():
1172 for ir_entry in ir_enum.entries:
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001173 entry_name_version_value_map[ir_entry.name][version] = ir_entry.value
1174
Andreas Wundsam27303462013-07-16 12:52:35 -07001175 self.entries = [ JavaEnumEntry(self, name, version_value_map)
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001176 for (name, version_value_map) in entry_name_version_value_map.items() ]
Andreas Wundsam43526532013-08-01 22:03:50 -07001177
1178 self.entries = [ e for e in self.entries if e.name not in model.enum_entry_blacklist[self.name] ]
Yotam Harchol791e4882013-09-05 16:32:56 -07001179 self.package = "org.projectfloodlight.openflow.protocol"
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001180
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -07001181 self.metadata = model.enum_metadata_map[self.name]
1182
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001183 def wire_type(self, version):
1184 ir_enum = self.version_enums[version]
1185 if "wire_type" in ir_enum.params:
1186 return java_type.convert_enum_wire_type_to_jtype(ir_enum.params["wire_type"])
1187 else:
1188 return java_type.u8
1189
1190 @property
Andreas Wundsam7cfeac32013-09-17 13:53:48 -07001191 @memoize
1192 def is_bitmask(self):
1193 return any(ir_enum.is_bitmask for ir_enum in self.version_enums.values())
1194
1195 @property
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001196 def versions(self):
1197 return self.version_enums.keys()
1198
Andreas Wundsam27303462013-07-16 12:52:35 -07001199 @memoize
1200 def entry_by_name(self, name):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -07001201 res = find(lambda e: e.name == name, self.entries)
1202 if res:
1203 return res
1204 else:
Andreas Wundsam27303462013-07-16 12:52:35 -07001205 raise KeyError("Enum %s: no entry with name %s" % (self.name, name))
1206
1207 @memoize
1208 def entry_by_c_name(self, name):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -07001209 res = find(lambda e: e.c_name == name, self.entries)
1210 if res:
1211 return res
1212 else:
Andreas Wundsam27303462013-07-16 12:52:35 -07001213 raise KeyError("Enum %s: no entry with c_name %s" % (self.name, name))
1214
1215 @memoize
1216 def entry_by_version_value(self, version, value):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -07001217 res = find(lambda e: e.values[version] == value if version in e.values else False, self.entries)
1218 if res:
1219 return res
1220 else:
Andreas Wundsam27303462013-07-16 12:52:35 -07001221 raise KeyError("Enum %s: no entry with version %s, value %s" % (self.name, version, value))
1222
1223# values: Map JavaVersion->Value
1224class JavaEnumEntry(object):
1225 def __init__(self, enum, name, values):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001226 self.enum = enum
Andreas Wundsam27303462013-07-16 12:52:35 -07001227 self.name = enum.name_prefix + "_".join(name.split("_")[1:]).upper()
1228 self.values = values
1229
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -07001230 @property
1231 def constructor_params(self):
Rob Vaterlaus6035bf52013-09-17 19:32:38 -07001232 return [ m.value(self) for m in self.enum.metadata.properties ]
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -07001233
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001234 def has_value(self, version):
1235 return version in self.values
1236
Andreas Wundsam27303462013-07-16 12:52:35 -07001237 def value(self, version):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001238 return self.values[version]
1239
1240 def format_value(self, version):
1241 res = self.enum.wire_type(version).format_value(self.values[version])
Andreas Wundsam27303462013-07-16 12:52:35 -07001242 return res
1243
Andreas Wundsam27303462013-07-16 12:52:35 -07001244 def all_values(self, versions, not_present=None):
1245 return [ self.values[version] if version in self.values else not_present for version in versions ]
Andreas Wundsamc1acfcc2013-09-20 13:10:20 -07001246
1247 @property
1248 @memoize
1249 def masked_enum_group(self):
1250 group = find(lambda g: self.name in g.members, model.masked_enum_groups[self.enum.name])
1251 return group
1252
1253 @property
1254 @memoize
1255 def is_mask(self):
1256 return any(self.name == g.mask for g in model.masked_enum_groups[self.enum.name])