blob: f2a1c01eccc4d6b539852f1bce2fdf5933359029 [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})
Rob Vaterlausfbd5b6b2013-09-24 15:55:47 -0700143 enum_blacklist = set(("OFDefinitions", "OFPortNo",))
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),
232 "OFOxmMplsTcMasked": OxmMapEntry("U8", "MPLS_TC", True)
233 }
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700234
Andreas Wundsamf1949682013-09-23 14:48:31 -0700235 # Registry of nullable properties:
236 # ${java_class_name} -> set(${java_property_name})
237 nullable_map = defaultdict(lambda: set(),
238 )
239
240 # represents a subgroup of a bitmask enum that is actualy a normal enumerable within a masked part of the enum
241 # e.g., the flags STP.* in OF1.0 port state are bit mask entries, but instead enumerables according to the mask "STP_MASK"
242 # name: a name for the group
243 # mask: java name of the enum entry that defines the mask
244 # members: set of names of the members of the group
Andreas Wundsamc1acfcc2013-09-20 13:10:20 -0700245 MaskedEnumGroup = namedtuple("MaskedEnumGroup", ("name", "mask", "members"))
246
Andreas Wundsamf1949682013-09-23 14:48:31 -0700247 # registry of MaskedEnumGroups (see above).
248 # map: ${java_enum_name}: tuple(MaskedEnumGroup)
Andreas Wundsamc1acfcc2013-09-20 13:10:20 -0700249 masked_enum_groups = defaultdict(lambda: (),
250 OFPortState= (MaskedEnumGroup("stp_flags", mask="STP_MASK", members=set(("STP_LISTEN", "STP_LEARN", "STP_FORWARD", "STP_BLOCK"))), )
251 )
252
Andreas Wundsamf1949682013-09-23 14:48:31 -0700253 # represents a metadata property associated with an EnumClass
254 # name:
Rob Vaterlaus6035bf52013-09-17 19:32:38 -0700255 class OFEnumPropertyMetadata(namedtuple("OFEnumPropertyMetadata", ("name", "type", "value"))):
Andreas Wundsamf1949682013-09-23 14:48:31 -0700256 """
257 represents a metadata property associated with an Enum Class
258 @param name name of metadata property
259 @param type java_type instance describing the type
260 @value: Generator function f(entry) that generates the value
261 """
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -0700262 @property
263 def variable_name(self):
264 return self.name[0].lower() + self.name[1:]
265
266 @property
267 def getter_name(self):
Rob Vaterlausbda9ce62013-09-17 14:45:48 -0700268 prefix = "is" if self.type == java_type.boolean else "get"
269 return prefix+self.name
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -0700270
Andreas Wundsamf1949682013-09-23 14:48:31 -0700271 """ Metadata container. """
Rob Vaterlaus6035bf52013-09-17 19:32:38 -0700272 OFEnumMetadata = namedtuple("OFEnumMetadata", ("properties", "to_string"))
273
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -0700274 def gen_port_speed(enum_entry):
Andreas Wundsamf1949682013-09-23 14:48:31 -0700275 """ Generator function for OFortFeatures.PortSpeed"""
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -0700276 splits = enum_entry.name.split("_")
277 if len(splits)>=2:
278 m = re.match(r'\d+[MGTP]B', splits[1])
279 if m:
280 return "PortSpeed.SPEED_{}".format(splits[1])
281 return "PortSpeed.SPEED_NONE";
282
Rob Vaterlausbda9ce62013-09-17 14:45:48 -0700283 def gen_stp_state(enum_entry):
Andreas Wundsamf1949682013-09-23 14:48:31 -0700284 """ Generator function for OFPortState.StpState"""
Rob Vaterlausbda9ce62013-09-17 14:45:48 -0700285 splits = enum_entry.name.split("_")
286 if len(splits)>=1:
287 if splits[0] == "STP":
288 return "true"
289 return "false"
290
Andreas Wundsamf1949682013-09-23 14:48:31 -0700291 # registry for metadata properties for enums
292 # map: ${java_enum_name}: OFEnumMetadata
Rob Vaterlaus6035bf52013-09-17 19:32:38 -0700293 enum_metadata_map = defaultdict(lambda: JavaModel.OFEnumMetadata((), None),
Rob Vaterlausa049dee2013-09-18 16:18:37 -0700294 OFPortFeatures = OFEnumMetadata((OFEnumPropertyMetadata("PortSpeed", java_type.port_speed, gen_port_speed),), None),
295 OFPortState = OFEnumMetadata((OFEnumPropertyMetadata("StpState", java_type.boolean, gen_stp_state),), None),
Rob Vaterlaus4d311942013-09-24 13:41:44 -0700296 OFErrorCode = OFEnumMetadata((OFEnumPropertyMetadata("ErrorType", java_type.error_type, gen_error_type),), None),
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -0700297 )
298
Andreas Wundsam27303462013-07-16 12:52:35 -0700299 @property
300 @memoize
301 def versions(self):
302 return OrderedSet( JavaOFVersion(raw_version) for raw_version in of_g.target_version_list )
303
304 @property
305 @memoize
306 def interfaces(self):
Andreas Wundsam5204de22013-07-30 11:34:45 -0700307 version_map_per_class = collections.OrderedDict()
Andreas Wundsam27303462013-07-16 12:52:35 -0700308
309 for raw_version, of_protocol in of_g.ir.items():
310 jversion = JavaOFVersion(of_protocol.wire_version)
311
312 for of_class in of_protocol.classes:
Andreas Wundsam5204de22013-07-30 11:34:45 -0700313 if not of_class.name in version_map_per_class:
314 version_map_per_class[of_class.name] = collections.OrderedDict()
315
Andreas Wundsam27303462013-07-16 12:52:35 -0700316 version_map_per_class[of_class.name][jversion] = of_class
317
318 interfaces = []
319 for class_name, version_map in version_map_per_class.items():
320 interfaces.append(JavaOFInterface(class_name, version_map))
321
Andreas Wundsambe168f72013-08-03 22:49:35 -0700322 interfaces = [ i for i in interfaces if i.name not in self.interface_blacklist ]
323
Andreas Wundsam27303462013-07-16 12:52:35 -0700324 return interfaces
325
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700326 @memoize
327 def interface_by_name(self, name):
328 return find(lambda i: erase_type_annotation(i.name) == erase_type_annotation(name), self.interfaces)
329
Andreas Wundsam27303462013-07-16 12:52:35 -0700330 @property
331 @memoize
Andreas Wundsam001b1822013-08-02 22:25:55 -0700332 def all_classes(self):
333 return [clazz for interface in self.interfaces for clazz in interface.versioned_classes]
334
335 @property
336 @memoize
Andreas Wundsam27303462013-07-16 12:52:35 -0700337 def enums(self):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700338 name_version_enum_map = OrderedDefaultDict(lambda: OrderedDict())
Andreas Wundsam27303462013-07-16 12:52:35 -0700339
340 for version in self.versions:
341 of_protocol = of_g.ir[version.int_version]
342 for enum in of_protocol.enums:
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700343 name_version_enum_map[enum.name][version] = enum
Andreas Wundsam27303462013-07-16 12:52:35 -0700344
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700345 enums = [ JavaEnum(name, version_enum_map) for name, version_enum_map,
346 in name_version_enum_map.items() ]
Andreas Wundsam27303462013-07-16 12:52:35 -0700347
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700348 # inelegant - need java name here
349 enums = [ enum for enum in enums if enum.name not in self.enum_blacklist ]
Andreas Wundsam27303462013-07-16 12:52:35 -0700350 return enums
351
352 @memoize
353 def enum_by_name(self, name):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700354 res = find(lambda e: e.name == name, self.enums)
355 if not res:
Andreas Wundsam27303462013-07-16 12:52:35 -0700356 raise KeyError("Could not find enum with name %s" % name)
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700357 return res
Andreas Wundsam27303462013-07-16 12:52:35 -0700358
Andreas Wundsam5204de22013-07-30 11:34:45 -0700359 @property
360 @memoize
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700361 def of_factories(self):
Yotam Harchol791e4882013-09-05 16:32:56 -0700362 prefix = "org.projectfloodlight.openflow.protocol"
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700363
364 factories = OrderedDict()
365
366 sub_factory_classes = ("OFAction", "OFInstruction", "OFMeterBand", "OFOxm", "OFQueueProp")
367 for base_class in sub_factory_classes:
368 package = base_class[2:].lower()
369 remove_prefix = base_class[2].lower() + base_class[3:]
370
371 # HACK need to have a better way to deal with parameterized base classes
372 annotated_base_class = base_class + "<?>" if base_class == "OFOxm" else base_class
373
374 factories[base_class] = OFFactory(package="%s.%s" % (prefix, package),
375 name=base_class + "s", members=[], remove_prefix=remove_prefix, base_class=annotated_base_class, sub_factories={})
376
377 factories[""] = OFFactory(
378 package=prefix,
Andreas Wundsam5204de22013-07-30 11:34:45 -0700379 name="OFFactory",
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700380 remove_prefix="",
381 members=[], base_class="OFMessage", sub_factories=OrderedDict(
382 ("{}{}s".format(n[2].lower(), n[3:]), "{}s".format(n)) for n in sub_factory_classes ))
383
384 for i in self.interfaces:
385 for n, factory in factories.items():
386 if n == "":
387 factory.members.append(i)
388 break
389 else:
390 super_class = self.interface_by_name(n)
391 if i.is_instance_of(super_class):
392 factory.members.append(i)
393 break
394 return factories.values()
Andreas Wundsam5204de22013-07-30 11:34:45 -0700395
396 def generate_class(self, clazz):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700397 """ return wether or not to generate implementation class clazz.
398 Now true for everything except OFTableModVer10.
399 @param clazz JavaOFClass instance
400 """
Andreas Wundsam43526532013-08-01 22:03:50 -0700401 if clazz.interface.name.startswith("OFMatchV"):
402 return True
Andreas Wundsam001b1822013-08-02 22:25:55 -0700403 elif clazz.name == "OFTableModVer10":
404 # tablemod ver 10 is a hack and has no oftype defined
405 return False
Andreas Wundsam5204de22013-07-30 11:34:45 -0700406 if loxi_utils.class_is_message(clazz.interface.c_name):
407 return True
408 if loxi_utils.class_is_oxm(clazz.interface.c_name):
409 return True
Andreas Wundsam43526532013-08-01 22:03:50 -0700410 if loxi_utils.class_is_action(clazz.interface.c_name):
411 return True
412 if loxi_utils.class_is_instruction(clazz.interface.c_name):
413 return True
Andreas Wundsam5204de22013-07-30 11:34:45 -0700414 else:
Andreas Wundsam001b1822013-08-02 22:25:55 -0700415 return True
Andreas Wundsam5204de22013-07-30 11:34:45 -0700416
417
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700418class OFFactory(namedtuple("OFFactory", ("package", "name", "members", "remove_prefix", "base_class", "sub_factories"))):
Andreas Wundsam5204de22013-07-30 11:34:45 -0700419 @property
420 def factory_classes(self):
421 return [ OFFactoryClass(
Yotam Harchol791e4882013-09-05 16:32:56 -0700422 package="org.projectfloodlight.openflow.protocol.ver{}".format(version.of_version),
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700423 name="{}Ver{}".format(self.name, version.of_version),
Andreas Wundsam5204de22013-07-30 11:34:45 -0700424 interface=self,
425 version=version
426 ) for version in model.versions ]
427
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700428 def method_name(self, member, builder=True):
429 n = member.variable_name
430 if n.startswith(self.remove_prefix):
431 n = n[len(self.remove_prefix):]
432 n = n[0].lower() + n[1:]
433 if builder:
434 return "build" + n[0].upper() + n[1:]
435 else:
436 return n
Andreas Wundsam5204de22013-07-30 11:34:45 -0700437
438OFGenericClass = namedtuple("OFGenericClass", ("package", "name"))
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700439class OFFactoryClass(namedtuple("OFFactoryClass", ("package", "name", "interface", "version"))):
440 @property
441 def base_class(self):
442 return self.interface.base_class
443
444 @property
445 def versioned_base_class(self):
446 base_class_interface = model.interface_by_name(self.interface.base_class)
447 if base_class_interface and base_class_interface.has_version(self.version):
448 return base_class_interface.versioned_class(self.version)
449 else:
450 return None
Andreas Wundsam5204de22013-07-30 11:34:45 -0700451
Andreas Wundsam27303462013-07-16 12:52:35 -0700452model = JavaModel()
453
454#######################################################################
455### OFVersion
456#######################################################################
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700457
458class JavaOFVersion(object):
459 """ Models a version of OpenFlow. contains methods to convert the internal
460 Loxi version to a java constant / a string """
461 def __init__(self, int_version):
462 self.int_version = int(int_version)
463
464 @property
465 def of_version(self):
466 return "1" + str(int(self.int_version) - 1)
467
468 @property
469 def constant_version(self):
470 return "OF_" + self.of_version
471
Andreas Wundsam27303462013-07-16 12:52:35 -0700472 def __repr__(self):
473 return "JavaOFVersion(%d)" % self.int_version
474
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700475 def __str__(self):
476 return of_g.param_version_names[self.int_version]
477
Andreas Wundsam27303462013-07-16 12:52:35 -0700478 def __hash__(self):
479 return hash(self.int_version)
480
481 def __eq__(self, other):
482 if other is None or type(self) != type(other):
483 return False
484 return (self.int_version,) == (other.int_version,)
485
486#######################################################################
487### Interface
488#######################################################################
489
490class JavaOFInterface(object):
491 """ Models an OpenFlow Message class for the purpose of the java class.
492 Version agnostic, in contrast to the loxi_ir python model.
493 """
494 def __init__(self, c_name, version_map):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700495 """"
496 @param c_name: loxi style name (e.g., of_flow_add)
497 @param version_map map of { JavaOFVersion: OFClass (from loxi_ir) }
498 """
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700499 self.c_name = c_name
Andreas Wundsam27303462013-07-16 12:52:35 -0700500 self.version_map = version_map
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700501 # name: the Java Type name, e.g., OFFlowAdd
Andreas Wundsam001b1822013-08-02 22:25:55 -0700502 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 -0700503 # variable_name name to use for variables of this type. i.e., flowAdd
Andreas Wundsam5204de22013-07-30 11:34:45 -0700504 self.variable_name = self.name[2].lower() + self.name[3:]
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700505 self.title_name = self.variable_name[0].upper() + self.variable_name[1:]
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700506 # name for use in constants: FLOW_ADD
Andreas Wundsam27303462013-07-16 12:52:35 -0700507 self.constant_name = c_name.upper().replace("OF_", "")
508
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700509 pck_suffix, parent_interface, self.type_annotation = self.class_info()
Yotam Harchol791e4882013-09-05 16:32:56 -0700510 self.package = "org.projectfloodlight.openflow.protocol.%s" % pck_suffix if pck_suffix else "org.projectfloodlight.openflow.protocol"
Andreas Wundsam27303462013-07-16 12:52:35 -0700511 if self.name != parent_interface:
512 self.parent_interface = parent_interface
513 else:
514 self.parent_interface = None
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700515
Andreas Wundsama705c6b2013-09-04 11:21:09 -0700516 @property
517 @memoize
518 def all_parent_interfaces(self):
519 return [ "OFObject" ] + \
520 ([ self.parent_interface ] if self.parent_interface else [] )+ \
521 self.additional_parent_interfaces
522 @property
523 @memoize
524 def additional_parent_interfaces(self):
525 if loxi_utils.class_is_message(self.c_name) and not self.is_virtual:
526 m = re.match(r'(.*)Request$', self.name)
527 if m:
528 reply_name = m.group(1) + "Reply"
529 if model.interface_by_name(reply_name):
530 return ["OFRequest<%s>" % reply_name ]
531 return []
532
533
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700534 def is_instance_of(self, other_class):
535 if self == other_class:
536 return True
537 parent = self.super_class
538 if parent is None:
539 return False
540 else:
541 return parent.is_instance_of(other_class)
542
543 @property
544 def super_class(self):
545 if not self.parent_interface:
546 return None
547 else:
548 return model.interface_by_name(self.parent_interface)
549
550
551 def inherited_declaration(self, type_spec="?"):
552 if self.type_annotation:
553 return "%s<%s>" % (self.name, type_spec)
554 else:
555 return "%s" % self.name
556
557 @property
558 def type_variable(self):
559 if self.type_annotation:
560 return "<T>"
561 else:
562 return "";
563
Andreas Wundsam27303462013-07-16 12:52:35 -0700564 def class_info(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700565 """ return tuple of (package_prefix, parent_class) for the current JavaOFInterface"""
566 # FIXME: This duplicates inheritance information that is now available in the loxi_ir
567 # model (note, that the loxi model is on versioned classes). Should check/infer the
568 # inheritance information from the versioned lox_ir classes.
Andreas Wundsam001b1822013-08-02 22:25:55 -0700569 if re.match(r'OF.+StatsRequest$', self.name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700570 return ("", "OFStatsRequest", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700571 elif re.match(r'OF.+StatsReply$', self.name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700572 return ("", "OFStatsReply", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700573 elif re.match(r'OFFlow(Add|Modify(Strict)?|Delete(Strict)?)$', self.name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700574 return ("", "OFFlowMod", None)
Andreas Wundsama705c6b2013-09-04 11:21:09 -0700575 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 -0700576 return ("", "OFBsnHeader", None)
Andreas Wundsama705c6b2013-09-04 11:21:09 -0700577 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 -0700578 return ("", "OFNiciraHeader", None)
Andreas Wundsamb1da3f62013-09-04 18:46:03 -0700579 elif self.name == "OFBsnHeader" or self.name =="OFNiciraHeader":
580 return ("", "OFExperimenter", None)
Andreas Wundsam43526532013-08-01 22:03:50 -0700581 elif re.match(r'OFMatch.*', self.name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700582 return ("", "Match", None)
Andreas Wundsam5204de22013-07-30 11:34:45 -0700583 elif loxi_utils.class_is_message(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700584 return ("", "OFMessage", None)
Andreas Wundsam5204de22013-07-30 11:34:45 -0700585 elif loxi_utils.class_is_action(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700586 if re.match(r'OFActionBsn.+', self.name):
587 return ("action", "OFActionBsn", None)
588 elif re.match(r'OFActionNicira.+', self.name):
589 return ("action", "OFActionNicira", None)
Andreas Wundsamb1da3f62013-09-04 18:46:03 -0700590 elif self.name == "OFActionBsn" or self.name == "OFActionNicira":
591 return ("action", "OFActionExperimenter", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700592 else:
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700593 return ("action", "OFAction", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700594 elif re.match(r'OFBsnVport.+$', self.name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700595 return ("", "OFBsnVport", None)
596 elif self.name == "OFOxm":
597 return ("oxm", None, "T extends OFValueType<T>")
Andreas Wundsam5204de22013-07-30 11:34:45 -0700598 elif loxi_utils.class_is_oxm(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700599 if self.name in model.oxm_map:
600 return ("oxm", "OFOxm<%s>" % model.oxm_map[self.name].type_name, None)
601 else:
602 return ("oxm", "OFOxm", None)
Andreas Wundsam5204de22013-07-30 11:34:45 -0700603 elif loxi_utils.class_is_instruction(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700604 return ("instruction", "OFInstruction", None)
Andreas Wundsam5204de22013-07-30 11:34:45 -0700605 elif loxi_utils.class_is_meter_band(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700606 return ("meterband", "OFMeterBand", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700607 elif loxi_utils.class_is_queue_prop(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700608 return ("queueprop", "OFQueueProp", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700609 elif loxi_utils.class_is_hello_elem(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700610 return ("", "OFHelloElem", None)
Andreas Wundsam27303462013-07-16 12:52:35 -0700611 else:
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700612 return ("", None, None)
613
614 @property
615 @memoize
616 def writeable_members(self):
617 return [ m for m in self.members if m.is_writeable ]
Andreas Wundsam27303462013-07-16 12:52:35 -0700618
619 @property
620 @memoize
621 def members(self):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700622 return self.ir_model_members + self.virtual_members
623
624 @property
625 @memoize
626 def ir_model_members(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700627 """return a list of all members to be exposed by this interface. Corresponds to
628 the union of the members of the vesioned classes without length, fieldlength
629 and pads (those are handled automatically during (de)serialization and not exposed"""
Andreas Wundsam27303462013-07-16 12:52:35 -0700630 all_versions = []
631 member_map = collections.OrderedDict()
632
633 for (version, of_class) in self.version_map.items():
634 for of_member in of_class.members:
635 if isinstance(of_member, OFLengthMember) or \
636 isinstance(of_member, OFFieldLengthMember) or \
637 isinstance(of_member, OFPadMember):
638 continue
639 if of_member.name not in member_map:
640 member_map[of_member.name] = JavaMember.for_of_member(self, of_member)
641
Andreas Wundsamb1da3f62013-09-04 18:46:03 -0700642 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 -0700643
644 @property
645 def virtual_members(self):
646 if self.name == "OFOxm":
647 return (
648 JavaVirtualMember(self, "value", java_type.generic_t),
649 JavaVirtualMember(self, "mask", java_type.generic_t),
650 JavaVirtualMember(self, "matchField", java_type.make_match_field_jtype("T")),
651 JavaVirtualMember(self, "masked", java_type.boolean),
652 )
653 elif self.parent_interface and self.parent_interface.startswith("OFOxm"):
654 field_type = java_type.make_match_field_jtype(model.oxm_map[self.name].type_name) \
655 if self.name in model.oxm_map \
656 else java_type.make_match_field_jtype()
657
658 return (
659 JavaVirtualMember(self, "matchField", field_type),
660 JavaVirtualMember(self, "masked", java_type.boolean),
661 ) \
662 + \
663 (
664 ( JavaVirtualMember(self, "mask", find(lambda x: x.name == "value", self.ir_model_members).java_type), ) if not find(lambda x: x.name == "mask", self.ir_model_members) else
665 ()
666 )
667 else:
668 return ()
Andreas Wundsam27303462013-07-16 12:52:35 -0700669
670 @property
671 @memoize
672 def is_virtual(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700673 """ Is this interface virtual. If so, do not generate a builder interface """
Andreas Wundsam001b1822013-08-02 22:25:55 -0700674 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 -0700675
676 @property
Andreas Wundsam5204de22013-07-30 11:34:45 -0700677 def is_universal(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700678 """ Is this interface universal, i.e., does it exist in all OF versions? """
Andreas Wundsam5204de22013-07-30 11:34:45 -0700679 return len(self.all_versions) == len(model.versions)
680
681 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700682 @memoize
683 def all_versions(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700684 """ return list of all versions that this interface exists in """
Andreas Wundsam27303462013-07-16 12:52:35 -0700685 return self.version_map.keys()
686
Andreas Wundsam5204de22013-07-30 11:34:45 -0700687 def has_version(self, version):
688 return version in self.version_map
689
Andreas Wundsam27303462013-07-16 12:52:35 -0700690 def versioned_class(self, version):
691 return JavaOFClass(self, version, self.version_map[version])
692
693 @property
694 @memoize
695 def versioned_classes(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700696 return [ self.versioned_class(version) for version in self.all_versions ]
697
698#######################################################################
699### (Versioned) Classes
700#######################################################################
701
702class JavaOFClass(object):
703 """ Models an OpenFlow Message class for the purpose of the java class.
Andreas Wundsamd40ddbf2013-07-25 15:40:47 -0700704 Version specific child of a JavaOFInterface
Andreas Wundsam27303462013-07-16 12:52:35 -0700705 """
706 def __init__(self, interface, version, ir_class):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700707 """
708 @param interface JavaOFInterface instance of the parent interface
709 @param version JavaOFVersion
710 @param ir_class OFClass from loxi_ir
711 """
Andreas Wundsam27303462013-07-16 12:52:35 -0700712 self.interface = interface
713 self.ir_class = ir_class
714 self.c_name = self.ir_class.name
715 self.version = version
716 self.constant_name = self.c_name.upper().replace("OF_", "")
Yotam Harchol791e4882013-09-05 16:32:56 -0700717 self.package = "org.projectfloodlight.openflow.protocol.ver%s" % version.of_version
Andreas Wundsame916d6f2013-07-30 11:33:58 -0700718 self.generated = False
719
720 @property
721 @memoize
722 def unit_test(self):
Yotam Harchol466b3212013-08-15 12:14:46 -0700723 return JavaUnitTestSet(self)
Andreas Wundsam27303462013-07-16 12:52:35 -0700724
725 @property
726 def name(self):
727 return "%sVer%s" % (self.interface.name, self.version.of_version)
728
729 @property
Andreas Wundsam5204de22013-07-30 11:34:45 -0700730 def variable_name(self):
731 return self.name[3:]
732
733 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700734 def length(self):
735 if self.is_fixed_length:
736 return self.min_length
737 else:
738 raise Exception("No fixed length for class %s, version %s" % (self.name, self.version))
739
740 @property
741 def min_length(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700742 """ @return the minimum wire length of an instance of this class in bytes """
Andreas Wundsam27303462013-07-16 12:52:35 -0700743 id_tuple = (self.ir_class.name, self.version.int_version)
744 return of_g.base_length[id_tuple] if id_tuple in of_g.base_length else -1
745
746 @property
747 def is_fixed_length(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700748 """ true iff this class serializes to a fixed length on the wire """
Andreas Wundsambbf85e12013-09-13 14:18:01 -0700749 return (self.ir_class.name, self.version.int_version) in of_g.is_fixed_length and \
750 not self.is_virtual
Andreas Wundsam27303462013-07-16 12:52:35 -0700751
752 def all_properties(self):
753 return self.interface.members
754
755 def get_member(self, name):
756 for m in self.members:
757 if m.name == name:
758 return m
759
760 @property
761 @memoize
762 def data_members(self):
763 return [ prop for prop in self.members if prop.is_data ]
764
765 @property
766 @memoize
767 def fixed_value_members(self):
768 return [ prop for prop in self.members if prop.is_fixed_value ]
769
770 @property
771 @memoize
772 def public_members(self):
773 return [ prop for prop in self.members if prop.is_public ]
774
775 @property
776 @memoize
777 def members(self):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700778 return self.ir_model_members + self.virtual_members
779
780 @property
781 def ir_model_members(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700782 members = [ JavaMember.for_of_member(self, of_member) for of_member in self.ir_class.members ]
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700783 return tuple(members)
784
785 @property
786 def virtual_members(self):
787 if self.interface.parent_interface and self.interface.parent_interface.startswith("OFOxm"):
788 if self.interface.name in model.oxm_map:
789 oxm_entry = model.oxm_map[self.interface.name]
790 return (
791 JavaVirtualMember(self, "matchField", java_type.make_match_field_jtype(oxm_entry.type_name), "MatchField.%s" % oxm_entry.value),
792 JavaVirtualMember(self, "masked", java_type.boolean, "true" if oxm_entry.masked else "false"),
793 )
794 else:
795 return (
796 JavaVirtualMember(self, "matchField", java_type.make_match_field_jtype(), "null"),
797 JavaVirtualMember(self, "masked", java_type.boolean, "false"),
798 )
799 else:
800 return ()
Andreas Wundsam27303462013-07-16 12:52:35 -0700801
802 def all_versions(self):
803 return [ JavaOFVersion(int_version)
804 for int_version in of_g.unified[self.c_name]
805 if int_version != 'union' and int_version != 'object_id' ]
806
807 def version_is_inherited(self, version):
808 return 'use_version' in of_g.unified[self.ir_class.name][version.int_version]
809
810 def inherited_from(self, version):
811 return JavaOFVersion(of_g.unified[self.ir_class.name][version.int_version]['use_version'])
812
813 @property
814 def is_virtual(self):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700815 return self.ir_class.virtual # type_maps.class_is_virtual(self.c_name) or self.ir_class.virtual
816
817 @property
818 def discriminator(self):
819 return find(lambda m: isinstance(m, OFDiscriminatorMember), self.ir_class.members)
Andreas Wundsam27303462013-07-16 12:52:35 -0700820
821 @property
822 def is_extension(self):
823 return type_maps.message_is_extension(self.c_name, -1)
824
Andreas Wundsam758c9cc2013-08-01 22:16:06 -0700825 @property
826 def align(self):
827 return int(self.ir_class.params['align']) if 'align' in self.ir_class.params else 0
828
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700829 @property
830 @memoize
831 def superclass(self):
832 return find(lambda c: c.version == self.version and c.c_name == self.ir_class.superclass, model.all_classes)
833
834 @property
835 @memoize
836 def subclasses(self):
837 return [ c for c in model.all_classes if c.version == self.version and c.ir_class.superclass == self.c_name ]
838
Andreas Wundsam27303462013-07-16 12:52:35 -0700839#######################################################################
840### Member
841#######################################################################
842
843
844class JavaMember(object):
845 """ Models a property (member) of an openflow class. """
846 def __init__(self, msg, name, java_type, member):
847 self.msg = msg
848 self.name = name
849 self.java_type = java_type
850 self.member = member
851 self.c_name = self.member.name if(hasattr(self.member, "name")) else ""
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700852
853 @property
854 def title_name(self):
855 return self.name[0].upper() + self.name[1:]
856
857 @property
858 def constant_name(self):
859 return self.c_name.upper()
860
861 @property
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700862 def getter_name(self):
863 return ("is" if self.java_type.public_type == "boolean" else "get") + self.title_name
864
865 @property
866 def setter_name(self):
867 return "set" + self.title_name
868
869 @property
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700870 def default_name(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700871 if self.is_fixed_value:
872 return self.constant_name
873 else:
874 return "DEFAULT_"+self.constant_name
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700875
876 @property
877 def default_value(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700878 if self.is_fixed_value:
879 return self.enum_value
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700880 else:
Andreas Wundsam142dd2a2013-09-23 14:47:37 -0700881 default = self.java_type.default_op(self.msg.version)
882 if default == "null" and not self.is_nullable:
883 return None
884 else:
885 return default
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700886
887 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700888 def enum_value(self):
889 if self.name == "version":
890 return "OFVersion.%s" % self.msg.version.constant_version
891
892 java_type = self.java_type.public_type;
893 try:
894 global model
895 enum = model.enum_by_name(java_type)
896 entry = enum.entry_by_version_value(self.msg.version, self.value)
897 return "%s.%s" % ( enum.name, entry.name)
898 except KeyError, e:
899 print e.message
900 return self.value
901
902 @property
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700903 def is_pad(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700904 return isinstance(self.member, OFPadMember)
905
906 def is_type_value(self, version=None):
907 if(version==None):
908 return any(self.is_type_value(version) for version in self.msg.all_versions)
909 try:
910 return self.c_name in get_type_values(self.msg.c_name, version.int_version)
911 except:
912 return False
913
914 @property
915 def is_field_length_value(self):
916 return isinstance(self.member, OFFieldLengthMember)
917
918 @property
Andreas Wundsam001b1822013-08-02 22:25:55 -0700919 def is_discriminator(self):
920 return isinstance(self.member, OFDiscriminatorMember)
921
922 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700923 def is_length_value(self):
924 return isinstance(self.member, OFLengthMember)
925
926 @property
927 def is_public(self):
928 return not (self.is_pad or self.is_length_value)
929
930 @property
931 def is_data(self):
932 return isinstance(self.member, OFDataMember) and self.name != "version"
933
934 @property
935 def is_fixed_value(self):
936 return hasattr(self.member, "value") or self.name == "version" \
937 or ( self.name == "length" and self.msg.is_fixed_length) \
938 or ( self.name == "len" and self.msg.is_fixed_length)
939
940 @property
941 def value(self):
942 if self.name == "version":
943 return self.msg.version.int_version
944 elif self.name == "length" or self.name == "len":
945 return self.msg.length
Andreas Wundsam27303462013-07-16 12:52:35 -0700946 else:
Andreas Wundsam001b1822013-08-02 22:25:55 -0700947 return self.java_type.format_value(self.member.value)
948
949 @property
950 def priv_value(self):
951 if self.name == "version":
952 return self.msg.version.int_version
953 elif self.name == "length" or self.name == "len":
954 return self.msg.length
955 else:
956 return self.java_type.format_value(self.member.value, pub_type=False)
957
Andreas Wundsam27303462013-07-16 12:52:35 -0700958
959 @property
960 def is_writeable(self):
961 return self.is_data and not self.name in model.write_blacklist[self.msg.name]
962
963 def get_type_value_info(self, version):
964 return get_type_values(msg.c_name, version.int_version)[self.c_name]
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700965
966 @property
967 def length(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700968 if hasattr(self.member, "length"):
969 return self.member.length
970 else:
Andreas Wundsam5204de22013-07-30 11:34:45 -0700971 count, base = loxi_utils.type_dec_to_count_base(self.member.type)
Andreas Wundsam27303462013-07-16 12:52:35 -0700972 return of_g.of_base_types[base]['bytes'] * count
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700973
974 @staticmethod
Andreas Wundsam27303462013-07-16 12:52:35 -0700975 def for_of_member(java_class, member):
976 if isinstance(member, OFPadMember):
977 return JavaMember(None, "", None, member)
978 else:
979 if member.name == 'len':
980 name = 'length'
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700981 elif member.name == 'value_mask':
982 name = 'mask'
Andreas Wundsam27303462013-07-16 12:52:35 -0700983 else:
984 name = java_type.name_c_to_camel(member.name)
985 j_type = java_type.convert_to_jtype(java_class.c_name, member.name, member.oftype)
986 return JavaMember(java_class, name, j_type, member)
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700987
988 @property
989 def is_universal(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700990 if not self.msg.c_name in of_g.unified:
991 print("%s not self.unified" % self.msg.c_name)
992 return False
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700993 for version in of_g.unified[self.msg.c_name]:
994 if version == 'union' or version =='object_id':
995 continue
996 if 'use_version' in of_g.unified[self.msg.c_name][version]:
997 continue
998
Andreas Wundsam27303462013-07-16 12:52:35 -0700999 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 -07001000 return False
1001 return True
1002
Andreas Wundsam2be7da52013-08-22 07:34:25 -07001003 @property
1004 def is_virtual(self):
1005 return False
1006
Andreas Wundsam27303462013-07-16 12:52:35 -07001007 def __hash__(self):
1008 return hash(self.name)
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001009
Andreas Wundsam27303462013-07-16 12:52:35 -07001010 def __eq__(self, other):
1011 if other is None or type(self) != type(other):
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001012 return False
Andreas Wundsam27303462013-07-16 12:52:35 -07001013 return (self.name,) == (other.name,)
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001014
Andreas Wundsamf1949682013-09-23 14:48:31 -07001015 @property
1016 def is_nullable(self):
1017 return self.name in model.nullable_map[self.msg.name]
1018
1019
Andreas Wundsam2be7da52013-08-22 07:34:25 -07001020class JavaVirtualMember(JavaMember):
1021 """ Models a virtual property (member) of an openflow class that is not backed by a loxi ir member """
1022 def __init__(self, msg, name, java_type, value=None):
1023 JavaMember.__init__(self, msg, name, java_type, member=None)
1024 self._value = value
1025
1026 @property
1027 def is_fixed_value(self):
1028 return True
1029
1030 @property
1031 def value(self):
1032 return self._value
1033
1034 @property
1035 def priv_value(self):
1036 return self._value
1037
1038
1039 @property
1040 def is_universal(self):
1041 return True
1042
1043 @property
1044 def is_virtual(self):
1045 return True
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001046
1047#######################################################################
1048### Unit Test
1049#######################################################################
1050
Yotam Harchol466b3212013-08-15 12:14:46 -07001051class JavaUnitTestSet(object):
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001052 def __init__(self, java_class):
1053 self.java_class = java_class
Yotam Harchol466b3212013-08-15 12:14:46 -07001054 first_data_file_name = "of{version}/{name}.data".format(version=java_class.version.of_version,
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001055 name=java_class.c_name[3:])
Yotam Harchol466b3212013-08-15 12:14:46 -07001056 data_file_template = "of{version}/{name}.".format(version=java_class.version.of_version,
1057 name=java_class.c_name[3:]) + "{i}.data"
1058 test_class_name = self.java_class.name + "Test"
1059 self.test_units = []
1060 if test_data.exists(first_data_file_name):
1061 self.test_units.append(JavaUnitTest(java_class, first_data_file_name, test_class_name))
1062 i = 1
1063 while test_data.exists(data_file_template.format(i=i)):
1064 self.test_units.append(JavaUnitTest(java_class, data_file_template.format(i=i), test_class_name + str(i)))
1065 i = i + 1
Andreas Wundsamf1949682013-09-23 14:48:31 -07001066
Yotam Harchol466b3212013-08-15 12:14:46 -07001067 @property
1068 def package(self):
1069 return self.java_class.package
1070
1071 @property
1072 def has_test_data(self):
1073 return len(self.test_units) > 0
1074
1075 @property
1076 def length(self):
1077 return len(self.test_units)
Andreas Wundsamf1949682013-09-23 14:48:31 -07001078
Yotam Harchol466b3212013-08-15 12:14:46 -07001079 def get_test_unit(self, i):
1080 return self.test_units[i]
1081
1082
1083class JavaUnitTest(object):
1084 def __init__(self, java_class, file_name=None, test_class_name=None):
1085 self.java_class = java_class
1086 if file_name is None:
1087 self.data_file_name = "of{version}/{name}.data".format(version=java_class.version.of_version,
1088 name=java_class.c_name[3:])
1089 else:
1090 self.data_file_name = file_name
1091 if test_class_name is None:
1092 self.test_class_name = self.java_class.name + "Test"
1093 else:
1094 self.test_class_name = test_class_name
Andreas Wundsamf1949682013-09-23 14:48:31 -07001095
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001096 @property
1097 def package(self):
1098 return self.java_class.package
1099
1100 @property
1101 def name(self):
Yotam Harchol466b3212013-08-15 12:14:46 -07001102 return self.test_class_name
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001103
1104 @property
1105 def has_test_data(self):
1106 return test_data.exists(self.data_file_name)
1107
1108 @property
1109 @memoize
1110 def test_data(self):
1111 return test_data.read(self.data_file_name)
1112
1113
Andreas Wundsam27303462013-07-16 12:52:35 -07001114#######################################################################
1115### Enums
1116#######################################################################
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001117
Andreas Wundsam27303462013-07-16 12:52:35 -07001118class JavaEnum(object):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001119 def __init__(self, c_name, version_enum_map):
Andreas Wundsam27303462013-07-16 12:52:35 -07001120 self.c_name = c_name
Andreas Wundsambe168f72013-08-03 22:49:35 -07001121
1122 if c_name == "of_stats_types":
1123 self.name = "OFStatsType"
1124 else:
1125 self.name = "OF" + java_type.name_c_to_caps_camel("_".join(c_name.split("_")[1:]))
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001126
Andreas Wundsam27303462013-07-16 12:52:35 -07001127 # Port_features has constants that start with digits
1128 self.name_prefix = "PF_" if self.name == "OFPortFeatures" else ""
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001129
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001130 self.version_enums = version_enum_map
1131
1132 entry_name_version_value_map = OrderedDefaultDict(lambda: OrderedDict())
1133 for version, ir_enum in version_enum_map.items():
1134 for ir_entry in ir_enum.entries:
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001135 entry_name_version_value_map[ir_entry.name][version] = ir_entry.value
1136
Andreas Wundsam27303462013-07-16 12:52:35 -07001137 self.entries = [ JavaEnumEntry(self, name, version_value_map)
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001138 for (name, version_value_map) in entry_name_version_value_map.items() ]
Andreas Wundsam43526532013-08-01 22:03:50 -07001139
1140 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 -07001141 self.package = "org.projectfloodlight.openflow.protocol"
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001142
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -07001143 self.metadata = model.enum_metadata_map[self.name]
1144
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001145 def wire_type(self, version):
1146 ir_enum = self.version_enums[version]
1147 if "wire_type" in ir_enum.params:
1148 return java_type.convert_enum_wire_type_to_jtype(ir_enum.params["wire_type"])
1149 else:
1150 return java_type.u8
1151
1152 @property
Andreas Wundsam7cfeac32013-09-17 13:53:48 -07001153 @memoize
1154 def is_bitmask(self):
1155 return any(ir_enum.is_bitmask for ir_enum in self.version_enums.values())
1156
1157 @property
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001158 def versions(self):
1159 return self.version_enums.keys()
1160
Andreas Wundsam27303462013-07-16 12:52:35 -07001161 @memoize
1162 def entry_by_name(self, name):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -07001163 res = find(lambda e: e.name == name, self.entries)
1164 if res:
1165 return res
1166 else:
Andreas Wundsam27303462013-07-16 12:52:35 -07001167 raise KeyError("Enum %s: no entry with name %s" % (self.name, name))
1168
1169 @memoize
1170 def entry_by_c_name(self, name):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -07001171 res = find(lambda e: e.c_name == name, self.entries)
1172 if res:
1173 return res
1174 else:
Andreas Wundsam27303462013-07-16 12:52:35 -07001175 raise KeyError("Enum %s: no entry with c_name %s" % (self.name, name))
1176
1177 @memoize
1178 def entry_by_version_value(self, version, value):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -07001179 res = find(lambda e: e.values[version] == value if version in e.values else False, self.entries)
1180 if res:
1181 return res
1182 else:
Andreas Wundsam27303462013-07-16 12:52:35 -07001183 raise KeyError("Enum %s: no entry with version %s, value %s" % (self.name, version, value))
1184
1185# values: Map JavaVersion->Value
1186class JavaEnumEntry(object):
1187 def __init__(self, enum, name, values):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001188 self.enum = enum
Andreas Wundsam27303462013-07-16 12:52:35 -07001189 self.name = enum.name_prefix + "_".join(name.split("_")[1:]).upper()
1190 self.values = values
1191
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -07001192 @property
1193 def constructor_params(self):
Rob Vaterlaus6035bf52013-09-17 19:32:38 -07001194 return [ m.value(self) for m in self.enum.metadata.properties ]
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -07001195
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001196 def has_value(self, version):
1197 return version in self.values
1198
Andreas Wundsam27303462013-07-16 12:52:35 -07001199 def value(self, version):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001200 return self.values[version]
1201
1202 def format_value(self, version):
1203 res = self.enum.wire_type(version).format_value(self.values[version])
Andreas Wundsam27303462013-07-16 12:52:35 -07001204 return res
1205
Andreas Wundsam27303462013-07-16 12:52:35 -07001206 def all_values(self, versions, not_present=None):
1207 return [ self.values[version] if version in self.values else not_present for version in versions ]
Andreas Wundsamc1acfcc2013-09-20 13:10:20 -07001208
1209 @property
1210 @memoize
1211 def masked_enum_group(self):
1212 group = find(lambda g: self.name in g.members, model.masked_enum_groups[self.enum.name])
1213 return group
1214
1215 @property
1216 @memoize
1217 def is_mask(self):
1218 return any(self.name == g.mask for g in model.masked_enum_groups[self.enum.name])