blob: 6aac39cf8e9e4f8e37d74be4c38ce816355ad3a6 [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 Wundsamc8912c12013-11-15 13:44:48 -080039from loxi_globals import OFVersions
40import loxi_globals
Andreas Wundsam27303462013-07-16 12:52:35 -070041from loxi_ir import *
Andreas Wundsam5204de22013-07-30 11:34:45 -070042import loxi_utils.loxi_utils as loxi_utils
Andreas Wundsam5204de22013-07-30 11:34:45 -070043import test_data
Andreas Wundsam40e14f72013-05-06 14:49:08 -070044
Andreas Wundsam27303462013-07-16 12:52:35 -070045import java_gen.java_type as java_type
Andreas Wundsame0d52be2013-08-22 07:52:13 -070046from java_gen.java_type import erase_type_annotation
Andreas Wundsam40e14f72013-05-06 14:49:08 -070047
Andreas Wundsamc8912c12013-11-15 13:44:48 -080048logger = logging.getLogger(__name__)
49
Andreas Wundsam907c3612014-01-15 12:03:04 -080050
51def java_class_name(c_name):
52 return java_type.name_c_to_caps_camel(c_name) if c_name != "of_header" else "OFMessage"
53
Andreas Wundsam27303462013-07-16 12:52:35 -070054class JavaModel(object):
Andreas Wundsamf1949682013-09-23 14:48:31 -070055 # registry for enums that should not be generated
56 # set(${java_enum_name})
Andreas Wundsamacd57d52013-10-18 17:35:01 -070057 enum_blacklist = set(("OFDefinitions", "OFPortNo", "OFVlanId", "OFGroup"))
Andreas Wundsamf1949682013-09-23 14:48:31 -070058 # registry for enum *entry* that should not be generated
59 # map: ${java_enum_name} -> set(${java_entry_entry_name})
Andreas Wundsam43526532013-08-01 22:03:50 -070060 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 -070061 # registry of interfaces that should not be generated
62 # set(java_names)
Andreas Wundsambe168f72013-08-03 22:49:35 -070063 # OFUint structs are there for god-knows what in loci. We certainly don't need them.
64 interface_blacklist = set( ("OFUint8", "OFUint32",))
Andreas Wundsamf1949682013-09-23 14:48:31 -070065 # registry of interface properties that should not be generated
66 # map: $java_type -> set(java_name_property)
Rich Lane97fd13d2013-11-13 11:25:48 -080067 read_blacklist = defaultdict(lambda: set(),
68 OFExperimenter=set(('data','subtype')),
69 OFActionExperimenter=set(('data',)),
70 OFExperimenterStatsRequest=set(('data','subtype')),
Rich Lanef33f32f2013-11-18 15:49:54 -080071 OFExperimenterStatsReply=set(('data','subtype')),
72 OFInstructionExperimenter=set(('data',)))
Andreas Wundsamf1949682013-09-23 14:48:31 -070073 # map: $java_type -> set(java_name_property)
Rich Lane13730732013-12-03 13:05:19 -080074 write_blacklist = defaultdict(
75 lambda: set(),
76 OFOxm=set(('typeLen',)),
Murat Parlakisikf95672c2016-12-05 00:53:17 -080077 OFOxs=set(('typeLen',)),
Rich Lane13730732013-12-03 13:05:19 -080078 OFAction=set(('type',)),
79 OFInstruction=set(('type',)),
80 OFFlowMod=set(('command', )),
81 OFExperimenter=set(('data','subtype')),
82 OFActionExperimenter=set(('data',)),
83 OFBsnTlv=set(('type',)))
Andreas Wundsamf1949682013-09-23 14:48:31 -070084 # interfaces that are virtual
Murat Parlakisikf95672c2016-12-05 00:53:17 -080085 virtual_interfaces = set(['OFOxm', 'OFInstruction', 'OFFlowMod', 'OFBsnVport', 'OFOxs' ])
Andreas Wundsam27303462013-07-16 12:52:35 -070086
Andreas Wundsamf1949682013-09-23 14:48:31 -070087 # Registry of nullable properties:
88 # ${java_class_name} -> set(${java_property_name})
89 nullable_map = defaultdict(lambda: set(),
90 )
91
92 # represents a subgroup of a bitmask enum that is actualy a normal enumerable within a masked part of the enum
93 # e.g., the flags STP.* in OF1.0 port state are bit mask entries, but instead enumerables according to the mask "STP_MASK"
94 # name: a name for the group
95 # mask: java name of the enum entry that defines the mask
96 # members: set of names of the members of the group
Andreas Wundsamc1acfcc2013-09-20 13:10:20 -070097 MaskedEnumGroup = namedtuple("MaskedEnumGroup", ("name", "mask", "members"))
98
Andreas Wundsamf1949682013-09-23 14:48:31 -070099 # registry of MaskedEnumGroups (see above).
100 # map: ${java_enum_name}: tuple(MaskedEnumGroup)
Andreas Wundsamc1acfcc2013-09-20 13:10:20 -0700101 masked_enum_groups = defaultdict(lambda: (),
Andreas Wundsamaebe5182013-09-24 13:01:07 -0700102 OFPortState = (MaskedEnumGroup("stp_flags", mask="STP_MASK", members=set(("STP_LISTEN", "STP_LEARN", "STP_FORWARD", "STP_BLOCK"))), ),
103 OFConfigFlags = (
104 MaskedEnumGroup("frag_flags", mask="FRAG_MASK", members=set(("FRAG_NORMAL", "FRAG_DROP", "FRAG_REASM"))),
Andreas Wundsam376cef52013-09-24 13:52:18 -0700105 ),
106 OFTableConfig = (
107 MaskedEnumGroup("table_miss_flags", mask="TABLE_MISS_MASK", members=set(("TABLE_MISS_CONTROLLER", "TABLE_MISS_CONTINUE", "TABLE_MISS_DROP"))),
108 ),
Andreas Wundsam057540b2013-11-19 16:51:36 -0800109 OFGetConfigReply = (
110 MaskedEnumGroup("flags", mask="OFP_FRAG_MASK", members=set(("FRAG_NORMAL", "FRAG_DROP", "FRAG_REASM"))),
111 ),
112 OFSetConfig = (
113 MaskedEnumGroup("flags", mask="OFP_FRAG_MASK", members=set(("FRAG_NORMAL", "FRAG_DROP", "FRAG_REASM"))),
114 ),
Andreas Wundsamc1acfcc2013-09-20 13:10:20 -0700115 )
116
Andreas Wundsamf1949682013-09-23 14:48:31 -0700117 # represents a metadata property associated with an EnumClass
118 # name:
Rob Vaterlaus6035bf52013-09-17 19:32:38 -0700119 class OFEnumPropertyMetadata(namedtuple("OFEnumPropertyMetadata", ("name", "type", "value"))):
Andreas Wundsamf1949682013-09-23 14:48:31 -0700120 """
121 represents a metadata property associated with an Enum Class
122 @param name name of metadata property
123 @param type java_type instance describing the type
124 @value: Generator function f(entry) that generates the value
125 """
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -0700126 @property
127 def variable_name(self):
128 return self.name[0].lower() + self.name[1:]
129
130 @property
131 def getter_name(self):
Rob Vaterlausbda9ce62013-09-17 14:45:48 -0700132 prefix = "is" if self.type == java_type.boolean else "get"
133 return prefix+self.name
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -0700134
Andreas Wundsamf1949682013-09-23 14:48:31 -0700135 """ Metadata container. """
Rob Vaterlaus6035bf52013-09-17 19:32:38 -0700136 OFEnumMetadata = namedtuple("OFEnumMetadata", ("properties", "to_string"))
137
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -0700138 def gen_port_speed(enum_entry):
Andreas Wundsamf1949682013-09-23 14:48:31 -0700139 """ Generator function for OFortFeatures.PortSpeed"""
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -0700140 splits = enum_entry.name.split("_")
141 if len(splits)>=2:
142 m = re.match(r'\d+[MGTP]B', splits[1])
143 if m:
144 return "PortSpeed.SPEED_{}".format(splits[1])
145 return "PortSpeed.SPEED_NONE";
146
Rob Vaterlausbda9ce62013-09-17 14:45:48 -0700147 def gen_stp_state(enum_entry):
Andreas Wundsamf1949682013-09-23 14:48:31 -0700148 """ Generator function for OFPortState.StpState"""
Rob Vaterlausbda9ce62013-09-17 14:45:48 -0700149 splits = enum_entry.name.split("_")
150 if len(splits)>=1:
151 if splits[0] == "STP":
152 return "true"
153 return "false"
154
Andreas Wundsamf1949682013-09-23 14:48:31 -0700155 # registry for metadata properties for enums
156 # map: ${java_enum_name}: OFEnumMetadata
Rob Vaterlaus6035bf52013-09-17 19:32:38 -0700157 enum_metadata_map = defaultdict(lambda: JavaModel.OFEnumMetadata((), None),
Rob Vaterlausa049dee2013-09-18 16:18:37 -0700158 OFPortFeatures = OFEnumMetadata((OFEnumPropertyMetadata("PortSpeed", java_type.port_speed, gen_port_speed),), None),
159 OFPortState = OFEnumMetadata((OFEnumPropertyMetadata("StpState", java_type.boolean, gen_stp_state),), None),
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -0700160 )
161
Andreas Wundsam27303462013-07-16 12:52:35 -0700162 @property
163 @memoize
164 def versions(self):
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800165 return OrderedSet( JavaOFVersion(ir_version) for ir_version in OFVersions.target_versions)
Andreas Wundsam27303462013-07-16 12:52:35 -0700166
167 @property
168 @memoize
169 def interfaces(self):
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800170 interfaces = [ JavaOFInterface(ir_class) for ir_class in loxi_globals.unified.classes ]
Andreas Wundsambe168f72013-08-03 22:49:35 -0700171 interfaces = [ i for i in interfaces if i.name not in self.interface_blacklist ]
172
Andreas Wundsam27303462013-07-16 12:52:35 -0700173 return interfaces
174
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700175 @memoize
176 def interface_by_name(self, name):
177 return find(lambda i: erase_type_annotation(i.name) == erase_type_annotation(name), self.interfaces)
178
Andreas Wundsam27303462013-07-16 12:52:35 -0700179 @property
180 @memoize
Andreas Wundsam001b1822013-08-02 22:25:55 -0700181 def all_classes(self):
182 return [clazz for interface in self.interfaces for clazz in interface.versioned_classes]
183
184 @property
185 @memoize
Andreas Wundsam27303462013-07-16 12:52:35 -0700186 def enums(self):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700187 name_version_enum_map = OrderedDefaultDict(lambda: OrderedDict())
Andreas Wundsamf299baa2014-10-23 20:15:32 -0700188 name_stable_map = {}
Andreas Wundsam27303462013-07-16 12:52:35 -0700189
190 for version in self.versions:
Andreas Wundsamf299baa2014-10-23 20:15:32 -0700191 logger.debug("version: {}".format(version.ir_version))
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800192 of_protocol = loxi_globals.ir[version.ir_version]
Andreas Wundsam27303462013-07-16 12:52:35 -0700193 for enum in of_protocol.enums:
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700194 name_version_enum_map[enum.name][version] = enum
Andreas Wundsamf299baa2014-10-23 20:15:32 -0700195 stable = (enum.params.get('stable') == 'True')
Andreas Wundsam27303462013-07-16 12:52:35 -0700196
Andreas Wundsamf299baa2014-10-23 20:15:32 -0700197 logger.debug("Enum: %s stable: %s", enum.name, stable)
198
199 if not enum.name in name_stable_map:
200 name_stable_map[enum.name] = stable
201 else:
202 if name_stable_map[enum.name] != stable:
203 raise Exception("Inconsistent enum stability (should be caught " +\
204 " by IR)")
205
206 enums = [ JavaEnum(name, name_stable_map[name], version_enum_map) for name, version_enum_map,
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700207 in name_version_enum_map.items() ]
Andreas Wundsam27303462013-07-16 12:52:35 -0700208
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700209 # inelegant - need java name here
210 enums = [ enum for enum in enums if enum.name not in self.enum_blacklist ]
Andreas Wundsam27303462013-07-16 12:52:35 -0700211 return enums
212
213 @memoize
214 def enum_by_name(self, name):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700215 res = find(lambda e: e.name == name, self.enums)
216 if not res:
Andreas Wundsam27303462013-07-16 12:52:35 -0700217 raise KeyError("Could not find enum with name %s" % name)
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700218 return res
Andreas Wundsam27303462013-07-16 12:52:35 -0700219
Andreas Wundsam5204de22013-07-30 11:34:45 -0700220 @property
221 @memoize
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700222 def of_factories(self):
Yotam Harchol791e4882013-09-05 16:32:56 -0700223 prefix = "org.projectfloodlight.openflow.protocol"
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700224
225 factories = OrderedDict()
226
Murat Parlakisikf95672c2016-12-05 00:53:17 -0800227 sub_factory_classes = ("OFAction", "OFInstruction", "OFMeterBand", "OFOxm", "OFQueueProp", "OFErrorMsg", "OFActionId", "OFInstructionId", "OFBsnTlv", "OFOxs")
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700228 for base_class in sub_factory_classes:
229 package = base_class[2:].lower()
230 remove_prefix = base_class[2].lower() + base_class[3:]
231
232 # HACK need to have a better way to deal with parameterized base classes
Murat Parlakisikf95672c2016-12-05 00:53:17 -0800233 annotated_base_class = base_class + "<?>" if base_class == "OFOxm" or base_class == "OFOxs" else base_class
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700234
235 factories[base_class] = OFFactory(package="%s.%s" % (prefix, package),
Andreas Wundsamd1ca4f92013-12-10 18:58:44 -0800236 name=base_class + "s", members=[], remove_prefix=remove_prefix, base_class=annotated_base_class, sub_factories={}, xid_generator= (base_class == "OFErrorMsg"))
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700237
238 factories[""] = OFFactory(
239 package=prefix,
Andreas Wundsam5204de22013-07-30 11:34:45 -0700240 name="OFFactory",
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700241 remove_prefix="",
242 members=[], base_class="OFMessage", sub_factories=OrderedDict(
Andreas Wundsamcb17b232013-09-28 19:05:36 -0700243 ("{}{}s".format(n[2].lower(), n[3:]), "{}s".format(n)) for n in sub_factory_classes ),
244 xid_generator=True)
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700245
246 for i in self.interfaces:
247 for n, factory in factories.items():
248 if n == "":
249 factory.members.append(i)
250 break
251 else:
252 super_class = self.interface_by_name(n)
253 if i.is_instance_of(super_class):
254 factory.members.append(i)
255 break
256 return factories.values()
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800257
Yotam Harchol595c6442013-09-27 16:29:08 -0700258 @memoize
259 def factory_of(self, interface):
260 for factory in self.of_factories:
261 if interface in factory.members:
262 return factory
263 return None
Andreas Wundsam5204de22013-07-30 11:34:45 -0700264
265 def generate_class(self, clazz):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700266 """ return wether or not to generate implementation class clazz.
267 Now true for everything except OFTableModVer10.
268 @param clazz JavaOFClass instance
269 """
Andreas Wundsam43526532013-08-01 22:03:50 -0700270 if clazz.interface.name.startswith("OFMatchV"):
271 return True
Murat Parlakisikf95672c2016-12-05 00:53:17 -0800272 if clazz.interface.name.startswith("OFStatV"):
273 return True
Andreas Wundsam001b1822013-08-02 22:25:55 -0700274 elif clazz.name == "OFTableModVer10":
275 # tablemod ver 10 is a hack and has no oftype defined
276 return False
Andreas Wundsam5204de22013-07-30 11:34:45 -0700277 if loxi_utils.class_is_message(clazz.interface.c_name):
278 return True
279 if loxi_utils.class_is_oxm(clazz.interface.c_name):
280 return True
Andreas Wundsam43526532013-08-01 22:03:50 -0700281 if loxi_utils.class_is_action(clazz.interface.c_name):
282 return True
283 if loxi_utils.class_is_instruction(clazz.interface.c_name):
284 return True
Murat Parlakisikf95672c2016-12-05 00:53:17 -0800285 if loxi_utils.class_is_oxs(clazz.interface.c_name):
286 return True
Andreas Wundsam5204de22013-07-30 11:34:45 -0700287 else:
Andreas Wundsam001b1822013-08-02 22:25:55 -0700288 return True
Andreas Wundsam5204de22013-07-30 11:34:45 -0700289
Andreas Wundsam83aebd32013-11-19 10:43:50 -0800290 @property
291 @memoize
292 def oxm_map(self):
293 OxmMapEntry = namedtuple("OxmMapEntry", ["type_name", "value", "masked" ])
294 return OrderedDict( (oxm.name, OxmMapEntry(type_name=oxm.member_by_name("value").java_type.public_type,
295 value=re.sub(r'^of_oxm_', r'', re.sub(r'_masked$', r'', oxm.ir_class.name)).upper(),
296 masked=oxm.ir_class.name.endswith("_masked")))
297 for oxm in self.interfaces if oxm.ir_class.is_subclassof("of_oxm") )
Andreas Wundsam5204de22013-07-30 11:34:45 -0700298
Murat Parlakisikf95672c2016-12-05 00:53:17 -0800299 @property
300 @memoize
301 def oxs_map(self):
302 OxsMapEntry = namedtuple("OxsMapEntry", ["type_name", "value", "masked" ])
303 return OrderedDict( (oxs.name, OxsMapEntry(type_name=oxs.member_by_name("value").java_type.public_type,
304 value=re.sub(r'^of_oxs_', r'', re.sub(r'_masked$', r'', oxs.ir_class.name)).upper(),
305 masked=oxs.ir_class.name.endswith("_masked")))
306 for oxs in self.interfaces if oxs.ir_class.is_subclassof("of_oxs") )
307
Andreas Wundsamcb17b232013-09-28 19:05:36 -0700308class OFFactory(namedtuple("OFFactory", ("package", "name", "members", "remove_prefix", "base_class", "sub_factories", "xid_generator"))):
Andreas Wundsam5204de22013-07-30 11:34:45 -0700309 @property
310 def factory_classes(self):
311 return [ OFFactoryClass(
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800312 package="org.projectfloodlight.openflow.protocol.ver{}".format(version.dotless_version),
313 name="{}Ver{}".format(self.name, version.dotless_version),
Andreas Wundsam5204de22013-07-30 11:34:45 -0700314 interface=self,
315 version=version
316 ) for version in model.versions ]
317
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700318 def method_name(self, member, builder=True):
319 n = member.variable_name
320 if n.startswith(self.remove_prefix):
321 n = n[len(self.remove_prefix):]
322 n = n[0].lower() + n[1:]
323 if builder:
324 return "build" + n[0].upper() + n[1:]
325 else:
326 return n
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800327
Yotam Harchol595c6442013-09-27 16:29:08 -0700328 def of_version(self, version):
329 for fc in self.factory_classes:
330 if fc.version == version:
331 return fc
332 return None
Andreas Wundsam5204de22013-07-30 11:34:45 -0700333
334OFGenericClass = namedtuple("OFGenericClass", ("package", "name"))
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700335class OFFactoryClass(namedtuple("OFFactoryClass", ("package", "name", "interface", "version"))):
336 @property
337 def base_class(self):
338 return self.interface.base_class
339
340 @property
341 def versioned_base_class(self):
342 base_class_interface = model.interface_by_name(self.interface.base_class)
343 if base_class_interface and base_class_interface.has_version(self.version):
344 return base_class_interface.versioned_class(self.version)
345 else:
346 return None
Andreas Wundsam5204de22013-07-30 11:34:45 -0700347
Andreas Wundsam27303462013-07-16 12:52:35 -0700348model = JavaModel()
349
350#######################################################################
351### OFVersion
352#######################################################################
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700353
354class JavaOFVersion(object):
355 """ Models a version of OpenFlow. contains methods to convert the internal
356 Loxi version to a java constant / a string """
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800357 def __init__(self, ir_version):
358 assert isinstance(ir_version, OFVersion)
359 self.ir_version = ir_version
360 self.int_version = self.ir_version.wire_version
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700361
362 @property
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800363 def dotless_version(self):
364 return self.ir_version.version.replace(".", "")
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700365
366 @property
367 def constant_version(self):
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800368 return "OF_" + self.dotless_version
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700369
Andreas Wundsam27303462013-07-16 12:52:35 -0700370 def __repr__(self):
371 return "JavaOFVersion(%d)" % self.int_version
372
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700373 def __str__(self):
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800374 return self.ir_version.version
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700375
Andreas Wundsam27303462013-07-16 12:52:35 -0700376 def __hash__(self):
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800377 return hash(self.ir_version)
Andreas Wundsam27303462013-07-16 12:52:35 -0700378
379 def __eq__(self, other):
380 if other is None or type(self) != type(other):
381 return False
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800382 return (self.ir_version,) == (other.ir_version,)
Andreas Wundsam27303462013-07-16 12:52:35 -0700383
384#######################################################################
385### Interface
386#######################################################################
387
388class JavaOFInterface(object):
389 """ Models an OpenFlow Message class for the purpose of the java class.
390 Version agnostic, in contrast to the loxi_ir python model.
391 """
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800392 def __init__(self, ir_class):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700393 """"
394 @param c_name: loxi style name (e.g., of_flow_add)
395 @param version_map map of { JavaOFVersion: OFClass (from loxi_ir) }
396 """
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800397 self.ir_class = ir_class
398 self.c_name = ir_class.name
399 self.version_map = { JavaOFVersion(v): c for v,c in ir_class.version_classes.items() }
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700400 # name: the Java Type name, e.g., OFFlowAdd
Andreas Wundsam907c3612014-01-15 12:03:04 -0800401 self.name = java_class_name(self.c_name)
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700402 # variable_name name to use for variables of this type. i.e., flowAdd
Andreas Wundsam5204de22013-07-30 11:34:45 -0700403 self.variable_name = self.name[2].lower() + self.name[3:]
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700404 self.title_name = self.variable_name[0].upper() + self.variable_name[1:]
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700405 # name for use in constants: FLOW_ADD
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800406 self.constant_name = self.c_name.upper().replace("OF_", "")
Andreas Wundsam27303462013-07-16 12:52:35 -0700407
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700408 pck_suffix, parent_interface, self.type_annotation = self.class_info()
Yotam Harchol791e4882013-09-05 16:32:56 -0700409 self.package = "org.projectfloodlight.openflow.protocol.%s" % pck_suffix if pck_suffix else "org.projectfloodlight.openflow.protocol"
Andreas Wundsam27303462013-07-16 12:52:35 -0700410 if self.name != parent_interface:
411 self.parent_interface = parent_interface
412 else:
413 self.parent_interface = None
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700414
Andreas Wundsama705c6b2013-09-04 11:21:09 -0700415 @property
416 @memoize
417 def all_parent_interfaces(self):
418 return [ "OFObject" ] + \
419 ([ self.parent_interface ] if self.parent_interface else [] )+ \
420 self.additional_parent_interfaces
421 @property
422 @memoize
423 def additional_parent_interfaces(self):
424 if loxi_utils.class_is_message(self.c_name) and not self.is_virtual:
425 m = re.match(r'(.*)Request$', self.name)
426 if m:
427 reply_name = m.group(1) + "Reply"
428 if model.interface_by_name(reply_name):
429 return ["OFRequest<%s>" % reply_name ]
Andreas Wundsam73d7d672014-11-12 17:07:33 -0800430 elif self.name == "OFBundleCtrlMsg":
431 reply_name = "OFBundleCtrlMsg"
432 return ["OFRequest<%s>" % reply_name ]
Andreas Wundsama705c6b2013-09-04 11:21:09 -0700433 return []
434
435
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700436 def is_instance_of(self, other_class):
437 if self == other_class:
438 return True
439 parent = self.super_class
440 if parent is None:
441 return False
442 else:
443 return parent.is_instance_of(other_class)
444
445 @property
446 def super_class(self):
447 if not self.parent_interface:
448 return None
449 else:
450 return model.interface_by_name(self.parent_interface)
451
452
453 def inherited_declaration(self, type_spec="?"):
454 if self.type_annotation:
455 return "%s<%s>" % (self.name, type_spec)
456 else:
457 return "%s" % self.name
458
459 @property
460 def type_variable(self):
461 if self.type_annotation:
462 return "<T>"
463 else:
464 return "";
465
Andreas Wundsam27303462013-07-16 12:52:35 -0700466 def class_info(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700467 """ return tuple of (package_prefix, parent_class) for the current JavaOFInterface"""
Andreas Wundsam907c3612014-01-15 12:03:04 -0800468 # FIXME: This code could be cleaned up further. Maybe some of the exceptions
469 # here could be folded into ir, or the type arithmetic specified in a more general
470 # fashion
471 def calc_package(i):
472 if i.is_subclassof("of_error_msg"):
473 return "errormsg"
474 elif i.is_instanceof("of_action"):
475 return "action"
476 elif i.is_instanceof("of_action_id"):
477 return "actionid"
478 elif i.is_instanceof("of_instruction"):
479 return "instruction"
480 elif i.is_instanceof("of_instruction_id"):
481 return "instructionid"
482 elif i.is_instanceof("of_oxm"):
483 return "oxm"
Murat Parlakisikf95672c2016-12-05 00:53:17 -0800484 elif i.is_instanceof("of_oxs"):
485 return "oxs"
Andreas Wundsam907c3612014-01-15 12:03:04 -0800486 elif i.is_instanceof("of_meter_band"):
487 return "meterband"
488 elif i.is_instanceof("of_queue_prop"):
489 return "queueprop"
490 elif i.is_instanceof("of_bsn_tlv"):
491 return "bsntlv"
492 else:
493 return ""
494
495 def calc_super_name(i):
496 if re.match('of_match_.*', i.name):
497 return "Match"
Murat Parlakisikf95672c2016-12-05 00:53:17 -0800498 elif re.match('of_stat_.*', i.name):
499 return "Stat"
Andreas Wundsam907c3612014-01-15 12:03:04 -0800500 else:
501 ir_super_class = self.ir_class.superclass
502 return java_class_name(ir_super_class.name) if ir_super_class else ""
503
504 package = calc_package(self.ir_class)
505 super_name = calc_super_name(self.ir_class)
506
507 if self.name == "OFStatsRequest":
508 # stats_requests are special because of their type annotation
509 return (package, "OFMessage", "T extends OFStatsReply")
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800510 elif self.ir_class.is_subclassof('of_stats_request'):
Andreas Wundsam907c3612014-01-15 12:03:04 -0800511 # stats_request subclasses are special because of their type annotation
512 reply_name = re.sub(r'Request$', 'Reply', self.name)
513 super_type_annotation = "T" if self.ir_class.virtual else reply_name
514
515 type_annotation = "T extends {}".format(reply_name) if self.ir_class.virtual \
516 else ""
517
518 return (package, "{}<{}>".format(super_name, super_type_annotation),
519 type_annotation)
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700520 elif self.name == "OFOxm":
Andreas Wundsam907c3612014-01-15 12:03:04 -0800521 return (package, None, "T extends OFValueType<T>")
Murat Parlakisikf95672c2016-12-05 00:53:17 -0800522 elif self.name == "OFOxs":
523 return (package, None, "T extends OFValueType<T>")
Andreas Wundsam5204de22013-07-30 11:34:45 -0700524 elif loxi_utils.class_is_oxm(self.c_name):
Andreas Wundsam907c3612014-01-15 12:03:04 -0800525 # look up type from member value for OFValueType type annotation
Andreas Wundsam83aebd32013-11-19 10:43:50 -0800526 if self.member_by_name("value") is not None:
Andreas Wundsam907c3612014-01-15 12:03:04 -0800527 return (package, "OFOxm<%s>" % self.member_by_name("value").java_type.public_type, None)
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700528 else:
Andreas Wundsam907c3612014-01-15 12:03:04 -0800529 return (package, "OFOxm", None)
Murat Parlakisikf95672c2016-12-05 00:53:17 -0800530 elif loxi_utils.class_is_oxs(self.c_name):
531 # look up type from member value for OFValueType type annotation
532 if self.member_by_name("value") is not None:
533 return (package, "OFOxs<%s>" % self.member_by_name("value").java_type.public_type, None)
534 else:
535 return (package, "OFOxs", None)
Andreas Wundsam27303462013-07-16 12:52:35 -0700536 else:
Andreas Wundsam907c3612014-01-15 12:03:04 -0800537 return (package, super_name, None)
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700538
539 @property
540 @memoize
541 def writeable_members(self):
542 return [ m for m in self.members if m.is_writeable ]
Andreas Wundsam27303462013-07-16 12:52:35 -0700543
Andreas Wundsamcb17b232013-09-28 19:05:36 -0700544 @memoize
545 def member_by_name(self, name):
546 return find(lambda m: m.name == name, self.members)
547
Andreas Wundsam27303462013-07-16 12:52:35 -0700548 @property
549 @memoize
550 def members(self):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700551 return self.ir_model_members + self.virtual_members
552
553 @property
554 @memoize
555 def ir_model_members(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700556 """return a list of all members to be exposed by this interface. Corresponds to
557 the union of the members of the vesioned classes without length, fieldlength
558 and pads (those are handled automatically during (de)serialization and not exposed"""
Andreas Wundsam27303462013-07-16 12:52:35 -0700559 all_versions = []
560 member_map = collections.OrderedDict()
561
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800562 member_version_map = {}
Andreas Wundsam27303462013-07-16 12:52:35 -0700563 for (version, of_class) in self.version_map.items():
564 for of_member in of_class.members:
565 if isinstance(of_member, OFLengthMember) or \
566 isinstance(of_member, OFFieldLengthMember) or \
567 isinstance(of_member, OFPadMember):
568 continue
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800569 java_member = JavaMember.for_of_member(self, of_member)
Andreas Wundsam27303462013-07-16 12:52:35 -0700570 if of_member.name not in member_map:
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800571 member_map[of_member.name] = java_member
572 member_version_map[of_member.name] = version
573 else:
574 existing = member_map[of_member.name]
575
576 if existing.java_type.public_type != java_member.java_type.public_type:
577 raise Exception(
578 "Error constructing interface {}: type signatures do not match up between versions.\n"
579 " Member Name: {}\n"
580 " Existing: Version={}, Java={}, IR={}\n"
581 " New: Version={}, Java={}, IR={}"
582 .format(self.name, existing.name,
583 member_version_map[of_member.name], existing.java_type.public_type, existing.member.oftype,
584 version, java_member.java_type.public_type, java_member.member.oftype)
585 )
Andreas Wundsam27303462013-07-16 12:52:35 -0700586
Andreas Wundsamb1da3f62013-09-04 18:46:03 -0700587 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 -0700588
589 @property
590 def virtual_members(self):
Andreas Wundsama0981022013-10-02 18:15:06 -0700591 virtual_members = []
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700592 if self.name == "OFOxm":
Andreas Wundsama0981022013-10-02 18:15:06 -0700593 virtual_members += [
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700594 JavaVirtualMember(self, "value", java_type.generic_t),
595 JavaVirtualMember(self, "mask", java_type.generic_t),
596 JavaVirtualMember(self, "matchField", java_type.make_match_field_jtype("T")),
597 JavaVirtualMember(self, "masked", java_type.boolean),
Andreas Wundsam661a2222013-11-05 17:18:59 -0800598 JavaVirtualMember(self, "canonical", java_type.make_oxm_jtype("T"))
Andreas Wundsama0981022013-10-02 18:15:06 -0700599 ]
Murat Parlakisikf95672c2016-12-05 00:53:17 -0800600 elif self.name == "OFOxs":
601 virtual_members += [
602 JavaVirtualMember(self, "value", java_type.generic_t),
603 JavaVirtualMember(self, "statField", java_type.make_stat_field_jtype("T")),
604 ]
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800605 elif self.ir_class.is_subclassof("of_oxm"):
Andreas Wundsam83aebd32013-11-19 10:43:50 -0800606 value = find(lambda m: m.name=="value", self.ir_model_members)
607 if value:
608 field_type = java_type.make_match_field_jtype(value.java_type.public_type)
609 else:
610 field_type = java_type.make_match_field_jtype()
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700611
Andreas Wundsama0981022013-10-02 18:15:06 -0700612 virtual_members += [
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700613 JavaVirtualMember(self, "matchField", field_type),
614 JavaVirtualMember(self, "masked", java_type.boolean),
Andreas Wundsam83aebd32013-11-19 10:43:50 -0800615 JavaVirtualMember(self, "canonical", java_type.make_oxm_jtype(value.java_type.public_type),
Andreas Wundsam661a2222013-11-05 17:18:59 -0800616 custom_template=lambda builder: "OFOxm{}_getCanonical.java".format(".Builder" if builder else "")),
Andreas Wundsama0981022013-10-02 18:15:06 -0700617 ]
618 if not find(lambda x: x.name == "mask", self.ir_model_members):
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800619 virtual_members.append(
620 JavaVirtualMember(self, "mask", find(lambda x: x.name == "value", self.ir_model_members).java_type))
Murat Parlakisikf95672c2016-12-05 00:53:17 -0800621 elif self.ir_class.is_subclassof("of_oxs"):
622 value = find(lambda m: m.name=="value", self.ir_model_members)
623 if value:
624 field_type = java_type.make_stat_field_jtype(value.java_type.public_type)
625 else:
626 field_type = java_type.make_stat_field_jtype()
627
628 virtual_members += [
629 JavaVirtualMember(self, "statField", field_type),
630 JavaVirtualMember(self, "masked", java_type.boolean),
631 JavaVirtualMember(self, "canonical", java_type.make_oxs_jtype(value.java_type.public_type),
632 custom_template=lambda builder: "OFOxs{}_getCanonical.java".format(".Builder" if builder else "")),
633 ]
634 if not find(lambda x: x.name == "mask", self.ir_model_members):
635 virtual_members.append(
636 JavaVirtualMember(self, "mask", find(lambda x: x.name == "value", self.ir_model_members).java_type))
Andreas Wundsam4d0752b2014-12-01 13:14:07 -0800637 elif self.name =="OFErrorMsg":
638 virtual_members += [ JavaVirtualMember(self, "data", java_type.error_cause_data) ]
Andreas Wundsama0981022013-10-02 18:15:06 -0700639
640 if not find(lambda m: m.name == "version", self.ir_model_members):
641 virtual_members.append(JavaVirtualMember(self, "version", java_type.of_version))
642
643 return tuple(virtual_members)
Andreas Wundsam27303462013-07-16 12:52:35 -0700644
645 @property
646 @memoize
647 def is_virtual(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700648 """ Is this interface virtual. If so, do not generate a builder interface """
Andreas Wundsam001b1822013-08-02 22:25:55 -0700649 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 -0700650
651 @property
Andreas Wundsam5204de22013-07-30 11:34:45 -0700652 def is_universal(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700653 """ Is this interface universal, i.e., does it exist in all OF versions? """
Andreas Wundsam5204de22013-07-30 11:34:45 -0700654 return len(self.all_versions) == len(model.versions)
655
656 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700657 @memoize
658 def all_versions(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700659 """ return list of all versions that this interface exists in """
Andreas Wundsam27303462013-07-16 12:52:35 -0700660 return self.version_map.keys()
661
Andreas Wundsam5204de22013-07-30 11:34:45 -0700662 def has_version(self, version):
663 return version in self.version_map
664
Andreas Wundsam27303462013-07-16 12:52:35 -0700665 def versioned_class(self, version):
666 return JavaOFClass(self, version, self.version_map[version])
667
668 @property
669 @memoize
670 def versioned_classes(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700671 return [ self.versioned_class(version) for version in self.all_versions ]
672
673#######################################################################
674### (Versioned) Classes
675#######################################################################
676
677class JavaOFClass(object):
678 """ Models an OpenFlow Message class for the purpose of the java class.
Andreas Wundsamd40ddbf2013-07-25 15:40:47 -0700679 Version specific child of a JavaOFInterface
Andreas Wundsam27303462013-07-16 12:52:35 -0700680 """
681 def __init__(self, interface, version, ir_class):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700682 """
683 @param interface JavaOFInterface instance of the parent interface
684 @param version JavaOFVersion
685 @param ir_class OFClass from loxi_ir
686 """
Andreas Wundsam27303462013-07-16 12:52:35 -0700687 self.interface = interface
688 self.ir_class = ir_class
689 self.c_name = self.ir_class.name
690 self.version = version
691 self.constant_name = self.c_name.upper().replace("OF_", "")
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800692 self.package = "org.projectfloodlight.openflow.protocol.ver%s" % version.dotless_version
Andreas Wundsame916d6f2013-07-30 11:33:58 -0700693 self.generated = False
694
695 @property
696 @memoize
697 def unit_test(self):
Yotam Harchol466b3212013-08-15 12:14:46 -0700698 return JavaUnitTestSet(self)
Andreas Wundsam27303462013-07-16 12:52:35 -0700699
700 @property
701 def name(self):
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800702 return "%sVer%s" % (self.interface.name, self.version.dotless_version)
Andreas Wundsam27303462013-07-16 12:52:35 -0700703
704 @property
Andreas Wundsam5204de22013-07-30 11:34:45 -0700705 def variable_name(self):
Andreas Wundsam781d99f2013-10-23 15:25:56 -0700706 return self.name[2].lower() + self.name[3:]
Andreas Wundsam5204de22013-07-30 11:34:45 -0700707
708 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700709 def length(self):
710 if self.is_fixed_length:
711 return self.min_length
712 else:
713 raise Exception("No fixed length for class %s, version %s" % (self.name, self.version))
714
715 @property
716 def min_length(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700717 """ @return the minimum wire length of an instance of this class in bytes """
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800718 return self.ir_class.base_length
Andreas Wundsam27303462013-07-16 12:52:35 -0700719
720 @property
721 def is_fixed_length(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700722 """ true iff this class serializes to a fixed length on the wire """
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800723 return self.ir_class.is_fixed_length and not self.is_virtual
Andreas Wundsam27303462013-07-16 12:52:35 -0700724
725 def all_properties(self):
726 return self.interface.members
727
Andreas Wundsam27303462013-07-16 12:52:35 -0700728 @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 Wundsam83aebd32013-11-19 10:43:50 -0800757 if self.ir_class.is_subclassof("of_oxm"):
758 value_member = find(lambda m: m.name, self.ir_model_members)
759 if value_member:
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700760 oxm_entry = model.oxm_map[self.interface.name]
Andreas Wundsama0981022013-10-02 18:15:06 -0700761 virtual_members += [
Andreas Wundsam83aebd32013-11-19 10:43:50 -0800762 JavaVirtualMember(self, "matchField", java_type.make_match_field_jtype(value_member.java_type.public_type), "MatchField.%s" % oxm_entry.value),
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700763 JavaVirtualMember(self, "masked", java_type.boolean, "true" if oxm_entry.masked else "false"),
Andreas Wundsam661a2222013-11-05 17:18:59 -0800764 ]
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700765 else:
Andreas Wundsama0981022013-10-02 18:15:06 -0700766 virtual_members += [
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700767 JavaVirtualMember(self, "matchField", java_type.make_match_field_jtype(), "null"),
768 JavaVirtualMember(self, "masked", java_type.boolean, "false"),
Andreas Wundsam661a2222013-11-05 17:18:59 -0800769 ]
Murat Parlakisikf95672c2016-12-05 00:53:17 -0800770 elif self.ir_class.is_subclassof("of_oxs"):
771 value_member = find(lambda m: m.name, self.ir_model_members)
772 if value_member:
773 oxs_entry = model.oxs_map[self.interface.name]
774 virtual_members += [
775 JavaVirtualMember(self, "statField", java_type.make_stat_field_jtype(value_member.java_type.public_type), "StatField.%s" % oxs_entry.value),
776 JavaVirtualMember(self, "masked", java_type.boolean, "true" if oxs_entry.masked else "false"),
777 ]
778 else:
779 virtual_members += [
780 JavaVirtualMember(self, "statField", java_type.make_stat_field_jtype(), "null"),
781 JavaVirtualMember(self, "masked", java_type.boolean, "false"),
782 ]
Andreas Wundsama0981022013-10-02 18:15:06 -0700783 if not find(lambda m: m.name == "version", self.ir_model_members):
784 virtual_members.append(JavaVirtualMember(self, "version", java_type.of_version, "OFVersion.%s" % self.version.constant_version))
785
786 return tuple(virtual_members)
Andreas Wundsam27303462013-07-16 12:52:35 -0700787
Andreas Wundsam661a2222013-11-05 17:18:59 -0800788 @memoize
789 def member_by_name(self, name):
790 return find(lambda m: m.name == name, self.members)
791
Andreas Wundsam27303462013-07-16 12:52:35 -0700792 def all_versions(self):
793 return [ JavaOFVersion(int_version)
794 for int_version in of_g.unified[self.c_name]
795 if int_version != 'union' and int_version != 'object_id' ]
796
797 def version_is_inherited(self, version):
798 return 'use_version' in of_g.unified[self.ir_class.name][version.int_version]
799
800 def inherited_from(self, version):
801 return JavaOFVersion(of_g.unified[self.ir_class.name][version.int_version]['use_version'])
802
803 @property
804 def is_virtual(self):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700805 return self.ir_class.virtual # type_maps.class_is_virtual(self.c_name) or self.ir_class.virtual
806
807 @property
808 def discriminator(self):
809 return find(lambda m: isinstance(m, OFDiscriminatorMember), self.ir_class.members)
Andreas Wundsam27303462013-07-16 12:52:35 -0700810
811 @property
812 def is_extension(self):
813 return type_maps.message_is_extension(self.c_name, -1)
814
Andreas Wundsam758c9cc2013-08-01 22:16:06 -0700815 @property
816 def align(self):
817 return int(self.ir_class.params['align']) if 'align' in self.ir_class.params else 0
818
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700819 @property
Andreas Wundsam5da68512013-10-22 22:18:00 -0700820 def length_includes_align(self):
821 return self.ir_class.params['length_includes_align'] == "True" if 'length_includes_align' in self.ir_class.params else False
822
823 @property
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700824 @memoize
825 def superclass(self):
826 return find(lambda c: c.version == self.version and c.c_name == self.ir_class.superclass, model.all_classes)
827
828 @property
829 @memoize
830 def subclasses(self):
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800831 return [ c for c in model.all_classes if c.version == self.version and c.ir_class.superclass
832 and c.ir_class.superclass.name == self.c_name ]
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700833
Andreas Wundsam27303462013-07-16 12:52:35 -0700834#######################################################################
835### Member
836#######################################################################
837
838
839class JavaMember(object):
840 """ Models a property (member) of an openflow class. """
841 def __init__(self, msg, name, java_type, member):
842 self.msg = msg
843 self.name = name
844 self.java_type = java_type
845 self.member = member
846 self.c_name = self.member.name if(hasattr(self.member, "name")) else ""
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700847
848 @property
849 def title_name(self):
850 return self.name[0].upper() + self.name[1:]
851
852 @property
853 def constant_name(self):
854 return self.c_name.upper()
855
856 @property
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700857 def getter_name(self):
858 return ("is" if self.java_type.public_type == "boolean" else "get") + self.title_name
859
860 @property
861 def setter_name(self):
862 return "set" + self.title_name
863
864 @property
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700865 def default_name(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700866 if self.is_fixed_value:
867 return self.constant_name
868 else:
869 return "DEFAULT_"+self.constant_name
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700870
871 @property
872 def default_value(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700873 if self.is_fixed_value:
874 return self.enum_value
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700875 else:
Andreas Wundsam142dd2a2013-09-23 14:47:37 -0700876 default = self.java_type.default_op(self.msg.version)
877 if default == "null" and not self.is_nullable:
878 return None
879 else:
880 return default
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700881
882 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700883 def enum_value(self):
884 if self.name == "version":
885 return "OFVersion.%s" % self.msg.version.constant_version
886
887 java_type = self.java_type.public_type;
888 try:
889 global model
890 enum = model.enum_by_name(java_type)
891 entry = enum.entry_by_version_value(self.msg.version, self.value)
892 return "%s.%s" % ( enum.name, entry.name)
893 except KeyError, e:
Andreas Wundsam9f8657a2014-10-10 21:28:17 -0700894 logger.debug("No enum found for type %s version %s value %s", java_type, self.msg.version, self.value)
Andreas Wundsam27303462013-07-16 12:52:35 -0700895 return self.value
896
897 @property
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700898 def is_pad(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700899 return isinstance(self.member, OFPadMember)
900
901 def is_type_value(self, version=None):
902 if(version==None):
903 return any(self.is_type_value(version) for version in self.msg.all_versions)
904 try:
905 return self.c_name in get_type_values(self.msg.c_name, version.int_version)
906 except:
907 return False
908
909 @property
910 def is_field_length_value(self):
911 return isinstance(self.member, OFFieldLengthMember)
912
913 @property
Andreas Wundsam001b1822013-08-02 22:25:55 -0700914 def is_discriminator(self):
915 return isinstance(self.member, OFDiscriminatorMember)
916
917 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700918 def is_length_value(self):
919 return isinstance(self.member, OFLengthMember)
920
921 @property
922 def is_public(self):
923 return not (self.is_pad or self.is_length_value)
924
925 @property
926 def is_data(self):
927 return isinstance(self.member, OFDataMember) and self.name != "version"
928
929 @property
930 def is_fixed_value(self):
931 return hasattr(self.member, "value") or self.name == "version" \
932 or ( self.name == "length" and self.msg.is_fixed_length) \
933 or ( self.name == "len" and self.msg.is_fixed_length)
934
935 @property
936 def value(self):
937 if self.name == "version":
938 return self.msg.version.int_version
939 elif self.name == "length" or self.name == "len":
940 return self.msg.length
Andreas Wundsam27303462013-07-16 12:52:35 -0700941 else:
Andreas Wundsam001b1822013-08-02 22:25:55 -0700942 return self.java_type.format_value(self.member.value)
943
944 @property
945 def priv_value(self):
946 if self.name == "version":
Andreas Wundsam22ba3af2013-10-04 16:00:30 -0700947 return self.java_type.format_value(self.msg.version.int_version, pub_type=False)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700948 elif self.name == "length" or self.name == "len":
Andreas Wundsam22ba3af2013-10-04 16:00:30 -0700949 return self.java_type.format_value(self.msg.length, pub_type=False)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700950 else:
951 return self.java_type.format_value(self.member.value, pub_type=False)
952
Murat Parlakisikf95672c2016-12-05 00:53:17 -0800953 @property
954 def needs_setter(self):
955 if self.is_writeable:
956 return True
957 super_class = self.msg.super_class
958 if super_class:
959 super_member = super_class.member_by_name(self.name)
960 if super_member:
961 return super_member.needs_setter
962 return False
Andreas Wundsam27303462013-07-16 12:52:35 -0700963
964 @property
965 def is_writeable(self):
966 return self.is_data and not self.name in model.write_blacklist[self.msg.name]
967
968 def get_type_value_info(self, version):
969 return get_type_values(msg.c_name, version.int_version)[self.c_name]
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700970
971 @property
972 def length(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700973 if hasattr(self.member, "length"):
974 return self.member.length
975 else:
Andreas Wundsam5204de22013-07-30 11:34:45 -0700976 count, base = loxi_utils.type_dec_to_count_base(self.member.type)
Andreas Wundsam27303462013-07-16 12:52:35 -0700977 return of_g.of_base_types[base]['bytes'] * count
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700978
979 @staticmethod
Andreas Wundsam27303462013-07-16 12:52:35 -0700980 def for_of_member(java_class, member):
981 if isinstance(member, OFPadMember):
982 return JavaMember(None, "", None, member)
983 else:
984 if member.name == 'len':
985 name = 'length'
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700986 elif member.name == 'value_mask':
987 name = 'mask'
Andreas Wundsamacd57d52013-10-18 17:35:01 -0700988 elif member.name == 'group_id':
989 name = 'group'
Andreas Wundsam27303462013-07-16 12:52:35 -0700990 else:
991 name = java_type.name_c_to_camel(member.name)
992 j_type = java_type.convert_to_jtype(java_class.c_name, member.name, member.oftype)
993 return JavaMember(java_class, name, j_type, member)
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700994
995 @property
996 def is_universal(self):
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800997 for version, ir_class in self.msg.ir_class.version_classes.items():
998 if not ir_class.member_by_name(self.member.name):
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700999 return False
1000 return True
1001
Andreas Wundsam2be7da52013-08-22 07:34:25 -07001002 @property
1003 def is_virtual(self):
1004 return False
1005
Andreas Wundsam27303462013-07-16 12:52:35 -07001006 def __hash__(self):
1007 return hash(self.name)
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001008
Andreas Wundsam27303462013-07-16 12:52:35 -07001009 def __eq__(self, other):
1010 if other is None or type(self) != type(other):
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001011 return False
Andreas Wundsam27303462013-07-16 12:52:35 -07001012 return (self.name,) == (other.name,)
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001013
Andreas Wundsamf1949682013-09-23 14:48:31 -07001014 @property
1015 def is_nullable(self):
1016 return self.name in model.nullable_map[self.msg.name]
1017
1018
Andreas Wundsam2be7da52013-08-22 07:34:25 -07001019class JavaVirtualMember(JavaMember):
1020 """ 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 -08001021 def __init__(self, msg, name, java_type, value=None, custom_template=None):
Andreas Wundsam2be7da52013-08-22 07:34:25 -07001022 JavaMember.__init__(self, msg, name, java_type, member=None)
1023 self._value = value
Andreas Wundsam4b8661f2013-11-05 17:17:28 -08001024 self.custom_template = custom_template
Andreas Wundsam2be7da52013-08-22 07:34:25 -07001025
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
Andreas Wundsamc8912c12013-11-15 13:44:48 -08001054 first_data_file_name = "of{version}/{name}.data".format(version=java_class.version.dotless_version,
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001055 name=java_class.c_name[3:])
Andreas Wundsamc8912c12013-11-15 13:44:48 -08001056 glob_file_name = "of{version}/{name}__*.data".format(version=java_class.version.dotless_version,
Andreas Wundsamaa4f3cd2013-10-22 22:19:08 -07001057 name=java_class.c_name[3:])
Yotam Harchol466b3212013-08-15 12:14:46 -07001058 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))
Andreas Wundsamaa4f3cd2013-10-22 22:19:08 -07001062
Yotam Harchol466b3212013-08-15 12:14:46 -07001063 i = 1
Andreas Wundsamaa4f3cd2013-10-22 22:19:08 -07001064 for f in test_data.glob(glob_file_name):
1065 m = re.match(".*__(.*).data", f)
1066 if m:
1067 suffix = java_type.name_c_to_caps_camel(m.group(1))
1068 else:
1069 suffix = str(i)
1070 i += 1
1071 test_class_name = self.java_class.name + suffix + "Test"
1072 self.test_units.append(JavaUnitTest(java_class, f, test_class_name))
Andreas Wundsamf1949682013-09-23 14:48:31 -07001073
Yotam Harchol466b3212013-08-15 12:14:46 -07001074 @property
1075 def package(self):
1076 return self.java_class.package
1077
1078 @property
1079 def has_test_data(self):
1080 return len(self.test_units) > 0
1081
1082 @property
1083 def length(self):
1084 return len(self.test_units)
Andreas Wundsamf1949682013-09-23 14:48:31 -07001085
Yotam Harchol466b3212013-08-15 12:14:46 -07001086 def get_test_unit(self, i):
1087 return self.test_units[i]
1088
1089
1090class JavaUnitTest(object):
1091 def __init__(self, java_class, file_name=None, test_class_name=None):
1092 self.java_class = java_class
1093 if file_name is None:
Andreas Wundsamc8912c12013-11-15 13:44:48 -08001094 self.data_file_name = "of{version}/{name}.data".format(version=java_class.version.dotless_version,
Yotam Harchol466b3212013-08-15 12:14:46 -07001095 name=java_class.c_name[3:])
1096 else:
1097 self.data_file_name = file_name
1098 if test_class_name is None:
1099 self.test_class_name = self.java_class.name + "Test"
1100 else:
1101 self.test_class_name = test_class_name
Andreas Wundsamf1949682013-09-23 14:48:31 -07001102
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001103 @property
1104 def package(self):
1105 return self.java_class.package
1106
1107 @property
1108 def name(self):
Yotam Harchol466b3212013-08-15 12:14:46 -07001109 return self.test_class_name
Murat Parlakisikf95672c2016-12-05 00:53:17 -08001110
Yotam Harchol595c6442013-09-27 16:29:08 -07001111 @property
1112 def interface(self):
1113 return self.java_class.interface
Murat Parlakisikf95672c2016-12-05 00:53:17 -08001114
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001115 @property
1116 def has_test_data(self):
1117 return test_data.exists(self.data_file_name)
1118
1119 @property
1120 @memoize
1121 def test_data(self):
1122 return test_data.read(self.data_file_name)
1123
1124
Andreas Wundsam27303462013-07-16 12:52:35 -07001125#######################################################################
1126### Enums
1127#######################################################################
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001128
Andreas Wundsam27303462013-07-16 12:52:35 -07001129class JavaEnum(object):
Andreas Wundsamf299baa2014-10-23 20:15:32 -07001130 def __init__(self, c_name, stable, version_enum_map):
Andreas Wundsam27303462013-07-16 12:52:35 -07001131 self.c_name = c_name
Andreas Wundsamf299baa2014-10-23 20:15:32 -07001132 self.stable = stable
Andreas Wundsambe168f72013-08-03 22:49:35 -07001133
Rob Vaterlausfeee3712013-09-30 11:24:19 -07001134 self.name = "OF" + java_type.name_c_to_caps_camel("_".join(c_name.split("_")[1:]))
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001135
Andreas Wundsam27303462013-07-16 12:52:35 -07001136 # Port_features has constants that start with digits
1137 self.name_prefix = "PF_" if self.name == "OFPortFeatures" else ""
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001138
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001139 self.version_enums = version_enum_map
1140
1141 entry_name_version_value_map = OrderedDefaultDict(lambda: OrderedDict())
1142 for version, ir_enum in version_enum_map.items():
1143 for ir_entry in ir_enum.entries:
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001144 entry_name_version_value_map[ir_entry.name][version] = ir_entry.value
1145
Andreas Wundsam27303462013-07-16 12:52:35 -07001146 self.entries = [ JavaEnumEntry(self, name, version_value_map)
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001147 for (name, version_value_map) in entry_name_version_value_map.items() ]
Andreas Wundsam43526532013-08-01 22:03:50 -07001148
1149 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 -07001150 self.package = "org.projectfloodlight.openflow.protocol"
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001151
Andreas Wundsamf299baa2014-10-23 20:15:32 -07001152 static_metadata = model.enum_metadata_map[self.name]
1153 if self.stable:
1154 # need this to look up wire_type, which does not matter
1155 any_version = version_enum_map.keys()[0]
1156 # if this is a 'stable' enum, i.e., its value won't change, add
1157 # a "Metadata" (virtual) field "StableValue" to it that returns
1158 # its wirevalue.
1159 stable_value = JavaModel.OFEnumPropertyMetadata("StableValue",
1160 self.wire_type(any_version),
1161 value = lambda entry: entry.stable_value)
1162
1163 self.metadata = JavaModel.OFEnumMetadata(
1164 properties=static_metadata.properties + (stable_value, ),
1165 to_string=static_metadata.to_string
1166 )
1167 else:
1168 self.metadata = static_metadata
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -07001169
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001170 def wire_type(self, version):
1171 ir_enum = self.version_enums[version]
1172 if "wire_type" in ir_enum.params:
1173 return java_type.convert_enum_wire_type_to_jtype(ir_enum.params["wire_type"])
1174 else:
1175 return java_type.u8
1176
1177 @property
Andreas Wundsam7cfeac32013-09-17 13:53:48 -07001178 @memoize
1179 def is_bitmask(self):
1180 return any(ir_enum.is_bitmask for ir_enum in self.version_enums.values())
1181
1182 @property
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001183 def versions(self):
1184 return self.version_enums.keys()
1185
Andreas Wundsam27303462013-07-16 12:52:35 -07001186 @memoize
1187 def entry_by_name(self, name):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -07001188 res = find(lambda e: e.name == name, self.entries)
1189 if res:
1190 return res
1191 else:
Andreas Wundsam27303462013-07-16 12:52:35 -07001192 raise KeyError("Enum %s: no entry with name %s" % (self.name, name))
1193
1194 @memoize
1195 def entry_by_c_name(self, name):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -07001196 res = find(lambda e: e.c_name == name, self.entries)
1197 if res:
1198 return res
1199 else:
Andreas Wundsam27303462013-07-16 12:52:35 -07001200 raise KeyError("Enum %s: no entry with c_name %s" % (self.name, name))
1201
1202 @memoize
1203 def entry_by_version_value(self, version, value):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -07001204 res = find(lambda e: e.values[version] == value if version in e.values else False, self.entries)
1205 if res:
1206 return res
1207 else:
Andreas Wundsam27303462013-07-16 12:52:35 -07001208 raise KeyError("Enum %s: no entry with version %s, value %s" % (self.name, version, value))
1209
1210# values: Map JavaVersion->Value
1211class JavaEnumEntry(object):
1212 def __init__(self, enum, name, values):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001213 self.enum = enum
Andreas Wundsam27303462013-07-16 12:52:35 -07001214 self.name = enum.name_prefix + "_".join(name.split("_")[1:]).upper()
1215 self.values = values
1216
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -07001217 @property
1218 def constructor_params(self):
Andreas Wundsamf299baa2014-10-23 20:15:32 -07001219 return [ (m.type, m.value(self)) for m in self.enum.metadata.properties ]
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -07001220
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001221 def has_value(self, version):
1222 return version in self.values
1223
Andreas Wundsam27303462013-07-16 12:52:35 -07001224 def value(self, version):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001225 return self.values[version]
1226
1227 def format_value(self, version):
1228 res = self.enum.wire_type(version).format_value(self.values[version])
Andreas Wundsam27303462013-07-16 12:52:35 -07001229 return res
1230
Andreas Wundsam27303462013-07-16 12:52:35 -07001231 def all_values(self, versions, not_present=None):
1232 return [ self.values[version] if version in self.values else not_present for version in versions ]
Andreas Wundsamc1acfcc2013-09-20 13:10:20 -07001233
1234 @property
Andreas Wundsamf299baa2014-10-23 20:15:32 -07001235 def stable_value(self):
1236 if self.enum.stable:
1237 return self.values.values()[0]
1238 else:
1239 raise Exception("Enum {} not stable".format(self.enum.name))
1240
1241 @property
Andreas Wundsamc1acfcc2013-09-20 13:10:20 -07001242 @memoize
1243 def masked_enum_group(self):
1244 group = find(lambda g: self.name in g.members, model.masked_enum_groups[self.enum.name])
1245 return group
1246
1247 @property
1248 @memoize
1249 def is_mask(self):
1250 return any(self.name == g.mask for g in model.masked_enum_groups[self.enum.name])