blob: a463d691d9d935d058917dff6f5176c986dd70e8 [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
Andreas Wundsam27303462013-07-16 12:52:35 -070049class JavaModel(object):
Andreas Wundsamf1949682013-09-23 14:48:31 -070050 # registry for enums that should not be generated
51 # set(${java_enum_name})
Andreas Wundsamacd57d52013-10-18 17:35:01 -070052 enum_blacklist = set(("OFDefinitions", "OFPortNo", "OFVlanId", "OFGroup"))
Andreas Wundsamf1949682013-09-23 14:48:31 -070053 # registry for enum *entry* that should not be generated
54 # map: ${java_enum_name} -> set(${java_entry_entry_name})
Andreas Wundsam43526532013-08-01 22:03:50 -070055 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 -070056
57 # registry of interfaces that should not be generated
58 # set(java_names)
Andreas Wundsambe168f72013-08-03 22:49:35 -070059 # OFUint structs are there for god-knows what in loci. We certainly don't need them.
60 interface_blacklist = set( ("OFUint8", "OFUint32",))
Andreas Wundsamf1949682013-09-23 14:48:31 -070061 # registry of interface properties that should not be generated
62 # map: $java_type -> set(java_name_property)
Rich Lane97fd13d2013-11-13 11:25:48 -080063 read_blacklist = defaultdict(lambda: set(),
64 OFExperimenter=set(('data','subtype')),
65 OFActionExperimenter=set(('data',)),
66 OFExperimenterStatsRequest=set(('data','subtype')),
67 OFExperimenterStatsReply=set(('data','subtype')))
Andreas Wundsamf1949682013-09-23 14:48:31 -070068 # map: $java_type -> set(java_name_property)
Andreas Wundsamb1da3f62013-09-04 18:46:03 -070069 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 -070070 # interfaces that are virtual
Andreas Wundsam001b1822013-08-02 22:25:55 -070071 virtual_interfaces = set(['OFOxm', 'OFInstruction', 'OFFlowMod', 'OFBsnVport' ])
Andreas Wundsam27303462013-07-16 12:52:35 -070072
Andreas Wundsam2be7da52013-08-22 07:34:25 -070073 OxmMapEntry = namedtuple("OxmMapEntry", ["type_name", "value", "masked" ])
Yotam Harchole5d92972013-08-22 14:18:36 -070074 oxm_map = { "OFOxmInPort": OxmMapEntry("OFPort", "IN_PORT", False),
75 "OFOxmInPortMasked": OxmMapEntry("OFPort", "IN_PORT", True),
76 "OFOxmInPhyPort": OxmMapEntry("OFPort", "IN_PHY_PORT", False),
77 "OFOxmInPhyPortMasked": OxmMapEntry("OFPort", "IN_PHY_PORT", True),
78 "OFOxmMetadata": OxmMapEntry("OFMetadata", "METADATA", False),
79 "OFOxmMetadataMasked": OxmMapEntry("OFMetadata", "METADATA", True),
80 "OFOxmEthDst": OxmMapEntry("MacAddress", "ETH_DST", False),
81 "OFOxmEthDstMasked": OxmMapEntry("MacAddress", "ETH_DST", True),
82 "OFOxmEthSrc": OxmMapEntry("MacAddress", "ETH_SRC", False),
83 "OFOxmEthSrcMasked": OxmMapEntry("MacAddress", "ETH_SRC", True),
84 "OFOxmEthType": OxmMapEntry("EthType", "ETH_TYPE", False),
85 "OFOxmEthTypeMasked": OxmMapEntry("EthType", "ETH_TYPE", True),
Andreas Wundsam98a18632013-11-05 11:34:24 -080086 "OFOxmVlanVid": OxmMapEntry("OFVlanVidMatch", "VLAN_VID", False),
87 "OFOxmVlanVidMasked": OxmMapEntry("OFVlanVidMatch", "VLAN_VID", True),
Yotam Harchole5d92972013-08-22 14:18:36 -070088 "OFOxmVlanPcp": OxmMapEntry("VlanPcp", "VLAN_PCP", False),
89 "OFOxmVlanPcpMasked": OxmMapEntry("VlanPcp", "VLAN_PCP", True),
90 "OFOxmIpDscp": OxmMapEntry("IpDscp", "IP_DSCP", False),
91 "OFOxmIpDscpMasked": OxmMapEntry("IpDscp", "IP_DSCP", True),
92 "OFOxmIpEcn": OxmMapEntry("IpEcn", "IP_ECN", False),
93 "OFOxmIpEcnMasked": OxmMapEntry("IpEcn", "IP_ECN", True),
94 "OFOxmIpProto": OxmMapEntry("IpProtocol", "IP_PROTO", False),
95 "OFOxmIpProtoMasked": OxmMapEntry("IpProtocol", "IP_PROTO", True),
Yotam Harchola289d552013-09-16 10:10:40 -070096 "OFOxmIpv4Src": OxmMapEntry("IPv4Address", "IPV4_SRC", False),
97 "OFOxmIpv4SrcMasked": OxmMapEntry("IPv4Address", "IPV4_SRC", True),
98 "OFOxmIpv4Dst": OxmMapEntry("IPv4Address", "IPV4_DST", False),
99 "OFOxmIpv4DstMasked": OxmMapEntry("IPv4Address", "IPV4_DST", True),
Yotam Harchole5d92972013-08-22 14:18:36 -0700100 "OFOxmTcpSrc": OxmMapEntry("TransportPort", "TCP_SRC", False),
101 "OFOxmTcpSrcMasked": OxmMapEntry("TransportPort", "TCP_SRC", True),
102 "OFOxmTcpDst": OxmMapEntry("TransportPort", "TCP_DST", False),
103 "OFOxmTcpDstMasked": OxmMapEntry("TransportPort", "TCP_DST", True),
104 "OFOxmUdpSrc": OxmMapEntry("TransportPort", "UDP_SRC", False),
105 "OFOxmUdpSrcMasked": OxmMapEntry("TransportPort", "UDP_SRC", True),
106 "OFOxmUdpDst": OxmMapEntry("TransportPort", "UDP_DST", False),
107 "OFOxmUdpDstMasked": OxmMapEntry("TransportPort", "UDP_DST", True),
108 "OFOxmSctpSrc": OxmMapEntry("TransportPort", "SCTP_SRC", False),
109 "OFOxmSctpSrcMasked": OxmMapEntry("TransportPort", "SCTP_SRC", True),
110 "OFOxmSctpDst": OxmMapEntry("TransportPort", "SCTP_DST", False),
111 "OFOxmSctpDstMasked": OxmMapEntry("TransportPort", "SCTP_DST", True),
112 "OFOxmIcmpv4Type": OxmMapEntry("ICMPv4Type", "ICMPV4_TYPE", False),
113 "OFOxmIcmpv4TypeMasked": OxmMapEntry("ICMPv4Type", "ICMPV4_TYPE", True),
114 "OFOxmIcmpv4Code": OxmMapEntry("ICMPv4Code", "ICMPV4_CODE", False),
115 "OFOxmIcmpv4CodeMasked": OxmMapEntry("ICMPv4Code", "ICMPV4_CODE", True),
116 "OFOxmArpOp": OxmMapEntry("ArpOpcode", "ARP_OP", False),
117 "OFOxmArpOpMasked": OxmMapEntry("ArpOpcode", "ARP_OP", True),
Yotam Harchola289d552013-09-16 10:10:40 -0700118 "OFOxmArpSpa": OxmMapEntry("IPv4Address", "ARP_SPA", False),
119 "OFOxmArpSpaMasked": OxmMapEntry("IPv4Address", "ARP_SPA", True),
120 "OFOxmArpTpa": OxmMapEntry("IPv4Address", "ARP_TPA", False),
121 "OFOxmArpTpaMasked": OxmMapEntry("IPv4Address", "ARP_TPA", True),
Yotam Harchole5d92972013-08-22 14:18:36 -0700122 "OFOxmArpSha": OxmMapEntry("MacAddress", "ARP_SHA", False),
123 "OFOxmArpShaMasked": OxmMapEntry("MacAddress", "ARP_SHA", True),
124 "OFOxmArpTha": OxmMapEntry("MacAddress", "ARP_THA", False),
125 "OFOxmArpThaMasked": OxmMapEntry("MacAddress", "ARP_THA", True),
Yotam Harchola289d552013-09-16 10:10:40 -0700126 "OFOxmIpv6Src": OxmMapEntry("IPv6Address", "IPV6_SRC", False),
127 "OFOxmIpv6SrcMasked": OxmMapEntry("IPv6Address", "IPV6_SRC", True),
128 "OFOxmIpv6Dst": OxmMapEntry("IPv6Address", "IPV6_DST", False),
129 "OFOxmIpv6DstMasked": OxmMapEntry("IPv6Address", "IPV6_DST", True),
Yotam Harchole5d92972013-08-22 14:18:36 -0700130 "OFOxmIpv6Flabel": OxmMapEntry("IPv6FlowLabel", "IPV6_FLABEL", False),
Yotam Harchola86e4252013-09-06 15:36:28 -0700131 "OFOxmIpv6FlabelMasked": OxmMapEntry("IPv6FlowLabel", "IPV6_FLABEL", True),
132 "OFOxmIcmpv6Type": OxmMapEntry("U8", "ICMPV6_TYPE", False),
133 "OFOxmIcmpv6TypeMasked": OxmMapEntry("U8", "ICMPV6_TYPE", True),
134 "OFOxmIcmpv6Code": OxmMapEntry("U8", "ICMPV6_CODE", False),
135 "OFOxmIcmpv6CodeMasked": OxmMapEntry("U8", "ICMPV6_CODE", True),
Yotam Harchola289d552013-09-16 10:10:40 -0700136 "OFOxmIpv6NdTarget": OxmMapEntry("IPv6Address", "IPV6_ND_TARGET", False),
137 "OFOxmIpv6NdTargetMasked": OxmMapEntry("IPv6Address", "IPV6_ND_TARGET", True),
Yotam Harchola86e4252013-09-06 15:36:28 -0700138 "OFOxmIpv6NdSll": OxmMapEntry("MacAddress", "IPV6_ND_SLL", False),
139 "OFOxmIpv6NdSllMasked": OxmMapEntry("MacAddress", "IPV6_ND_SLL", True),
140 "OFOxmIpv6NdTll": OxmMapEntry("MacAddress", "IPV6_ND_TLL", False),
141 "OFOxmIpv6NdTllMasked": OxmMapEntry("MacAddress", "IPV6_ND_TLL", True),
142 "OFOxmMplsLabel": OxmMapEntry("U32", "MPLS_LABEL", False),
143 "OFOxmMplsLabelMasked": OxmMapEntry("U32", "MPLS_LABEL", True),
144 "OFOxmMplsTc": OxmMapEntry("U8", "MPLS_TC", False),
Yotam Harchola11f38b2013-09-26 15:38:17 -0700145 "OFOxmMplsTcMasked": OxmMapEntry("U8", "MPLS_TC", True),
Yotam Harchol2c535582013-10-01 15:50:20 -0700146 "OFOxmBsnInPorts128": OxmMapEntry("OFBitMask128", "BSN_IN_PORTS_128", False),
Rich Lane1424d0b2013-10-24 17:16:24 -0700147 "OFOxmBsnInPorts128Masked": OxmMapEntry("OFBitMask128", "BSN_IN_PORTS_128", True),
Rich Lane376cafe2013-10-27 21:49:14 -0700148 "OFOxmBsnLagId": OxmMapEntry("LagId", "BSN_LAG_ID", False),
149 "OFOxmBsnLagIdMasked": OxmMapEntry("LagId", "BSN_LAG_ID", True),
Rich Laneeb21c4f2013-10-28 17:34:41 -0700150 "OFOxmBsnVrf": OxmMapEntry("VRF", "BSN_VRF", False),
151 "OFOxmBsnVrfMasked": OxmMapEntry("VRF", "BSN_VRF", True),
Rich Lane9c27b5d2013-10-30 17:14:32 -0700152 "OFOxmBsnGlobalVrfAllowed": OxmMapEntry("OFBooleanValue", "BSN_GLOBAL_VRF_ALLOWED", False),
153 "OFOxmBsnGlobalVrfAllowedMasked": OxmMapEntry("OFBooleanValue", "BSN_GLOBAL_VRF_ALLOWED", True),
Rich Laneeb21c4f2013-10-28 17:34:41 -0700154 "OFOxmBsnL3InterfaceClassId": OxmMapEntry("ClassId", "BSN_L3_INTERFACE_CLASS_ID", False),
155 "OFOxmBsnL3InterfaceClassIdMasked": OxmMapEntry("ClassId", "BSN_L3_INTERFACE_CLASS_ID", True),
156 "OFOxmBsnL3SrcClassId": OxmMapEntry("ClassId", "BSN_L3_SRC_CLASS_ID", False),
157 "OFOxmBsnL3SrcClassIdMasked": OxmMapEntry("ClassId", "BSN_L3_SRC_CLASS_ID", True),
158 "OFOxmBsnL3DstClassId": OxmMapEntry("ClassId", "BSN_L3_DST_CLASS_ID", False),
159 "OFOxmBsnL3DstClassIdMasked": OxmMapEntry("ClassId", "BSN_L3_DST_CLASS_ID", True),
Yotam Harchola86e4252013-09-06 15:36:28 -0700160 }
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700161
Andreas Wundsamf1949682013-09-23 14:48:31 -0700162 # Registry of nullable properties:
163 # ${java_class_name} -> set(${java_property_name})
164 nullable_map = defaultdict(lambda: set(),
165 )
166
167 # represents a subgroup of a bitmask enum that is actualy a normal enumerable within a masked part of the enum
168 # e.g., the flags STP.* in OF1.0 port state are bit mask entries, but instead enumerables according to the mask "STP_MASK"
169 # name: a name for the group
170 # mask: java name of the enum entry that defines the mask
171 # members: set of names of the members of the group
Andreas Wundsamc1acfcc2013-09-20 13:10:20 -0700172 MaskedEnumGroup = namedtuple("MaskedEnumGroup", ("name", "mask", "members"))
173
Andreas Wundsamf1949682013-09-23 14:48:31 -0700174 # registry of MaskedEnumGroups (see above).
175 # map: ${java_enum_name}: tuple(MaskedEnumGroup)
Andreas Wundsamc1acfcc2013-09-20 13:10:20 -0700176 masked_enum_groups = defaultdict(lambda: (),
Andreas Wundsamaebe5182013-09-24 13:01:07 -0700177 OFPortState = (MaskedEnumGroup("stp_flags", mask="STP_MASK", members=set(("STP_LISTEN", "STP_LEARN", "STP_FORWARD", "STP_BLOCK"))), ),
178 OFConfigFlags = (
179 MaskedEnumGroup("frag_flags", mask="FRAG_MASK", members=set(("FRAG_NORMAL", "FRAG_DROP", "FRAG_REASM"))),
Andreas Wundsam376cef52013-09-24 13:52:18 -0700180 ),
181 OFTableConfig = (
182 MaskedEnumGroup("table_miss_flags", mask="TABLE_MISS_MASK", members=set(("TABLE_MISS_CONTROLLER", "TABLE_MISS_CONTINUE", "TABLE_MISS_DROP"))),
183 ),
Andreas Wundsamc1acfcc2013-09-20 13:10:20 -0700184 )
185
Andreas Wundsamf1949682013-09-23 14:48:31 -0700186 # represents a metadata property associated with an EnumClass
187 # name:
Rob Vaterlaus6035bf52013-09-17 19:32:38 -0700188 class OFEnumPropertyMetadata(namedtuple("OFEnumPropertyMetadata", ("name", "type", "value"))):
Andreas Wundsamf1949682013-09-23 14:48:31 -0700189 """
190 represents a metadata property associated with an Enum Class
191 @param name name of metadata property
192 @param type java_type instance describing the type
193 @value: Generator function f(entry) that generates the value
194 """
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -0700195 @property
196 def variable_name(self):
197 return self.name[0].lower() + self.name[1:]
198
199 @property
200 def getter_name(self):
Rob Vaterlausbda9ce62013-09-17 14:45:48 -0700201 prefix = "is" if self.type == java_type.boolean else "get"
202 return prefix+self.name
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -0700203
Andreas Wundsamf1949682013-09-23 14:48:31 -0700204 """ Metadata container. """
Rob Vaterlaus6035bf52013-09-17 19:32:38 -0700205 OFEnumMetadata = namedtuple("OFEnumMetadata", ("properties", "to_string"))
206
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -0700207 def gen_port_speed(enum_entry):
Andreas Wundsamf1949682013-09-23 14:48:31 -0700208 """ Generator function for OFortFeatures.PortSpeed"""
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -0700209 splits = enum_entry.name.split("_")
210 if len(splits)>=2:
211 m = re.match(r'\d+[MGTP]B', splits[1])
212 if m:
213 return "PortSpeed.SPEED_{}".format(splits[1])
214 return "PortSpeed.SPEED_NONE";
215
Rob Vaterlausbda9ce62013-09-17 14:45:48 -0700216 def gen_stp_state(enum_entry):
Andreas Wundsamf1949682013-09-23 14:48:31 -0700217 """ Generator function for OFPortState.StpState"""
Rob Vaterlausbda9ce62013-09-17 14:45:48 -0700218 splits = enum_entry.name.split("_")
219 if len(splits)>=1:
220 if splits[0] == "STP":
221 return "true"
222 return "false"
223
Andreas Wundsamf1949682013-09-23 14:48:31 -0700224 # registry for metadata properties for enums
225 # map: ${java_enum_name}: OFEnumMetadata
Rob Vaterlaus6035bf52013-09-17 19:32:38 -0700226 enum_metadata_map = defaultdict(lambda: JavaModel.OFEnumMetadata((), None),
Rob Vaterlausa049dee2013-09-18 16:18:37 -0700227 OFPortFeatures = OFEnumMetadata((OFEnumPropertyMetadata("PortSpeed", java_type.port_speed, gen_port_speed),), None),
228 OFPortState = OFEnumMetadata((OFEnumPropertyMetadata("StpState", java_type.boolean, gen_stp_state),), None),
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -0700229 )
230
Andreas Wundsam27303462013-07-16 12:52:35 -0700231 @property
232 @memoize
233 def versions(self):
234 return OrderedSet( JavaOFVersion(raw_version) for raw_version in of_g.target_version_list )
235
236 @property
237 @memoize
238 def interfaces(self):
Andreas Wundsam5204de22013-07-30 11:34:45 -0700239 version_map_per_class = collections.OrderedDict()
Andreas Wundsam27303462013-07-16 12:52:35 -0700240
241 for raw_version, of_protocol in of_g.ir.items():
242 jversion = JavaOFVersion(of_protocol.wire_version)
243
244 for of_class in of_protocol.classes:
Andreas Wundsam5204de22013-07-30 11:34:45 -0700245 if not of_class.name in version_map_per_class:
246 version_map_per_class[of_class.name] = collections.OrderedDict()
247
Andreas Wundsam27303462013-07-16 12:52:35 -0700248 version_map_per_class[of_class.name][jversion] = of_class
249
250 interfaces = []
251 for class_name, version_map in version_map_per_class.items():
252 interfaces.append(JavaOFInterface(class_name, version_map))
253
Andreas Wundsambe168f72013-08-03 22:49:35 -0700254 interfaces = [ i for i in interfaces if i.name not in self.interface_blacklist ]
255
Andreas Wundsam27303462013-07-16 12:52:35 -0700256 return interfaces
257
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700258 @memoize
259 def interface_by_name(self, name):
260 return find(lambda i: erase_type_annotation(i.name) == erase_type_annotation(name), self.interfaces)
261
Andreas Wundsam27303462013-07-16 12:52:35 -0700262 @property
263 @memoize
Andreas Wundsam001b1822013-08-02 22:25:55 -0700264 def all_classes(self):
265 return [clazz for interface in self.interfaces for clazz in interface.versioned_classes]
266
267 @property
268 @memoize
Andreas Wundsam27303462013-07-16 12:52:35 -0700269 def enums(self):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700270 name_version_enum_map = OrderedDefaultDict(lambda: OrderedDict())
Andreas Wundsam27303462013-07-16 12:52:35 -0700271
272 for version in self.versions:
273 of_protocol = of_g.ir[version.int_version]
274 for enum in of_protocol.enums:
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700275 name_version_enum_map[enum.name][version] = enum
Andreas Wundsam27303462013-07-16 12:52:35 -0700276
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700277 enums = [ JavaEnum(name, version_enum_map) for name, version_enum_map,
278 in name_version_enum_map.items() ]
Andreas Wundsam27303462013-07-16 12:52:35 -0700279
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700280 # inelegant - need java name here
281 enums = [ enum for enum in enums if enum.name not in self.enum_blacklist ]
Andreas Wundsam27303462013-07-16 12:52:35 -0700282 return enums
283
284 @memoize
285 def enum_by_name(self, name):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700286 res = find(lambda e: e.name == name, self.enums)
287 if not res:
Andreas Wundsam27303462013-07-16 12:52:35 -0700288 raise KeyError("Could not find enum with name %s" % name)
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700289 return res
Andreas Wundsam27303462013-07-16 12:52:35 -0700290
Andreas Wundsam5204de22013-07-30 11:34:45 -0700291 @property
292 @memoize
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700293 def of_factories(self):
Yotam Harchol791e4882013-09-05 16:32:56 -0700294 prefix = "org.projectfloodlight.openflow.protocol"
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700295
296 factories = OrderedDict()
297
298 sub_factory_classes = ("OFAction", "OFInstruction", "OFMeterBand", "OFOxm", "OFQueueProp")
299 for base_class in sub_factory_classes:
300 package = base_class[2:].lower()
301 remove_prefix = base_class[2].lower() + base_class[3:]
302
303 # HACK need to have a better way to deal with parameterized base classes
304 annotated_base_class = base_class + "<?>" if base_class == "OFOxm" else base_class
305
306 factories[base_class] = OFFactory(package="%s.%s" % (prefix, package),
Andreas Wundsamcb17b232013-09-28 19:05:36 -0700307 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 -0700308
309 factories[""] = OFFactory(
310 package=prefix,
Andreas Wundsam5204de22013-07-30 11:34:45 -0700311 name="OFFactory",
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700312 remove_prefix="",
313 members=[], base_class="OFMessage", sub_factories=OrderedDict(
Andreas Wundsamcb17b232013-09-28 19:05:36 -0700314 ("{}{}s".format(n[2].lower(), n[3:]), "{}s".format(n)) for n in sub_factory_classes ),
315 xid_generator=True)
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700316
317 for i in self.interfaces:
318 for n, factory in factories.items():
319 if n == "":
320 factory.members.append(i)
321 break
322 else:
323 super_class = self.interface_by_name(n)
324 if i.is_instance_of(super_class):
325 factory.members.append(i)
326 break
327 return factories.values()
Yotam Harchol595c6442013-09-27 16:29:08 -0700328
329 @memoize
330 def factory_of(self, interface):
331 for factory in self.of_factories:
332 if interface in factory.members:
333 return factory
334 return None
Andreas Wundsam5204de22013-07-30 11:34:45 -0700335
336 def generate_class(self, clazz):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700337 """ return wether or not to generate implementation class clazz.
338 Now true for everything except OFTableModVer10.
339 @param clazz JavaOFClass instance
340 """
Andreas Wundsam43526532013-08-01 22:03:50 -0700341 if clazz.interface.name.startswith("OFMatchV"):
342 return True
Andreas Wundsam001b1822013-08-02 22:25:55 -0700343 elif clazz.name == "OFTableModVer10":
344 # tablemod ver 10 is a hack and has no oftype defined
345 return False
Andreas Wundsam5204de22013-07-30 11:34:45 -0700346 if loxi_utils.class_is_message(clazz.interface.c_name):
347 return True
348 if loxi_utils.class_is_oxm(clazz.interface.c_name):
349 return True
Andreas Wundsam43526532013-08-01 22:03:50 -0700350 if loxi_utils.class_is_action(clazz.interface.c_name):
351 return True
352 if loxi_utils.class_is_instruction(clazz.interface.c_name):
353 return True
Andreas Wundsam5204de22013-07-30 11:34:45 -0700354 else:
Andreas Wundsam001b1822013-08-02 22:25:55 -0700355 return True
Andreas Wundsam5204de22013-07-30 11:34:45 -0700356
357
Andreas Wundsamcb17b232013-09-28 19:05:36 -0700358class OFFactory(namedtuple("OFFactory", ("package", "name", "members", "remove_prefix", "base_class", "sub_factories", "xid_generator"))):
Andreas Wundsam5204de22013-07-30 11:34:45 -0700359 @property
360 def factory_classes(self):
361 return [ OFFactoryClass(
Yotam Harchol791e4882013-09-05 16:32:56 -0700362 package="org.projectfloodlight.openflow.protocol.ver{}".format(version.of_version),
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700363 name="{}Ver{}".format(self.name, version.of_version),
Andreas Wundsam5204de22013-07-30 11:34:45 -0700364 interface=self,
365 version=version
366 ) for version in model.versions ]
367
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700368 def method_name(self, member, builder=True):
369 n = member.variable_name
370 if n.startswith(self.remove_prefix):
371 n = n[len(self.remove_prefix):]
372 n = n[0].lower() + n[1:]
373 if builder:
374 return "build" + n[0].upper() + n[1:]
375 else:
376 return n
Yotam Harchol595c6442013-09-27 16:29:08 -0700377
378 def of_version(self, version):
379 for fc in self.factory_classes:
380 if fc.version == version:
381 return fc
382 return None
Andreas Wundsam5204de22013-07-30 11:34:45 -0700383
384OFGenericClass = namedtuple("OFGenericClass", ("package", "name"))
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700385class OFFactoryClass(namedtuple("OFFactoryClass", ("package", "name", "interface", "version"))):
386 @property
387 def base_class(self):
388 return self.interface.base_class
389
390 @property
391 def versioned_base_class(self):
392 base_class_interface = model.interface_by_name(self.interface.base_class)
393 if base_class_interface and base_class_interface.has_version(self.version):
394 return base_class_interface.versioned_class(self.version)
395 else:
396 return None
Andreas Wundsam5204de22013-07-30 11:34:45 -0700397
Andreas Wundsam27303462013-07-16 12:52:35 -0700398model = JavaModel()
399
400#######################################################################
401### OFVersion
402#######################################################################
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700403
404class JavaOFVersion(object):
405 """ Models a version of OpenFlow. contains methods to convert the internal
406 Loxi version to a java constant / a string """
407 def __init__(self, int_version):
408 self.int_version = int(int_version)
409
410 @property
411 def of_version(self):
412 return "1" + str(int(self.int_version) - 1)
413
414 @property
415 def constant_version(self):
416 return "OF_" + self.of_version
417
Andreas Wundsam27303462013-07-16 12:52:35 -0700418 def __repr__(self):
419 return "JavaOFVersion(%d)" % self.int_version
420
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700421 def __str__(self):
422 return of_g.param_version_names[self.int_version]
423
Andreas Wundsam27303462013-07-16 12:52:35 -0700424 def __hash__(self):
425 return hash(self.int_version)
426
427 def __eq__(self, other):
428 if other is None or type(self) != type(other):
429 return False
430 return (self.int_version,) == (other.int_version,)
431
432#######################################################################
433### Interface
434#######################################################################
435
436class JavaOFInterface(object):
437 """ Models an OpenFlow Message class for the purpose of the java class.
438 Version agnostic, in contrast to the loxi_ir python model.
439 """
440 def __init__(self, c_name, version_map):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700441 """"
442 @param c_name: loxi style name (e.g., of_flow_add)
443 @param version_map map of { JavaOFVersion: OFClass (from loxi_ir) }
444 """
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700445 self.c_name = c_name
Andreas Wundsam27303462013-07-16 12:52:35 -0700446 self.version_map = version_map
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700447 # name: the Java Type name, e.g., OFFlowAdd
Andreas Wundsam001b1822013-08-02 22:25:55 -0700448 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 -0700449 # variable_name name to use for variables of this type. i.e., flowAdd
Andreas Wundsam5204de22013-07-30 11:34:45 -0700450 self.variable_name = self.name[2].lower() + self.name[3:]
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700451 self.title_name = self.variable_name[0].upper() + self.variable_name[1:]
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700452 # name for use in constants: FLOW_ADD
Andreas Wundsam27303462013-07-16 12:52:35 -0700453 self.constant_name = c_name.upper().replace("OF_", "")
454
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700455 pck_suffix, parent_interface, self.type_annotation = self.class_info()
Yotam Harchol791e4882013-09-05 16:32:56 -0700456 self.package = "org.projectfloodlight.openflow.protocol.%s" % pck_suffix if pck_suffix else "org.projectfloodlight.openflow.protocol"
Andreas Wundsam27303462013-07-16 12:52:35 -0700457 if self.name != parent_interface:
458 self.parent_interface = parent_interface
459 else:
460 self.parent_interface = None
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700461
Andreas Wundsama705c6b2013-09-04 11:21:09 -0700462 @property
463 @memoize
464 def all_parent_interfaces(self):
465 return [ "OFObject" ] + \
466 ([ self.parent_interface ] if self.parent_interface else [] )+ \
467 self.additional_parent_interfaces
468 @property
469 @memoize
470 def additional_parent_interfaces(self):
471 if loxi_utils.class_is_message(self.c_name) and not self.is_virtual:
472 m = re.match(r'(.*)Request$', self.name)
473 if m:
474 reply_name = m.group(1) + "Reply"
475 if model.interface_by_name(reply_name):
476 return ["OFRequest<%s>" % reply_name ]
477 return []
478
479
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700480 def is_instance_of(self, other_class):
481 if self == other_class:
482 return True
483 parent = self.super_class
484 if parent is None:
485 return False
486 else:
487 return parent.is_instance_of(other_class)
488
489 @property
490 def super_class(self):
491 if not self.parent_interface:
492 return None
493 else:
494 return model.interface_by_name(self.parent_interface)
495
496
497 def inherited_declaration(self, type_spec="?"):
498 if self.type_annotation:
499 return "%s<%s>" % (self.name, type_spec)
500 else:
501 return "%s" % self.name
502
503 @property
504 def type_variable(self):
505 if self.type_annotation:
506 return "<T>"
507 else:
508 return "";
509
Andreas Wundsam27303462013-07-16 12:52:35 -0700510 def class_info(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700511 """ return tuple of (package_prefix, parent_class) for the current JavaOFInterface"""
512 # FIXME: This duplicates inheritance information that is now available in the loxi_ir
513 # model (note, that the loxi model is on versioned classes). Should check/infer the
514 # inheritance information from the versioned lox_ir classes.
Andreas Wundsamd6b0cb02013-09-28 18:55:56 -0700515 if re.match(r'OFStatsRequest$', self.name):
516 return ("", "OFMessage", "T extends OFStatsReply")
Rich Lane97fd13d2013-11-13 11:25:48 -0800517 elif re.match(r'OFBsnStatsRequest$', self.name):
518 return ("", "OFExperimenterStatsRequest", None)
519 elif re.match(r'OFBsnStatsReply$', self.name):
520 return ("", "OFExperimenterStatsReply", None)
521 elif re.match(r'OFBsn.+StatsRequest$', self.name):
522 return ("", "OFBsnStatsRequest", None)
523 elif re.match(r'OFBsn.+StatsReply$', self.name):
524 return ("", "OFBsnStatsReply", None)
Andreas Wundsamd6b0cb02013-09-28 18:55:56 -0700525 elif re.match(r'OF.+StatsRequest$', self.name):
526 return ("", "OFStatsRequest<{}>".format(re.sub(r'Request$', 'Reply', self.name)), None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700527 elif re.match(r'OF.+StatsReply$', self.name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700528 return ("", "OFStatsReply", None)
Rob Vaterlausfeee3712013-09-30 11:24:19 -0700529 elif re.match(r'OF.+ErrorMsg$', self.name):
530 return ("", "OFErrorMsg", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700531 elif re.match(r'OFFlow(Add|Modify(Strict)?|Delete(Strict)?)$', self.name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700532 return ("", "OFFlowMod", None)
Andreas Wundsama705c6b2013-09-04 11:21:09 -0700533 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 -0700534 return ("", "OFBsnHeader", None)
Andreas Wundsama705c6b2013-09-04 11:21:09 -0700535 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 -0700536 return ("", "OFNiciraHeader", None)
Andreas Wundsamb1da3f62013-09-04 18:46:03 -0700537 elif self.name == "OFBsnHeader" or self.name =="OFNiciraHeader":
538 return ("", "OFExperimenter", None)
Andreas Wundsam43526532013-08-01 22:03:50 -0700539 elif re.match(r'OFMatch.*', self.name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700540 return ("", "Match", None)
Andreas Wundsam5204de22013-07-30 11:34:45 -0700541 elif loxi_utils.class_is_message(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700542 return ("", "OFMessage", None)
Andreas Wundsam5204de22013-07-30 11:34:45 -0700543 elif loxi_utils.class_is_action(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700544 if re.match(r'OFActionBsn.+', self.name):
545 return ("action", "OFActionBsn", None)
546 elif re.match(r'OFActionNicira.+', self.name):
547 return ("action", "OFActionNicira", None)
Andreas Wundsamb1da3f62013-09-04 18:46:03 -0700548 elif self.name == "OFActionBsn" or self.name == "OFActionNicira":
549 return ("action", "OFActionExperimenter", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700550 else:
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700551 return ("action", "OFAction", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700552 elif re.match(r'OFBsnVport.+$', self.name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700553 return ("", "OFBsnVport", None)
554 elif self.name == "OFOxm":
555 return ("oxm", None, "T extends OFValueType<T>")
Andreas Wundsam5204de22013-07-30 11:34:45 -0700556 elif loxi_utils.class_is_oxm(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700557 if self.name in model.oxm_map:
558 return ("oxm", "OFOxm<%s>" % model.oxm_map[self.name].type_name, None)
559 else:
560 return ("oxm", "OFOxm", None)
Andreas Wundsam5204de22013-07-30 11:34:45 -0700561 elif loxi_utils.class_is_instruction(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700562 return ("instruction", "OFInstruction", None)
Andreas Wundsam5204de22013-07-30 11:34:45 -0700563 elif loxi_utils.class_is_meter_band(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700564 return ("meterband", "OFMeterBand", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700565 elif loxi_utils.class_is_queue_prop(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700566 return ("queueprop", "OFQueueProp", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700567 elif loxi_utils.class_is_hello_elem(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700568 return ("", "OFHelloElem", None)
Rich Lane26847562013-10-08 13:44:47 -0700569 elif loxi_utils.class_is_table_feature_prop(self.c_name):
570 return ("", "OFTableFeatureProp", None)
Andreas Wundsam27303462013-07-16 12:52:35 -0700571 else:
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700572 return ("", None, None)
573
574 @property
575 @memoize
576 def writeable_members(self):
577 return [ m for m in self.members if m.is_writeable ]
Andreas Wundsam27303462013-07-16 12:52:35 -0700578
Andreas Wundsamcb17b232013-09-28 19:05:36 -0700579 @memoize
580 def member_by_name(self, name):
581 return find(lambda m: m.name == name, self.members)
582
Andreas Wundsam27303462013-07-16 12:52:35 -0700583 @property
584 @memoize
585 def members(self):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700586 return self.ir_model_members + self.virtual_members
587
588 @property
589 @memoize
590 def ir_model_members(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700591 """return a list of all members to be exposed by this interface. Corresponds to
592 the union of the members of the vesioned classes without length, fieldlength
593 and pads (those are handled automatically during (de)serialization and not exposed"""
Andreas Wundsam27303462013-07-16 12:52:35 -0700594 all_versions = []
595 member_map = collections.OrderedDict()
596
597 for (version, of_class) in self.version_map.items():
598 for of_member in of_class.members:
599 if isinstance(of_member, OFLengthMember) or \
600 isinstance(of_member, OFFieldLengthMember) or \
601 isinstance(of_member, OFPadMember):
602 continue
603 if of_member.name not in member_map:
604 member_map[of_member.name] = JavaMember.for_of_member(self, of_member)
605
Andreas Wundsamb1da3f62013-09-04 18:46:03 -0700606 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 -0700607
608 @property
609 def virtual_members(self):
Andreas Wundsama0981022013-10-02 18:15:06 -0700610 virtual_members = []
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700611 if self.name == "OFOxm":
Andreas Wundsama0981022013-10-02 18:15:06 -0700612 virtual_members += [
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700613 JavaVirtualMember(self, "value", java_type.generic_t),
614 JavaVirtualMember(self, "mask", java_type.generic_t),
615 JavaVirtualMember(self, "matchField", java_type.make_match_field_jtype("T")),
616 JavaVirtualMember(self, "masked", java_type.boolean),
Andreas Wundsam661a2222013-11-05 17:18:59 -0800617 JavaVirtualMember(self, "canonical", java_type.make_oxm_jtype("T"))
Andreas Wundsama0981022013-10-02 18:15:06 -0700618 ]
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700619 elif self.parent_interface and self.parent_interface.startswith("OFOxm"):
620 field_type = java_type.make_match_field_jtype(model.oxm_map[self.name].type_name) \
621 if self.name in model.oxm_map \
622 else java_type.make_match_field_jtype()
623
Andreas Wundsama0981022013-10-02 18:15:06 -0700624 virtual_members += [
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700625 JavaVirtualMember(self, "matchField", field_type),
626 JavaVirtualMember(self, "masked", java_type.boolean),
Andreas Wundsam661a2222013-11-05 17:18:59 -0800627 JavaVirtualMember(self, "canonical", java_type.make_oxm_jtype(model.oxm_map[self.name].type_name),
628 custom_template=lambda builder: "OFOxm{}_getCanonical.java".format(".Builder" if builder else "")),
Andreas Wundsama0981022013-10-02 18:15:06 -0700629 ]
630 if not find(lambda x: x.name == "mask", self.ir_model_members):
631 virtual_members.append(JavaVirtualMember(self, "mask", find(lambda x: x.name == "value", self.ir_model_members).java_type))
632
633 if not find(lambda m: m.name == "version", self.ir_model_members):
634 virtual_members.append(JavaVirtualMember(self, "version", java_type.of_version))
635
636 return tuple(virtual_members)
Andreas Wundsam27303462013-07-16 12:52:35 -0700637
638 @property
639 @memoize
640 def is_virtual(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700641 """ Is this interface virtual. If so, do not generate a builder interface """
Andreas Wundsam001b1822013-08-02 22:25:55 -0700642 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 -0700643
644 @property
Andreas Wundsam5204de22013-07-30 11:34:45 -0700645 def is_universal(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700646 """ Is this interface universal, i.e., does it exist in all OF versions? """
Andreas Wundsam5204de22013-07-30 11:34:45 -0700647 return len(self.all_versions) == len(model.versions)
648
649 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700650 @memoize
651 def all_versions(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700652 """ return list of all versions that this interface exists in """
Andreas Wundsam27303462013-07-16 12:52:35 -0700653 return self.version_map.keys()
654
Andreas Wundsam5204de22013-07-30 11:34:45 -0700655 def has_version(self, version):
656 return version in self.version_map
657
Andreas Wundsam27303462013-07-16 12:52:35 -0700658 def versioned_class(self, version):
659 return JavaOFClass(self, version, self.version_map[version])
660
661 @property
662 @memoize
663 def versioned_classes(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700664 return [ self.versioned_class(version) for version in self.all_versions ]
665
666#######################################################################
667### (Versioned) Classes
668#######################################################################
669
670class JavaOFClass(object):
671 """ Models an OpenFlow Message class for the purpose of the java class.
Andreas Wundsamd40ddbf2013-07-25 15:40:47 -0700672 Version specific child of a JavaOFInterface
Andreas Wundsam27303462013-07-16 12:52:35 -0700673 """
674 def __init__(self, interface, version, ir_class):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700675 """
676 @param interface JavaOFInterface instance of the parent interface
677 @param version JavaOFVersion
678 @param ir_class OFClass from loxi_ir
679 """
Andreas Wundsam27303462013-07-16 12:52:35 -0700680 self.interface = interface
681 self.ir_class = ir_class
682 self.c_name = self.ir_class.name
683 self.version = version
684 self.constant_name = self.c_name.upper().replace("OF_", "")
Yotam Harchol791e4882013-09-05 16:32:56 -0700685 self.package = "org.projectfloodlight.openflow.protocol.ver%s" % version.of_version
Andreas Wundsame916d6f2013-07-30 11:33:58 -0700686 self.generated = False
687
688 @property
689 @memoize
690 def unit_test(self):
Yotam Harchol466b3212013-08-15 12:14:46 -0700691 return JavaUnitTestSet(self)
Andreas Wundsam27303462013-07-16 12:52:35 -0700692
693 @property
694 def name(self):
695 return "%sVer%s" % (self.interface.name, self.version.of_version)
696
697 @property
Andreas Wundsam5204de22013-07-30 11:34:45 -0700698 def variable_name(self):
Andreas Wundsam781d99f2013-10-23 15:25:56 -0700699 return self.name[2].lower() + self.name[3:]
Andreas Wundsam5204de22013-07-30 11:34:45 -0700700
701 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700702 def length(self):
703 if self.is_fixed_length:
704 return self.min_length
705 else:
706 raise Exception("No fixed length for class %s, version %s" % (self.name, self.version))
707
708 @property
709 def min_length(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700710 """ @return the minimum wire length of an instance of this class in bytes """
Andreas Wundsam27303462013-07-16 12:52:35 -0700711 id_tuple = (self.ir_class.name, self.version.int_version)
712 return of_g.base_length[id_tuple] if id_tuple in of_g.base_length else -1
713
714 @property
715 def is_fixed_length(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700716 """ true iff this class serializes to a fixed length on the wire """
Andreas Wundsambbf85e12013-09-13 14:18:01 -0700717 return (self.ir_class.name, self.version.int_version) in of_g.is_fixed_length and \
718 not self.is_virtual
Andreas Wundsam27303462013-07-16 12:52:35 -0700719
720 def all_properties(self):
721 return self.interface.members
722
723 def get_member(self, name):
724 for m in self.members:
725 if m.name == name:
726 return m
727
728 @property
729 @memoize
730 def data_members(self):
731 return [ prop for prop in self.members if prop.is_data ]
732
733 @property
734 @memoize
735 def fixed_value_members(self):
736 return [ prop for prop in self.members if prop.is_fixed_value ]
737
738 @property
739 @memoize
740 def public_members(self):
741 return [ prop for prop in self.members if prop.is_public ]
742
743 @property
744 @memoize
745 def members(self):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700746 return self.ir_model_members + self.virtual_members
747
748 @property
Andreas Wundsama0981022013-10-02 18:15:06 -0700749 @memoize
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700750 def ir_model_members(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700751 members = [ JavaMember.for_of_member(self, of_member) for of_member in self.ir_class.members ]
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700752 return tuple(members)
753
754 @property
755 def virtual_members(self):
Andreas Wundsama0981022013-10-02 18:15:06 -0700756 virtual_members = []
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700757 if self.interface.parent_interface and self.interface.parent_interface.startswith("OFOxm"):
758 if self.interface.name in model.oxm_map:
759 oxm_entry = model.oxm_map[self.interface.name]
Andreas Wundsama0981022013-10-02 18:15:06 -0700760 virtual_members += [
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700761 JavaVirtualMember(self, "matchField", java_type.make_match_field_jtype(oxm_entry.type_name), "MatchField.%s" % oxm_entry.value),
762 JavaVirtualMember(self, "masked", java_type.boolean, "true" if oxm_entry.masked else "false"),
Andreas Wundsam661a2222013-11-05 17:18:59 -0800763 ]
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700764 else:
Andreas Wundsama0981022013-10-02 18:15:06 -0700765 virtual_members += [
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700766 JavaVirtualMember(self, "matchField", java_type.make_match_field_jtype(), "null"),
767 JavaVirtualMember(self, "masked", java_type.boolean, "false"),
Andreas Wundsam661a2222013-11-05 17:18:59 -0800768 ]
Andreas Wundsama0981022013-10-02 18:15:06 -0700769 if not find(lambda m: m.name == "version", self.ir_model_members):
770 virtual_members.append(JavaVirtualMember(self, "version", java_type.of_version, "OFVersion.%s" % self.version.constant_version))
771
772 return tuple(virtual_members)
Andreas Wundsam27303462013-07-16 12:52:35 -0700773
Andreas Wundsam661a2222013-11-05 17:18:59 -0800774 @memoize
775 def member_by_name(self, name):
776 return find(lambda m: m.name == name, self.members)
777
Andreas Wundsam27303462013-07-16 12:52:35 -0700778 def all_versions(self):
779 return [ JavaOFVersion(int_version)
780 for int_version in of_g.unified[self.c_name]
781 if int_version != 'union' and int_version != 'object_id' ]
782
783 def version_is_inherited(self, version):
784 return 'use_version' in of_g.unified[self.ir_class.name][version.int_version]
785
786 def inherited_from(self, version):
787 return JavaOFVersion(of_g.unified[self.ir_class.name][version.int_version]['use_version'])
788
789 @property
790 def is_virtual(self):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700791 return self.ir_class.virtual # type_maps.class_is_virtual(self.c_name) or self.ir_class.virtual
792
793 @property
794 def discriminator(self):
795 return find(lambda m: isinstance(m, OFDiscriminatorMember), self.ir_class.members)
Andreas Wundsam27303462013-07-16 12:52:35 -0700796
797 @property
798 def is_extension(self):
799 return type_maps.message_is_extension(self.c_name, -1)
800
Andreas Wundsam758c9cc2013-08-01 22:16:06 -0700801 @property
802 def align(self):
803 return int(self.ir_class.params['align']) if 'align' in self.ir_class.params else 0
804
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700805 @property
Andreas Wundsam5da68512013-10-22 22:18:00 -0700806 def length_includes_align(self):
807 return self.ir_class.params['length_includes_align'] == "True" if 'length_includes_align' in self.ir_class.params else False
808
809 @property
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700810 @memoize
811 def superclass(self):
812 return find(lambda c: c.version == self.version and c.c_name == self.ir_class.superclass, model.all_classes)
813
814 @property
815 @memoize
816 def subclasses(self):
817 return [ c for c in model.all_classes if c.version == self.version and c.ir_class.superclass == self.c_name ]
818
Andreas Wundsam27303462013-07-16 12:52:35 -0700819#######################################################################
820### Member
821#######################################################################
822
823
824class JavaMember(object):
825 """ Models a property (member) of an openflow class. """
826 def __init__(self, msg, name, java_type, member):
827 self.msg = msg
828 self.name = name
829 self.java_type = java_type
830 self.member = member
831 self.c_name = self.member.name if(hasattr(self.member, "name")) else ""
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700832
833 @property
834 def title_name(self):
835 return self.name[0].upper() + self.name[1:]
836
837 @property
838 def constant_name(self):
839 return self.c_name.upper()
840
841 @property
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700842 def getter_name(self):
843 return ("is" if self.java_type.public_type == "boolean" else "get") + self.title_name
844
845 @property
846 def setter_name(self):
847 return "set" + self.title_name
848
849 @property
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700850 def default_name(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700851 if self.is_fixed_value:
852 return self.constant_name
853 else:
854 return "DEFAULT_"+self.constant_name
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700855
856 @property
857 def default_value(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700858 if self.is_fixed_value:
859 return self.enum_value
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700860 else:
Andreas Wundsam142dd2a2013-09-23 14:47:37 -0700861 default = self.java_type.default_op(self.msg.version)
862 if default == "null" and not self.is_nullable:
863 return None
864 else:
865 return default
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700866
867 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700868 def enum_value(self):
869 if self.name == "version":
870 return "OFVersion.%s" % self.msg.version.constant_version
871
872 java_type = self.java_type.public_type;
873 try:
874 global model
875 enum = model.enum_by_name(java_type)
876 entry = enum.entry_by_version_value(self.msg.version, self.value)
877 return "%s.%s" % ( enum.name, entry.name)
878 except KeyError, e:
879 print e.message
880 return self.value
881
882 @property
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700883 def is_pad(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700884 return isinstance(self.member, OFPadMember)
885
886 def is_type_value(self, version=None):
887 if(version==None):
888 return any(self.is_type_value(version) for version in self.msg.all_versions)
889 try:
890 return self.c_name in get_type_values(self.msg.c_name, version.int_version)
891 except:
892 return False
893
894 @property
895 def is_field_length_value(self):
896 return isinstance(self.member, OFFieldLengthMember)
897
898 @property
Andreas Wundsam001b1822013-08-02 22:25:55 -0700899 def is_discriminator(self):
900 return isinstance(self.member, OFDiscriminatorMember)
901
902 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700903 def is_length_value(self):
904 return isinstance(self.member, OFLengthMember)
905
906 @property
907 def is_public(self):
908 return not (self.is_pad or self.is_length_value)
909
910 @property
911 def is_data(self):
912 return isinstance(self.member, OFDataMember) and self.name != "version"
913
914 @property
915 def is_fixed_value(self):
916 return hasattr(self.member, "value") or self.name == "version" \
917 or ( self.name == "length" and self.msg.is_fixed_length) \
918 or ( self.name == "len" and self.msg.is_fixed_length)
919
920 @property
921 def value(self):
922 if self.name == "version":
923 return self.msg.version.int_version
924 elif self.name == "length" or self.name == "len":
925 return self.msg.length
Andreas Wundsam27303462013-07-16 12:52:35 -0700926 else:
Andreas Wundsam001b1822013-08-02 22:25:55 -0700927 return self.java_type.format_value(self.member.value)
928
929 @property
930 def priv_value(self):
931 if self.name == "version":
Andreas Wundsam22ba3af2013-10-04 16:00:30 -0700932 return self.java_type.format_value(self.msg.version.int_version, pub_type=False)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700933 elif self.name == "length" or self.name == "len":
Andreas Wundsam22ba3af2013-10-04 16:00:30 -0700934 return self.java_type.format_value(self.msg.length, pub_type=False)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700935 else:
936 return self.java_type.format_value(self.member.value, pub_type=False)
937
Andreas Wundsam27303462013-07-16 12:52:35 -0700938
939 @property
940 def is_writeable(self):
941 return self.is_data and not self.name in model.write_blacklist[self.msg.name]
942
943 def get_type_value_info(self, version):
944 return get_type_values(msg.c_name, version.int_version)[self.c_name]
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700945
946 @property
947 def length(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700948 if hasattr(self.member, "length"):
949 return self.member.length
950 else:
Andreas Wundsam5204de22013-07-30 11:34:45 -0700951 count, base = loxi_utils.type_dec_to_count_base(self.member.type)
Andreas Wundsam27303462013-07-16 12:52:35 -0700952 return of_g.of_base_types[base]['bytes'] * count
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700953
954 @staticmethod
Andreas Wundsam27303462013-07-16 12:52:35 -0700955 def for_of_member(java_class, member):
956 if isinstance(member, OFPadMember):
957 return JavaMember(None, "", None, member)
958 else:
959 if member.name == 'len':
960 name = 'length'
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700961 elif member.name == 'value_mask':
962 name = 'mask'
Andreas Wundsamacd57d52013-10-18 17:35:01 -0700963 elif member.name == 'group_id':
964 name = 'group'
Andreas Wundsam27303462013-07-16 12:52:35 -0700965 else:
966 name = java_type.name_c_to_camel(member.name)
967 j_type = java_type.convert_to_jtype(java_class.c_name, member.name, member.oftype)
968 return JavaMember(java_class, name, j_type, member)
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700969
970 @property
971 def is_universal(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700972 if not self.msg.c_name in of_g.unified:
973 print("%s not self.unified" % self.msg.c_name)
974 return False
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700975 for version in of_g.unified[self.msg.c_name]:
976 if version == 'union' or version =='object_id':
977 continue
978 if 'use_version' in of_g.unified[self.msg.c_name][version]:
979 continue
980
Andreas Wundsam27303462013-07-16 12:52:35 -0700981 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 -0700982 return False
983 return True
984
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700985 @property
986 def is_virtual(self):
987 return False
988
Andreas Wundsam27303462013-07-16 12:52:35 -0700989 def __hash__(self):
990 return hash(self.name)
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700991
Andreas Wundsam27303462013-07-16 12:52:35 -0700992 def __eq__(self, other):
993 if other is None or type(self) != type(other):
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700994 return False
Andreas Wundsam27303462013-07-16 12:52:35 -0700995 return (self.name,) == (other.name,)
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700996
Andreas Wundsamf1949682013-09-23 14:48:31 -0700997 @property
998 def is_nullable(self):
999 return self.name in model.nullable_map[self.msg.name]
1000
1001
Andreas Wundsam2be7da52013-08-22 07:34:25 -07001002class JavaVirtualMember(JavaMember):
1003 """ Models a virtual property (member) of an openflow class that is not backed by a loxi ir member """
Andreas Wundsam4b8661f2013-11-05 17:17:28 -08001004 def __init__(self, msg, name, java_type, value=None, custom_template=None):
Andreas Wundsam2be7da52013-08-22 07:34:25 -07001005 JavaMember.__init__(self, msg, name, java_type, member=None)
1006 self._value = value
Andreas Wundsam4b8661f2013-11-05 17:17:28 -08001007 self.custom_template = custom_template
Andreas Wundsam2be7da52013-08-22 07:34:25 -07001008
1009 @property
1010 def is_fixed_value(self):
1011 return True
1012
1013 @property
1014 def value(self):
1015 return self._value
1016
1017 @property
1018 def priv_value(self):
1019 return self._value
1020
1021
1022 @property
1023 def is_universal(self):
1024 return True
1025
1026 @property
1027 def is_virtual(self):
1028 return True
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001029
1030#######################################################################
1031### Unit Test
1032#######################################################################
1033
Yotam Harchol466b3212013-08-15 12:14:46 -07001034class JavaUnitTestSet(object):
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001035 def __init__(self, java_class):
1036 self.java_class = java_class
Yotam Harchol466b3212013-08-15 12:14:46 -07001037 first_data_file_name = "of{version}/{name}.data".format(version=java_class.version.of_version,
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001038 name=java_class.c_name[3:])
Andreas Wundsamaa4f3cd2013-10-22 22:19:08 -07001039 glob_file_name = "of{version}/{name}__*.data".format(version=java_class.version.of_version,
1040 name=java_class.c_name[3:])
Yotam Harchol466b3212013-08-15 12:14:46 -07001041 test_class_name = self.java_class.name + "Test"
1042 self.test_units = []
1043 if test_data.exists(first_data_file_name):
1044 self.test_units.append(JavaUnitTest(java_class, first_data_file_name, test_class_name))
Andreas Wundsamaa4f3cd2013-10-22 22:19:08 -07001045
Yotam Harchol466b3212013-08-15 12:14:46 -07001046 i = 1
Andreas Wundsamaa4f3cd2013-10-22 22:19:08 -07001047 for f in test_data.glob(glob_file_name):
1048 m = re.match(".*__(.*).data", f)
1049 if m:
1050 suffix = java_type.name_c_to_caps_camel(m.group(1))
1051 else:
1052 suffix = str(i)
1053 i += 1
1054 test_class_name = self.java_class.name + suffix + "Test"
1055 self.test_units.append(JavaUnitTest(java_class, f, test_class_name))
Andreas Wundsamf1949682013-09-23 14:48:31 -07001056
Yotam Harchol466b3212013-08-15 12:14:46 -07001057 @property
1058 def package(self):
1059 return self.java_class.package
1060
1061 @property
1062 def has_test_data(self):
1063 return len(self.test_units) > 0
1064
1065 @property
1066 def length(self):
1067 return len(self.test_units)
Andreas Wundsamf1949682013-09-23 14:48:31 -07001068
Yotam Harchol466b3212013-08-15 12:14:46 -07001069 def get_test_unit(self, i):
1070 return self.test_units[i]
1071
1072
1073class JavaUnitTest(object):
1074 def __init__(self, java_class, file_name=None, test_class_name=None):
1075 self.java_class = java_class
1076 if file_name is None:
1077 self.data_file_name = "of{version}/{name}.data".format(version=java_class.version.of_version,
1078 name=java_class.c_name[3:])
1079 else:
1080 self.data_file_name = file_name
1081 if test_class_name is None:
1082 self.test_class_name = self.java_class.name + "Test"
1083 else:
1084 self.test_class_name = test_class_name
Andreas Wundsamf1949682013-09-23 14:48:31 -07001085
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001086 @property
1087 def package(self):
1088 return self.java_class.package
1089
1090 @property
1091 def name(self):
Yotam Harchol466b3212013-08-15 12:14:46 -07001092 return self.test_class_name
Yotam Harchol595c6442013-09-27 16:29:08 -07001093
1094 @property
1095 def interface(self):
1096 return self.java_class.interface
1097
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001098 @property
1099 def has_test_data(self):
1100 return test_data.exists(self.data_file_name)
1101
1102 @property
1103 @memoize
1104 def test_data(self):
1105 return test_data.read(self.data_file_name)
1106
1107
Andreas Wundsam27303462013-07-16 12:52:35 -07001108#######################################################################
1109### Enums
1110#######################################################################
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001111
Andreas Wundsam27303462013-07-16 12:52:35 -07001112class JavaEnum(object):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001113 def __init__(self, c_name, version_enum_map):
Andreas Wundsam27303462013-07-16 12:52:35 -07001114 self.c_name = c_name
Andreas Wundsambe168f72013-08-03 22:49:35 -07001115
Rob Vaterlausfeee3712013-09-30 11:24:19 -07001116 self.name = "OF" + java_type.name_c_to_caps_camel("_".join(c_name.split("_")[1:]))
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001117
Andreas Wundsam27303462013-07-16 12:52:35 -07001118 # Port_features has constants that start with digits
1119 self.name_prefix = "PF_" if self.name == "OFPortFeatures" else ""
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001120
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001121 self.version_enums = version_enum_map
1122
1123 entry_name_version_value_map = OrderedDefaultDict(lambda: OrderedDict())
1124 for version, ir_enum in version_enum_map.items():
1125 for ir_entry in ir_enum.entries:
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001126 entry_name_version_value_map[ir_entry.name][version] = ir_entry.value
1127
Andreas Wundsam27303462013-07-16 12:52:35 -07001128 self.entries = [ JavaEnumEntry(self, name, version_value_map)
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001129 for (name, version_value_map) in entry_name_version_value_map.items() ]
Andreas Wundsam43526532013-08-01 22:03:50 -07001130
1131 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 -07001132 self.package = "org.projectfloodlight.openflow.protocol"
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001133
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -07001134 self.metadata = model.enum_metadata_map[self.name]
1135
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001136 def wire_type(self, version):
1137 ir_enum = self.version_enums[version]
1138 if "wire_type" in ir_enum.params:
1139 return java_type.convert_enum_wire_type_to_jtype(ir_enum.params["wire_type"])
1140 else:
1141 return java_type.u8
1142
1143 @property
Andreas Wundsam7cfeac32013-09-17 13:53:48 -07001144 @memoize
1145 def is_bitmask(self):
1146 return any(ir_enum.is_bitmask for ir_enum in self.version_enums.values())
1147
1148 @property
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001149 def versions(self):
1150 return self.version_enums.keys()
1151
Andreas Wundsam27303462013-07-16 12:52:35 -07001152 @memoize
1153 def entry_by_name(self, name):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -07001154 res = find(lambda e: e.name == name, self.entries)
1155 if res:
1156 return res
1157 else:
Andreas Wundsam27303462013-07-16 12:52:35 -07001158 raise KeyError("Enum %s: no entry with name %s" % (self.name, name))
1159
1160 @memoize
1161 def entry_by_c_name(self, name):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -07001162 res = find(lambda e: e.c_name == name, self.entries)
1163 if res:
1164 return res
1165 else:
Andreas Wundsam27303462013-07-16 12:52:35 -07001166 raise KeyError("Enum %s: no entry with c_name %s" % (self.name, name))
1167
1168 @memoize
1169 def entry_by_version_value(self, version, value):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -07001170 res = find(lambda e: e.values[version] == value if version in e.values else False, self.entries)
1171 if res:
1172 return res
1173 else:
Andreas Wundsam27303462013-07-16 12:52:35 -07001174 raise KeyError("Enum %s: no entry with version %s, value %s" % (self.name, version, value))
1175
1176# values: Map JavaVersion->Value
1177class JavaEnumEntry(object):
1178 def __init__(self, enum, name, values):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001179 self.enum = enum
Andreas Wundsam27303462013-07-16 12:52:35 -07001180 self.name = enum.name_prefix + "_".join(name.split("_")[1:]).upper()
1181 self.values = values
1182
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -07001183 @property
1184 def constructor_params(self):
Rob Vaterlaus6035bf52013-09-17 19:32:38 -07001185 return [ m.value(self) for m in self.enum.metadata.properties ]
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -07001186
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001187 def has_value(self, version):
1188 return version in self.values
1189
Andreas Wundsam27303462013-07-16 12:52:35 -07001190 def value(self, version):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001191 return self.values[version]
1192
1193 def format_value(self, version):
1194 res = self.enum.wire_type(version).format_value(self.values[version])
Andreas Wundsam27303462013-07-16 12:52:35 -07001195 return res
1196
Andreas Wundsam27303462013-07-16 12:52:35 -07001197 def all_values(self, versions, not_present=None):
1198 return [ self.values[version] if version in self.values else not_present for version in versions ]
Andreas Wundsamc1acfcc2013-09-20 13:10:20 -07001199
1200 @property
1201 @memoize
1202 def masked_enum_group(self):
1203 group = find(lambda g: self.name in g.members, model.masked_enum_groups[self.enum.name])
1204 return group
1205
1206 @property
1207 @memoize
1208 def is_mask(self):
1209 return any(self.name == g.mask for g in model.masked_enum_groups[self.enum.name])