blob: c8f70e6eaf25680e511b1c9552f7e69837f149d3 [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',)),
77 OFAction=set(('type',)),
78 OFInstruction=set(('type',)),
79 OFFlowMod=set(('command', )),
80 OFExperimenter=set(('data','subtype')),
81 OFActionExperimenter=set(('data',)),
82 OFBsnTlv=set(('type',)))
Andreas Wundsamf1949682013-09-23 14:48:31 -070083 # interfaces that are virtual
Andreas Wundsam001b1822013-08-02 22:25:55 -070084 virtual_interfaces = set(['OFOxm', 'OFInstruction', 'OFFlowMod', 'OFBsnVport' ])
Andreas Wundsam27303462013-07-16 12:52:35 -070085
Andreas Wundsamf1949682013-09-23 14:48:31 -070086 # Registry of nullable properties:
87 # ${java_class_name} -> set(${java_property_name})
88 nullable_map = defaultdict(lambda: set(),
89 )
90
91 # represents a subgroup of a bitmask enum that is actualy a normal enumerable within a masked part of the enum
92 # e.g., the flags STP.* in OF1.0 port state are bit mask entries, but instead enumerables according to the mask "STP_MASK"
93 # name: a name for the group
94 # mask: java name of the enum entry that defines the mask
95 # members: set of names of the members of the group
Andreas Wundsamc1acfcc2013-09-20 13:10:20 -070096 MaskedEnumGroup = namedtuple("MaskedEnumGroup", ("name", "mask", "members"))
97
Andreas Wundsamf1949682013-09-23 14:48:31 -070098 # registry of MaskedEnumGroups (see above).
99 # map: ${java_enum_name}: tuple(MaskedEnumGroup)
Andreas Wundsamc1acfcc2013-09-20 13:10:20 -0700100 masked_enum_groups = defaultdict(lambda: (),
Andreas Wundsamaebe5182013-09-24 13:01:07 -0700101 OFPortState = (MaskedEnumGroup("stp_flags", mask="STP_MASK", members=set(("STP_LISTEN", "STP_LEARN", "STP_FORWARD", "STP_BLOCK"))), ),
102 OFConfigFlags = (
103 MaskedEnumGroup("frag_flags", mask="FRAG_MASK", members=set(("FRAG_NORMAL", "FRAG_DROP", "FRAG_REASM"))),
Andreas Wundsam376cef52013-09-24 13:52:18 -0700104 ),
105 OFTableConfig = (
106 MaskedEnumGroup("table_miss_flags", mask="TABLE_MISS_MASK", members=set(("TABLE_MISS_CONTROLLER", "TABLE_MISS_CONTINUE", "TABLE_MISS_DROP"))),
107 ),
Andreas Wundsam057540b2013-11-19 16:51:36 -0800108 OFGetConfigReply = (
109 MaskedEnumGroup("flags", mask="OFP_FRAG_MASK", members=set(("FRAG_NORMAL", "FRAG_DROP", "FRAG_REASM"))),
110 ),
111 OFSetConfig = (
112 MaskedEnumGroup("flags", mask="OFP_FRAG_MASK", members=set(("FRAG_NORMAL", "FRAG_DROP", "FRAG_REASM"))),
113 ),
Andreas Wundsamc1acfcc2013-09-20 13:10:20 -0700114 )
115
Andreas Wundsamf1949682013-09-23 14:48:31 -0700116 # represents a metadata property associated with an EnumClass
117 # name:
Rob Vaterlaus6035bf52013-09-17 19:32:38 -0700118 class OFEnumPropertyMetadata(namedtuple("OFEnumPropertyMetadata", ("name", "type", "value"))):
Andreas Wundsamf1949682013-09-23 14:48:31 -0700119 """
120 represents a metadata property associated with an Enum Class
121 @param name name of metadata property
122 @param type java_type instance describing the type
123 @value: Generator function f(entry) that generates the value
124 """
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -0700125 @property
126 def variable_name(self):
127 return self.name[0].lower() + self.name[1:]
128
129 @property
130 def getter_name(self):
Rob Vaterlausbda9ce62013-09-17 14:45:48 -0700131 prefix = "is" if self.type == java_type.boolean else "get"
132 return prefix+self.name
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -0700133
Andreas Wundsamf1949682013-09-23 14:48:31 -0700134 """ Metadata container. """
Rob Vaterlaus6035bf52013-09-17 19:32:38 -0700135 OFEnumMetadata = namedtuple("OFEnumMetadata", ("properties", "to_string"))
136
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -0700137 def gen_port_speed(enum_entry):
Andreas Wundsamf1949682013-09-23 14:48:31 -0700138 """ Generator function for OFortFeatures.PortSpeed"""
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -0700139 splits = enum_entry.name.split("_")
140 if len(splits)>=2:
141 m = re.match(r'\d+[MGTP]B', splits[1])
142 if m:
143 return "PortSpeed.SPEED_{}".format(splits[1])
144 return "PortSpeed.SPEED_NONE";
145
Rob Vaterlausbda9ce62013-09-17 14:45:48 -0700146 def gen_stp_state(enum_entry):
Andreas Wundsamf1949682013-09-23 14:48:31 -0700147 """ Generator function for OFPortState.StpState"""
Rob Vaterlausbda9ce62013-09-17 14:45:48 -0700148 splits = enum_entry.name.split("_")
149 if len(splits)>=1:
150 if splits[0] == "STP":
151 return "true"
152 return "false"
153
Andreas Wundsamf1949682013-09-23 14:48:31 -0700154 # registry for metadata properties for enums
155 # map: ${java_enum_name}: OFEnumMetadata
Rob Vaterlaus6035bf52013-09-17 19:32:38 -0700156 enum_metadata_map = defaultdict(lambda: JavaModel.OFEnumMetadata((), None),
Rob Vaterlausa049dee2013-09-18 16:18:37 -0700157 OFPortFeatures = OFEnumMetadata((OFEnumPropertyMetadata("PortSpeed", java_type.port_speed, gen_port_speed),), None),
158 OFPortState = OFEnumMetadata((OFEnumPropertyMetadata("StpState", java_type.boolean, gen_stp_state),), None),
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -0700159 )
160
Andreas Wundsam27303462013-07-16 12:52:35 -0700161 @property
162 @memoize
163 def versions(self):
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800164 return OrderedSet( JavaOFVersion(ir_version) for ir_version in OFVersions.target_versions)
Andreas Wundsam27303462013-07-16 12:52:35 -0700165
166 @property
167 @memoize
168 def interfaces(self):
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800169 interfaces = [ JavaOFInterface(ir_class) for ir_class in loxi_globals.unified.classes ]
Andreas Wundsambe168f72013-08-03 22:49:35 -0700170 interfaces = [ i for i in interfaces if i.name not in self.interface_blacklist ]
171
Andreas Wundsam27303462013-07-16 12:52:35 -0700172 return interfaces
173
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700174 @memoize
175 def interface_by_name(self, name):
176 return find(lambda i: erase_type_annotation(i.name) == erase_type_annotation(name), self.interfaces)
177
Andreas Wundsam27303462013-07-16 12:52:35 -0700178 @property
179 @memoize
Andreas Wundsam001b1822013-08-02 22:25:55 -0700180 def all_classes(self):
181 return [clazz for interface in self.interfaces for clazz in interface.versioned_classes]
182
183 @property
184 @memoize
Andreas Wundsam27303462013-07-16 12:52:35 -0700185 def enums(self):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700186 name_version_enum_map = OrderedDefaultDict(lambda: OrderedDict())
Andreas Wundsamf299baa2014-10-23 20:15:32 -0700187 name_stable_map = {}
Andreas Wundsam27303462013-07-16 12:52:35 -0700188
189 for version in self.versions:
Andreas Wundsamf299baa2014-10-23 20:15:32 -0700190 logger.debug("version: {}".format(version.ir_version))
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800191 of_protocol = loxi_globals.ir[version.ir_version]
Andreas Wundsam27303462013-07-16 12:52:35 -0700192 for enum in of_protocol.enums:
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700193 name_version_enum_map[enum.name][version] = enum
Andreas Wundsamf299baa2014-10-23 20:15:32 -0700194 stable = (enum.params.get('stable') == 'True')
Andreas Wundsam27303462013-07-16 12:52:35 -0700195
Andreas Wundsamf299baa2014-10-23 20:15:32 -0700196 logger.debug("Enum: %s stable: %s", enum.name, stable)
197
198 if not enum.name in name_stable_map:
199 name_stable_map[enum.name] = stable
200 else:
201 if name_stable_map[enum.name] != stable:
202 raise Exception("Inconsistent enum stability (should be caught " +\
203 " by IR)")
204
205 enums = [ JavaEnum(name, name_stable_map[name], version_enum_map) for name, version_enum_map,
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700206 in name_version_enum_map.items() ]
Andreas Wundsam27303462013-07-16 12:52:35 -0700207
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700208 # inelegant - need java name here
209 enums = [ enum for enum in enums if enum.name not in self.enum_blacklist ]
Andreas Wundsam27303462013-07-16 12:52:35 -0700210 return enums
211
212 @memoize
213 def enum_by_name(self, name):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700214 res = find(lambda e: e.name == name, self.enums)
215 if not res:
Andreas Wundsam27303462013-07-16 12:52:35 -0700216 raise KeyError("Could not find enum with name %s" % name)
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700217 return res
Andreas Wundsam27303462013-07-16 12:52:35 -0700218
Andreas Wundsam5204de22013-07-30 11:34:45 -0700219 @property
220 @memoize
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700221 def of_factories(self):
Yotam Harchol791e4882013-09-05 16:32:56 -0700222 prefix = "org.projectfloodlight.openflow.protocol"
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700223
224 factories = OrderedDict()
225
Andreas Wundsamd4b22692014-01-14 14:17:26 -0800226 sub_factory_classes = ("OFAction", "OFInstruction", "OFMeterBand", "OFOxm", "OFQueueProp", "OFErrorMsg", "OFActionId", "OFInstructionId", "OFBsnTlv")
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700227 for base_class in sub_factory_classes:
228 package = base_class[2:].lower()
229 remove_prefix = base_class[2].lower() + base_class[3:]
230
231 # HACK need to have a better way to deal with parameterized base classes
232 annotated_base_class = base_class + "<?>" if base_class == "OFOxm" else base_class
233
234 factories[base_class] = OFFactory(package="%s.%s" % (prefix, package),
Andreas Wundsamd1ca4f92013-12-10 18:58:44 -0800235 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 -0700236
237 factories[""] = OFFactory(
238 package=prefix,
Andreas Wundsam5204de22013-07-30 11:34:45 -0700239 name="OFFactory",
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700240 remove_prefix="",
241 members=[], base_class="OFMessage", sub_factories=OrderedDict(
Andreas Wundsamcb17b232013-09-28 19:05:36 -0700242 ("{}{}s".format(n[2].lower(), n[3:]), "{}s".format(n)) for n in sub_factory_classes ),
243 xid_generator=True)
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700244
245 for i in self.interfaces:
246 for n, factory in factories.items():
247 if n == "":
248 factory.members.append(i)
249 break
250 else:
251 super_class = self.interface_by_name(n)
252 if i.is_instance_of(super_class):
253 factory.members.append(i)
254 break
255 return factories.values()
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800256
Yotam Harchol595c6442013-09-27 16:29:08 -0700257 @memoize
258 def factory_of(self, interface):
259 for factory in self.of_factories:
260 if interface in factory.members:
261 return factory
262 return None
Andreas Wundsam5204de22013-07-30 11:34:45 -0700263
264 def generate_class(self, clazz):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700265 """ return wether or not to generate implementation class clazz.
266 Now true for everything except OFTableModVer10.
267 @param clazz JavaOFClass instance
268 """
Andreas Wundsam43526532013-08-01 22:03:50 -0700269 if clazz.interface.name.startswith("OFMatchV"):
270 return True
Andreas Wundsam001b1822013-08-02 22:25:55 -0700271 elif clazz.name == "OFTableModVer10":
272 # tablemod ver 10 is a hack and has no oftype defined
273 return False
Andreas Wundsam5204de22013-07-30 11:34:45 -0700274 if loxi_utils.class_is_message(clazz.interface.c_name):
275 return True
276 if loxi_utils.class_is_oxm(clazz.interface.c_name):
277 return True
Andreas Wundsam43526532013-08-01 22:03:50 -0700278 if loxi_utils.class_is_action(clazz.interface.c_name):
279 return True
280 if loxi_utils.class_is_instruction(clazz.interface.c_name):
281 return True
Andreas Wundsam5204de22013-07-30 11:34:45 -0700282 else:
Andreas Wundsam001b1822013-08-02 22:25:55 -0700283 return True
Andreas Wundsam5204de22013-07-30 11:34:45 -0700284
Andreas Wundsam83aebd32013-11-19 10:43:50 -0800285 @property
286 @memoize
287 def oxm_map(self):
288 OxmMapEntry = namedtuple("OxmMapEntry", ["type_name", "value", "masked" ])
289 return OrderedDict( (oxm.name, OxmMapEntry(type_name=oxm.member_by_name("value").java_type.public_type,
290 value=re.sub(r'^of_oxm_', r'', re.sub(r'_masked$', r'', oxm.ir_class.name)).upper(),
291 masked=oxm.ir_class.name.endswith("_masked")))
292 for oxm in self.interfaces if oxm.ir_class.is_subclassof("of_oxm") )
Andreas Wundsam5204de22013-07-30 11:34:45 -0700293
Andreas Wundsamcb17b232013-09-28 19:05:36 -0700294class OFFactory(namedtuple("OFFactory", ("package", "name", "members", "remove_prefix", "base_class", "sub_factories", "xid_generator"))):
Andreas Wundsam5204de22013-07-30 11:34:45 -0700295 @property
296 def factory_classes(self):
297 return [ OFFactoryClass(
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800298 package="org.projectfloodlight.openflow.protocol.ver{}".format(version.dotless_version),
299 name="{}Ver{}".format(self.name, version.dotless_version),
Andreas Wundsam5204de22013-07-30 11:34:45 -0700300 interface=self,
301 version=version
302 ) for version in model.versions ]
303
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700304 def method_name(self, member, builder=True):
305 n = member.variable_name
306 if n.startswith(self.remove_prefix):
307 n = n[len(self.remove_prefix):]
308 n = n[0].lower() + n[1:]
309 if builder:
310 return "build" + n[0].upper() + n[1:]
311 else:
312 return n
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800313
Yotam Harchol595c6442013-09-27 16:29:08 -0700314 def of_version(self, version):
315 for fc in self.factory_classes:
316 if fc.version == version:
317 return fc
318 return None
Andreas Wundsam5204de22013-07-30 11:34:45 -0700319
320OFGenericClass = namedtuple("OFGenericClass", ("package", "name"))
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700321class OFFactoryClass(namedtuple("OFFactoryClass", ("package", "name", "interface", "version"))):
322 @property
323 def base_class(self):
324 return self.interface.base_class
325
326 @property
327 def versioned_base_class(self):
328 base_class_interface = model.interface_by_name(self.interface.base_class)
329 if base_class_interface and base_class_interface.has_version(self.version):
330 return base_class_interface.versioned_class(self.version)
331 else:
332 return None
Andreas Wundsam5204de22013-07-30 11:34:45 -0700333
Andreas Wundsam27303462013-07-16 12:52:35 -0700334model = JavaModel()
335
336#######################################################################
337### OFVersion
338#######################################################################
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700339
340class JavaOFVersion(object):
341 """ Models a version of OpenFlow. contains methods to convert the internal
342 Loxi version to a java constant / a string """
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800343 def __init__(self, ir_version):
344 assert isinstance(ir_version, OFVersion)
345 self.ir_version = ir_version
346 self.int_version = self.ir_version.wire_version
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700347
348 @property
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800349 def dotless_version(self):
350 return self.ir_version.version.replace(".", "")
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700351
352 @property
353 def constant_version(self):
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800354 return "OF_" + self.dotless_version
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700355
Andreas Wundsam27303462013-07-16 12:52:35 -0700356 def __repr__(self):
357 return "JavaOFVersion(%d)" % self.int_version
358
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700359 def __str__(self):
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800360 return self.ir_version.version
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700361
Andreas Wundsam27303462013-07-16 12:52:35 -0700362 def __hash__(self):
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800363 return hash(self.ir_version)
Andreas Wundsam27303462013-07-16 12:52:35 -0700364
365 def __eq__(self, other):
366 if other is None or type(self) != type(other):
367 return False
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800368 return (self.ir_version,) == (other.ir_version,)
Andreas Wundsam27303462013-07-16 12:52:35 -0700369
370#######################################################################
371### Interface
372#######################################################################
373
374class JavaOFInterface(object):
375 """ Models an OpenFlow Message class for the purpose of the java class.
376 Version agnostic, in contrast to the loxi_ir python model.
377 """
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800378 def __init__(self, ir_class):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700379 """"
380 @param c_name: loxi style name (e.g., of_flow_add)
381 @param version_map map of { JavaOFVersion: OFClass (from loxi_ir) }
382 """
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800383 self.ir_class = ir_class
384 self.c_name = ir_class.name
385 self.version_map = { JavaOFVersion(v): c for v,c in ir_class.version_classes.items() }
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700386 # name: the Java Type name, e.g., OFFlowAdd
Andreas Wundsam907c3612014-01-15 12:03:04 -0800387 self.name = java_class_name(self.c_name)
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700388 # variable_name name to use for variables of this type. i.e., flowAdd
Andreas Wundsam5204de22013-07-30 11:34:45 -0700389 self.variable_name = self.name[2].lower() + self.name[3:]
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700390 self.title_name = self.variable_name[0].upper() + self.variable_name[1:]
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700391 # name for use in constants: FLOW_ADD
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800392 self.constant_name = self.c_name.upper().replace("OF_", "")
Andreas Wundsam27303462013-07-16 12:52:35 -0700393
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700394 pck_suffix, parent_interface, self.type_annotation = self.class_info()
Yotam Harchol791e4882013-09-05 16:32:56 -0700395 self.package = "org.projectfloodlight.openflow.protocol.%s" % pck_suffix if pck_suffix else "org.projectfloodlight.openflow.protocol"
Andreas Wundsam27303462013-07-16 12:52:35 -0700396 if self.name != parent_interface:
397 self.parent_interface = parent_interface
398 else:
399 self.parent_interface = None
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700400
Andreas Wundsama705c6b2013-09-04 11:21:09 -0700401 @property
402 @memoize
403 def all_parent_interfaces(self):
404 return [ "OFObject" ] + \
405 ([ self.parent_interface ] if self.parent_interface else [] )+ \
406 self.additional_parent_interfaces
407 @property
408 @memoize
409 def additional_parent_interfaces(self):
410 if loxi_utils.class_is_message(self.c_name) and not self.is_virtual:
411 m = re.match(r'(.*)Request$', self.name)
412 if m:
413 reply_name = m.group(1) + "Reply"
414 if model.interface_by_name(reply_name):
415 return ["OFRequest<%s>" % reply_name ]
Andreas Wundsam73d7d672014-11-12 17:07:33 -0800416 elif self.name == "OFBundleCtrlMsg":
417 reply_name = "OFBundleCtrlMsg"
418 return ["OFRequest<%s>" % reply_name ]
Andreas Wundsama705c6b2013-09-04 11:21:09 -0700419 return []
420
421
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700422 def is_instance_of(self, other_class):
423 if self == other_class:
424 return True
425 parent = self.super_class
426 if parent is None:
427 return False
428 else:
429 return parent.is_instance_of(other_class)
430
431 @property
432 def super_class(self):
433 if not self.parent_interface:
434 return None
435 else:
436 return model.interface_by_name(self.parent_interface)
437
438
439 def inherited_declaration(self, type_spec="?"):
440 if self.type_annotation:
441 return "%s<%s>" % (self.name, type_spec)
442 else:
443 return "%s" % self.name
444
445 @property
446 def type_variable(self):
447 if self.type_annotation:
448 return "<T>"
449 else:
450 return "";
451
Andreas Wundsam27303462013-07-16 12:52:35 -0700452 def class_info(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700453 """ return tuple of (package_prefix, parent_class) for the current JavaOFInterface"""
Andreas Wundsam907c3612014-01-15 12:03:04 -0800454 # FIXME: This code could be cleaned up further. Maybe some of the exceptions
455 # here could be folded into ir, or the type arithmetic specified in a more general
456 # fashion
457 def calc_package(i):
458 if i.is_subclassof("of_error_msg"):
459 return "errormsg"
460 elif i.is_instanceof("of_action"):
461 return "action"
462 elif i.is_instanceof("of_action_id"):
463 return "actionid"
464 elif i.is_instanceof("of_instruction"):
465 return "instruction"
466 elif i.is_instanceof("of_instruction_id"):
467 return "instructionid"
468 elif i.is_instanceof("of_oxm"):
469 return "oxm"
470 elif i.is_instanceof("of_meter_band"):
471 return "meterband"
472 elif i.is_instanceof("of_queue_prop"):
473 return "queueprop"
474 elif i.is_instanceof("of_bsn_tlv"):
475 return "bsntlv"
476 else:
477 return ""
478
479 def calc_super_name(i):
480 if re.match('of_match_.*', i.name):
481 return "Match"
482 else:
483 ir_super_class = self.ir_class.superclass
484 return java_class_name(ir_super_class.name) if ir_super_class else ""
485
486 package = calc_package(self.ir_class)
487 super_name = calc_super_name(self.ir_class)
488
489 if self.name == "OFStatsRequest":
490 # stats_requests are special because of their type annotation
491 return (package, "OFMessage", "T extends OFStatsReply")
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800492 elif self.ir_class.is_subclassof('of_stats_request'):
Andreas Wundsam907c3612014-01-15 12:03:04 -0800493 # stats_request subclasses are special because of their type annotation
494 reply_name = re.sub(r'Request$', 'Reply', self.name)
495 super_type_annotation = "T" if self.ir_class.virtual else reply_name
496
497 type_annotation = "T extends {}".format(reply_name) if self.ir_class.virtual \
498 else ""
499
500 return (package, "{}<{}>".format(super_name, super_type_annotation),
501 type_annotation)
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700502 elif self.name == "OFOxm":
Andreas Wundsam907c3612014-01-15 12:03:04 -0800503 return (package, None, "T extends OFValueType<T>")
Andreas Wundsam5204de22013-07-30 11:34:45 -0700504 elif loxi_utils.class_is_oxm(self.c_name):
Andreas Wundsam907c3612014-01-15 12:03:04 -0800505 # look up type from member value for OFValueType type annotation
Andreas Wundsam83aebd32013-11-19 10:43:50 -0800506 if self.member_by_name("value") is not None:
Andreas Wundsam907c3612014-01-15 12:03:04 -0800507 return (package, "OFOxm<%s>" % self.member_by_name("value").java_type.public_type, None)
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700508 else:
Andreas Wundsam907c3612014-01-15 12:03:04 -0800509 return (package, "OFOxm", None)
Andreas Wundsam27303462013-07-16 12:52:35 -0700510 else:
Andreas Wundsam907c3612014-01-15 12:03:04 -0800511 return (package, super_name, None)
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700512
513 @property
514 @memoize
515 def writeable_members(self):
516 return [ m for m in self.members if m.is_writeable ]
Andreas Wundsam27303462013-07-16 12:52:35 -0700517
Andreas Wundsamcb17b232013-09-28 19:05:36 -0700518 @memoize
519 def member_by_name(self, name):
520 return find(lambda m: m.name == name, self.members)
521
Andreas Wundsam27303462013-07-16 12:52:35 -0700522 @property
523 @memoize
524 def members(self):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700525 return self.ir_model_members + self.virtual_members
526
527 @property
528 @memoize
529 def ir_model_members(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700530 """return a list of all members to be exposed by this interface. Corresponds to
531 the union of the members of the vesioned classes without length, fieldlength
532 and pads (those are handled automatically during (de)serialization and not exposed"""
Andreas Wundsam27303462013-07-16 12:52:35 -0700533 all_versions = []
534 member_map = collections.OrderedDict()
535
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800536 member_version_map = {}
Andreas Wundsam27303462013-07-16 12:52:35 -0700537 for (version, of_class) in self.version_map.items():
538 for of_member in of_class.members:
539 if isinstance(of_member, OFLengthMember) or \
540 isinstance(of_member, OFFieldLengthMember) or \
541 isinstance(of_member, OFPadMember):
542 continue
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800543 java_member = JavaMember.for_of_member(self, of_member)
Andreas Wundsam27303462013-07-16 12:52:35 -0700544 if of_member.name not in member_map:
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800545 member_map[of_member.name] = java_member
546 member_version_map[of_member.name] = version
547 else:
548 existing = member_map[of_member.name]
549
550 if existing.java_type.public_type != java_member.java_type.public_type:
551 raise Exception(
552 "Error constructing interface {}: type signatures do not match up between versions.\n"
553 " Member Name: {}\n"
554 " Existing: Version={}, Java={}, IR={}\n"
555 " New: Version={}, Java={}, IR={}"
556 .format(self.name, existing.name,
557 member_version_map[of_member.name], existing.java_type.public_type, existing.member.oftype,
558 version, java_member.java_type.public_type, java_member.member.oftype)
559 )
Andreas Wundsam27303462013-07-16 12:52:35 -0700560
Andreas Wundsamb1da3f62013-09-04 18:46:03 -0700561 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 -0700562
563 @property
564 def virtual_members(self):
Andreas Wundsama0981022013-10-02 18:15:06 -0700565 virtual_members = []
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700566 if self.name == "OFOxm":
Andreas Wundsama0981022013-10-02 18:15:06 -0700567 virtual_members += [
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700568 JavaVirtualMember(self, "value", java_type.generic_t),
569 JavaVirtualMember(self, "mask", java_type.generic_t),
570 JavaVirtualMember(self, "matchField", java_type.make_match_field_jtype("T")),
571 JavaVirtualMember(self, "masked", java_type.boolean),
Andreas Wundsam661a2222013-11-05 17:18:59 -0800572 JavaVirtualMember(self, "canonical", java_type.make_oxm_jtype("T"))
Andreas Wundsama0981022013-10-02 18:15:06 -0700573 ]
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800574 elif self.ir_class.is_subclassof("of_oxm"):
Andreas Wundsam83aebd32013-11-19 10:43:50 -0800575 value = find(lambda m: m.name=="value", self.ir_model_members)
576 if value:
577 field_type = java_type.make_match_field_jtype(value.java_type.public_type)
578 else:
579 field_type = java_type.make_match_field_jtype()
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700580
Andreas Wundsama0981022013-10-02 18:15:06 -0700581 virtual_members += [
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700582 JavaVirtualMember(self, "matchField", field_type),
583 JavaVirtualMember(self, "masked", java_type.boolean),
Andreas Wundsam83aebd32013-11-19 10:43:50 -0800584 JavaVirtualMember(self, "canonical", java_type.make_oxm_jtype(value.java_type.public_type),
Andreas Wundsam661a2222013-11-05 17:18:59 -0800585 custom_template=lambda builder: "OFOxm{}_getCanonical.java".format(".Builder" if builder else "")),
Andreas Wundsama0981022013-10-02 18:15:06 -0700586 ]
587 if not find(lambda x: x.name == "mask", self.ir_model_members):
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800588 virtual_members.append(
589 JavaVirtualMember(self, "mask", find(lambda x: x.name == "value", self.ir_model_members).java_type))
Andreas Wundsam4d0752b2014-12-01 13:14:07 -0800590 elif self.name =="OFErrorMsg":
591 virtual_members += [ JavaVirtualMember(self, "data", java_type.error_cause_data) ]
Andreas Wundsama0981022013-10-02 18:15:06 -0700592
593 if not find(lambda m: m.name == "version", self.ir_model_members):
594 virtual_members.append(JavaVirtualMember(self, "version", java_type.of_version))
595
596 return tuple(virtual_members)
Andreas Wundsam27303462013-07-16 12:52:35 -0700597
598 @property
599 @memoize
600 def is_virtual(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700601 """ Is this interface virtual. If so, do not generate a builder interface """
Andreas Wundsam001b1822013-08-02 22:25:55 -0700602 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 -0700603
604 @property
Andreas Wundsam5204de22013-07-30 11:34:45 -0700605 def is_universal(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700606 """ Is this interface universal, i.e., does it exist in all OF versions? """
Andreas Wundsam5204de22013-07-30 11:34:45 -0700607 return len(self.all_versions) == len(model.versions)
608
609 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700610 @memoize
611 def all_versions(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700612 """ return list of all versions that this interface exists in """
Andreas Wundsam27303462013-07-16 12:52:35 -0700613 return self.version_map.keys()
614
Andreas Wundsam5204de22013-07-30 11:34:45 -0700615 def has_version(self, version):
616 return version in self.version_map
617
Andreas Wundsam27303462013-07-16 12:52:35 -0700618 def versioned_class(self, version):
619 return JavaOFClass(self, version, self.version_map[version])
620
621 @property
622 @memoize
623 def versioned_classes(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700624 return [ self.versioned_class(version) for version in self.all_versions ]
625
626#######################################################################
627### (Versioned) Classes
628#######################################################################
629
630class JavaOFClass(object):
631 """ Models an OpenFlow Message class for the purpose of the java class.
Andreas Wundsamd40ddbf2013-07-25 15:40:47 -0700632 Version specific child of a JavaOFInterface
Andreas Wundsam27303462013-07-16 12:52:35 -0700633 """
634 def __init__(self, interface, version, ir_class):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700635 """
636 @param interface JavaOFInterface instance of the parent interface
637 @param version JavaOFVersion
638 @param ir_class OFClass from loxi_ir
639 """
Andreas Wundsam27303462013-07-16 12:52:35 -0700640 self.interface = interface
641 self.ir_class = ir_class
642 self.c_name = self.ir_class.name
643 self.version = version
644 self.constant_name = self.c_name.upper().replace("OF_", "")
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800645 self.package = "org.projectfloodlight.openflow.protocol.ver%s" % version.dotless_version
Andreas Wundsame916d6f2013-07-30 11:33:58 -0700646 self.generated = False
647
648 @property
649 @memoize
650 def unit_test(self):
Yotam Harchol466b3212013-08-15 12:14:46 -0700651 return JavaUnitTestSet(self)
Andreas Wundsam27303462013-07-16 12:52:35 -0700652
653 @property
654 def name(self):
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800655 return "%sVer%s" % (self.interface.name, self.version.dotless_version)
Andreas Wundsam27303462013-07-16 12:52:35 -0700656
657 @property
Andreas Wundsam5204de22013-07-30 11:34:45 -0700658 def variable_name(self):
Andreas Wundsam781d99f2013-10-23 15:25:56 -0700659 return self.name[2].lower() + self.name[3:]
Andreas Wundsam5204de22013-07-30 11:34:45 -0700660
661 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700662 def length(self):
663 if self.is_fixed_length:
664 return self.min_length
665 else:
666 raise Exception("No fixed length for class %s, version %s" % (self.name, self.version))
667
668 @property
669 def min_length(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700670 """ @return the minimum wire length of an instance of this class in bytes """
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800671 return self.ir_class.base_length
Andreas Wundsam27303462013-07-16 12:52:35 -0700672
673 @property
674 def is_fixed_length(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700675 """ true iff this class serializes to a fixed length on the wire """
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800676 return self.ir_class.is_fixed_length and not self.is_virtual
Andreas Wundsam27303462013-07-16 12:52:35 -0700677
678 def all_properties(self):
679 return self.interface.members
680
Andreas Wundsam27303462013-07-16 12:52:35 -0700681 @property
682 @memoize
683 def data_members(self):
684 return [ prop for prop in self.members if prop.is_data ]
685
686 @property
687 @memoize
688 def fixed_value_members(self):
689 return [ prop for prop in self.members if prop.is_fixed_value ]
690
691 @property
692 @memoize
693 def public_members(self):
694 return [ prop for prop in self.members if prop.is_public ]
695
696 @property
697 @memoize
698 def members(self):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700699 return self.ir_model_members + self.virtual_members
700
701 @property
Andreas Wundsama0981022013-10-02 18:15:06 -0700702 @memoize
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700703 def ir_model_members(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700704 members = [ JavaMember.for_of_member(self, of_member) for of_member in self.ir_class.members ]
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700705 return tuple(members)
706
707 @property
708 def virtual_members(self):
Andreas Wundsama0981022013-10-02 18:15:06 -0700709 virtual_members = []
Andreas Wundsam83aebd32013-11-19 10:43:50 -0800710 if self.ir_class.is_subclassof("of_oxm"):
711 value_member = find(lambda m: m.name, self.ir_model_members)
712 if value_member:
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700713 oxm_entry = model.oxm_map[self.interface.name]
Andreas Wundsama0981022013-10-02 18:15:06 -0700714 virtual_members += [
Andreas Wundsam83aebd32013-11-19 10:43:50 -0800715 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 -0700716 JavaVirtualMember(self, "masked", java_type.boolean, "true" if oxm_entry.masked else "false"),
Andreas Wundsam661a2222013-11-05 17:18:59 -0800717 ]
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700718 else:
Andreas Wundsama0981022013-10-02 18:15:06 -0700719 virtual_members += [
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700720 JavaVirtualMember(self, "matchField", java_type.make_match_field_jtype(), "null"),
721 JavaVirtualMember(self, "masked", java_type.boolean, "false"),
Andreas Wundsam661a2222013-11-05 17:18:59 -0800722 ]
Andreas Wundsama0981022013-10-02 18:15:06 -0700723 if not find(lambda m: m.name == "version", self.ir_model_members):
724 virtual_members.append(JavaVirtualMember(self, "version", java_type.of_version, "OFVersion.%s" % self.version.constant_version))
725
726 return tuple(virtual_members)
Andreas Wundsam27303462013-07-16 12:52:35 -0700727
Andreas Wundsam661a2222013-11-05 17:18:59 -0800728 @memoize
729 def member_by_name(self, name):
730 return find(lambda m: m.name == name, self.members)
731
Andreas Wundsam27303462013-07-16 12:52:35 -0700732 def all_versions(self):
733 return [ JavaOFVersion(int_version)
734 for int_version in of_g.unified[self.c_name]
735 if int_version != 'union' and int_version != 'object_id' ]
736
737 def version_is_inherited(self, version):
738 return 'use_version' in of_g.unified[self.ir_class.name][version.int_version]
739
740 def inherited_from(self, version):
741 return JavaOFVersion(of_g.unified[self.ir_class.name][version.int_version]['use_version'])
742
743 @property
744 def is_virtual(self):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700745 return self.ir_class.virtual # type_maps.class_is_virtual(self.c_name) or self.ir_class.virtual
746
747 @property
748 def discriminator(self):
749 return find(lambda m: isinstance(m, OFDiscriminatorMember), self.ir_class.members)
Andreas Wundsam27303462013-07-16 12:52:35 -0700750
751 @property
752 def is_extension(self):
753 return type_maps.message_is_extension(self.c_name, -1)
754
Andreas Wundsam758c9cc2013-08-01 22:16:06 -0700755 @property
756 def align(self):
757 return int(self.ir_class.params['align']) if 'align' in self.ir_class.params else 0
758
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700759 @property
Andreas Wundsam5da68512013-10-22 22:18:00 -0700760 def length_includes_align(self):
761 return self.ir_class.params['length_includes_align'] == "True" if 'length_includes_align' in self.ir_class.params else False
762
763 @property
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700764 @memoize
765 def superclass(self):
766 return find(lambda c: c.version == self.version and c.c_name == self.ir_class.superclass, model.all_classes)
767
768 @property
769 @memoize
770 def subclasses(self):
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800771 return [ c for c in model.all_classes if c.version == self.version and c.ir_class.superclass
772 and c.ir_class.superclass.name == self.c_name ]
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700773
Andreas Wundsam27303462013-07-16 12:52:35 -0700774#######################################################################
775### Member
776#######################################################################
777
778
779class JavaMember(object):
780 """ Models a property (member) of an openflow class. """
781 def __init__(self, msg, name, java_type, member):
782 self.msg = msg
783 self.name = name
784 self.java_type = java_type
785 self.member = member
786 self.c_name = self.member.name if(hasattr(self.member, "name")) else ""
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700787
788 @property
789 def title_name(self):
790 return self.name[0].upper() + self.name[1:]
791
792 @property
793 def constant_name(self):
794 return self.c_name.upper()
795
796 @property
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700797 def getter_name(self):
798 return ("is" if self.java_type.public_type == "boolean" else "get") + self.title_name
799
800 @property
801 def setter_name(self):
802 return "set" + self.title_name
803
804 @property
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700805 def default_name(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700806 if self.is_fixed_value:
807 return self.constant_name
808 else:
809 return "DEFAULT_"+self.constant_name
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700810
811 @property
812 def default_value(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700813 if self.is_fixed_value:
814 return self.enum_value
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700815 else:
Andreas Wundsam142dd2a2013-09-23 14:47:37 -0700816 default = self.java_type.default_op(self.msg.version)
817 if default == "null" and not self.is_nullable:
818 return None
819 else:
820 return default
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700821
822 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700823 def enum_value(self):
824 if self.name == "version":
825 return "OFVersion.%s" % self.msg.version.constant_version
826
827 java_type = self.java_type.public_type;
828 try:
829 global model
830 enum = model.enum_by_name(java_type)
831 entry = enum.entry_by_version_value(self.msg.version, self.value)
832 return "%s.%s" % ( enum.name, entry.name)
833 except KeyError, e:
Andreas Wundsam9f8657a2014-10-10 21:28:17 -0700834 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 -0700835 return self.value
836
837 @property
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700838 def is_pad(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700839 return isinstance(self.member, OFPadMember)
840
841 def is_type_value(self, version=None):
842 if(version==None):
843 return any(self.is_type_value(version) for version in self.msg.all_versions)
844 try:
845 return self.c_name in get_type_values(self.msg.c_name, version.int_version)
846 except:
847 return False
848
849 @property
850 def is_field_length_value(self):
851 return isinstance(self.member, OFFieldLengthMember)
852
853 @property
Andreas Wundsam001b1822013-08-02 22:25:55 -0700854 def is_discriminator(self):
855 return isinstance(self.member, OFDiscriminatorMember)
856
857 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700858 def is_length_value(self):
859 return isinstance(self.member, OFLengthMember)
860
861 @property
862 def is_public(self):
863 return not (self.is_pad or self.is_length_value)
864
865 @property
866 def is_data(self):
867 return isinstance(self.member, OFDataMember) and self.name != "version"
868
869 @property
870 def is_fixed_value(self):
871 return hasattr(self.member, "value") or self.name == "version" \
872 or ( self.name == "length" and self.msg.is_fixed_length) \
873 or ( self.name == "len" and self.msg.is_fixed_length)
874
875 @property
876 def value(self):
877 if self.name == "version":
878 return self.msg.version.int_version
879 elif self.name == "length" or self.name == "len":
880 return self.msg.length
Andreas Wundsam27303462013-07-16 12:52:35 -0700881 else:
Andreas Wundsam001b1822013-08-02 22:25:55 -0700882 return self.java_type.format_value(self.member.value)
883
884 @property
885 def priv_value(self):
886 if self.name == "version":
Andreas Wundsam22ba3af2013-10-04 16:00:30 -0700887 return self.java_type.format_value(self.msg.version.int_version, pub_type=False)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700888 elif self.name == "length" or self.name == "len":
Andreas Wundsam22ba3af2013-10-04 16:00:30 -0700889 return self.java_type.format_value(self.msg.length, pub_type=False)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700890 else:
891 return self.java_type.format_value(self.member.value, pub_type=False)
892
Andreas Wundsam27303462013-07-16 12:52:35 -0700893
894 @property
895 def is_writeable(self):
896 return self.is_data and not self.name in model.write_blacklist[self.msg.name]
897
898 def get_type_value_info(self, version):
899 return get_type_values(msg.c_name, version.int_version)[self.c_name]
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700900
901 @property
902 def length(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700903 if hasattr(self.member, "length"):
904 return self.member.length
905 else:
Andreas Wundsam5204de22013-07-30 11:34:45 -0700906 count, base = loxi_utils.type_dec_to_count_base(self.member.type)
Andreas Wundsam27303462013-07-16 12:52:35 -0700907 return of_g.of_base_types[base]['bytes'] * count
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700908
909 @staticmethod
Andreas Wundsam27303462013-07-16 12:52:35 -0700910 def for_of_member(java_class, member):
911 if isinstance(member, OFPadMember):
912 return JavaMember(None, "", None, member)
913 else:
914 if member.name == 'len':
915 name = 'length'
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700916 elif member.name == 'value_mask':
917 name = 'mask'
Andreas Wundsamacd57d52013-10-18 17:35:01 -0700918 elif member.name == 'group_id':
919 name = 'group'
Andreas Wundsam27303462013-07-16 12:52:35 -0700920 else:
921 name = java_type.name_c_to_camel(member.name)
922 j_type = java_type.convert_to_jtype(java_class.c_name, member.name, member.oftype)
923 return JavaMember(java_class, name, j_type, member)
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700924
925 @property
926 def is_universal(self):
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800927 for version, ir_class in self.msg.ir_class.version_classes.items():
928 if not ir_class.member_by_name(self.member.name):
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700929 return False
930 return True
931
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700932 @property
933 def is_virtual(self):
934 return False
935
Andreas Wundsam27303462013-07-16 12:52:35 -0700936 def __hash__(self):
937 return hash(self.name)
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700938
Andreas Wundsam27303462013-07-16 12:52:35 -0700939 def __eq__(self, other):
940 if other is None or type(self) != type(other):
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700941 return False
Andreas Wundsam27303462013-07-16 12:52:35 -0700942 return (self.name,) == (other.name,)
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700943
Andreas Wundsamf1949682013-09-23 14:48:31 -0700944 @property
945 def is_nullable(self):
946 return self.name in model.nullable_map[self.msg.name]
947
948
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700949class JavaVirtualMember(JavaMember):
950 """ 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 -0800951 def __init__(self, msg, name, java_type, value=None, custom_template=None):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700952 JavaMember.__init__(self, msg, name, java_type, member=None)
953 self._value = value
Andreas Wundsam4b8661f2013-11-05 17:17:28 -0800954 self.custom_template = custom_template
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700955
956 @property
957 def is_fixed_value(self):
958 return True
959
960 @property
961 def value(self):
962 return self._value
963
964 @property
965 def priv_value(self):
966 return self._value
967
968
969 @property
970 def is_universal(self):
971 return True
972
973 @property
974 def is_virtual(self):
975 return True
Andreas Wundsame916d6f2013-07-30 11:33:58 -0700976
977#######################################################################
978### Unit Test
979#######################################################################
980
Yotam Harchol466b3212013-08-15 12:14:46 -0700981class JavaUnitTestSet(object):
Andreas Wundsame916d6f2013-07-30 11:33:58 -0700982 def __init__(self, java_class):
983 self.java_class = java_class
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800984 first_data_file_name = "of{version}/{name}.data".format(version=java_class.version.dotless_version,
Andreas Wundsame916d6f2013-07-30 11:33:58 -0700985 name=java_class.c_name[3:])
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800986 glob_file_name = "of{version}/{name}__*.data".format(version=java_class.version.dotless_version,
Andreas Wundsamaa4f3cd2013-10-22 22:19:08 -0700987 name=java_class.c_name[3:])
Yotam Harchol466b3212013-08-15 12:14:46 -0700988 test_class_name = self.java_class.name + "Test"
989 self.test_units = []
990 if test_data.exists(first_data_file_name):
991 self.test_units.append(JavaUnitTest(java_class, first_data_file_name, test_class_name))
Andreas Wundsamaa4f3cd2013-10-22 22:19:08 -0700992
Yotam Harchol466b3212013-08-15 12:14:46 -0700993 i = 1
Andreas Wundsamaa4f3cd2013-10-22 22:19:08 -0700994 for f in test_data.glob(glob_file_name):
995 m = re.match(".*__(.*).data", f)
996 if m:
997 suffix = java_type.name_c_to_caps_camel(m.group(1))
998 else:
999 suffix = str(i)
1000 i += 1
1001 test_class_name = self.java_class.name + suffix + "Test"
1002 self.test_units.append(JavaUnitTest(java_class, f, test_class_name))
Andreas Wundsamf1949682013-09-23 14:48:31 -07001003
Yotam Harchol466b3212013-08-15 12:14:46 -07001004 @property
1005 def package(self):
1006 return self.java_class.package
1007
1008 @property
1009 def has_test_data(self):
1010 return len(self.test_units) > 0
1011
1012 @property
1013 def length(self):
1014 return len(self.test_units)
Andreas Wundsamf1949682013-09-23 14:48:31 -07001015
Yotam Harchol466b3212013-08-15 12:14:46 -07001016 def get_test_unit(self, i):
1017 return self.test_units[i]
1018
1019
1020class JavaUnitTest(object):
1021 def __init__(self, java_class, file_name=None, test_class_name=None):
1022 self.java_class = java_class
1023 if file_name is None:
Andreas Wundsamc8912c12013-11-15 13:44:48 -08001024 self.data_file_name = "of{version}/{name}.data".format(version=java_class.version.dotless_version,
Yotam Harchol466b3212013-08-15 12:14:46 -07001025 name=java_class.c_name[3:])
1026 else:
1027 self.data_file_name = file_name
1028 if test_class_name is None:
1029 self.test_class_name = self.java_class.name + "Test"
1030 else:
1031 self.test_class_name = test_class_name
Andreas Wundsamf1949682013-09-23 14:48:31 -07001032
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001033 @property
1034 def package(self):
1035 return self.java_class.package
1036
1037 @property
1038 def name(self):
Yotam Harchol466b3212013-08-15 12:14:46 -07001039 return self.test_class_name
Yotam Harchol595c6442013-09-27 16:29:08 -07001040
1041 @property
1042 def interface(self):
1043 return self.java_class.interface
1044
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001045 @property
1046 def has_test_data(self):
1047 return test_data.exists(self.data_file_name)
1048
1049 @property
1050 @memoize
1051 def test_data(self):
1052 return test_data.read(self.data_file_name)
1053
1054
Andreas Wundsam27303462013-07-16 12:52:35 -07001055#######################################################################
1056### Enums
1057#######################################################################
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001058
Andreas Wundsam27303462013-07-16 12:52:35 -07001059class JavaEnum(object):
Andreas Wundsamf299baa2014-10-23 20:15:32 -07001060 def __init__(self, c_name, stable, version_enum_map):
Andreas Wundsam27303462013-07-16 12:52:35 -07001061 self.c_name = c_name
Andreas Wundsamf299baa2014-10-23 20:15:32 -07001062 self.stable = stable
Andreas Wundsambe168f72013-08-03 22:49:35 -07001063
Rob Vaterlausfeee3712013-09-30 11:24:19 -07001064 self.name = "OF" + java_type.name_c_to_caps_camel("_".join(c_name.split("_")[1:]))
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001065
Andreas Wundsam27303462013-07-16 12:52:35 -07001066 # Port_features has constants that start with digits
1067 self.name_prefix = "PF_" if self.name == "OFPortFeatures" else ""
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001068
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001069 self.version_enums = version_enum_map
1070
1071 entry_name_version_value_map = OrderedDefaultDict(lambda: OrderedDict())
1072 for version, ir_enum in version_enum_map.items():
1073 for ir_entry in ir_enum.entries:
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001074 entry_name_version_value_map[ir_entry.name][version] = ir_entry.value
1075
Andreas Wundsam27303462013-07-16 12:52:35 -07001076 self.entries = [ JavaEnumEntry(self, name, version_value_map)
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001077 for (name, version_value_map) in entry_name_version_value_map.items() ]
Andreas Wundsam43526532013-08-01 22:03:50 -07001078
1079 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 -07001080 self.package = "org.projectfloodlight.openflow.protocol"
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001081
Andreas Wundsamf299baa2014-10-23 20:15:32 -07001082 static_metadata = model.enum_metadata_map[self.name]
1083 if self.stable:
1084 # need this to look up wire_type, which does not matter
1085 any_version = version_enum_map.keys()[0]
1086 # if this is a 'stable' enum, i.e., its value won't change, add
1087 # a "Metadata" (virtual) field "StableValue" to it that returns
1088 # its wirevalue.
1089 stable_value = JavaModel.OFEnumPropertyMetadata("StableValue",
1090 self.wire_type(any_version),
1091 value = lambda entry: entry.stable_value)
1092
1093 self.metadata = JavaModel.OFEnumMetadata(
1094 properties=static_metadata.properties + (stable_value, ),
1095 to_string=static_metadata.to_string
1096 )
1097 else:
1098 self.metadata = static_metadata
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -07001099
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001100 def wire_type(self, version):
1101 ir_enum = self.version_enums[version]
1102 if "wire_type" in ir_enum.params:
1103 return java_type.convert_enum_wire_type_to_jtype(ir_enum.params["wire_type"])
1104 else:
1105 return java_type.u8
1106
1107 @property
Andreas Wundsam7cfeac32013-09-17 13:53:48 -07001108 @memoize
1109 def is_bitmask(self):
1110 return any(ir_enum.is_bitmask for ir_enum in self.version_enums.values())
1111
1112 @property
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001113 def versions(self):
1114 return self.version_enums.keys()
1115
Andreas Wundsam27303462013-07-16 12:52:35 -07001116 @memoize
1117 def entry_by_name(self, name):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -07001118 res = find(lambda e: e.name == name, self.entries)
1119 if res:
1120 return res
1121 else:
Andreas Wundsam27303462013-07-16 12:52:35 -07001122 raise KeyError("Enum %s: no entry with name %s" % (self.name, name))
1123
1124 @memoize
1125 def entry_by_c_name(self, name):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -07001126 res = find(lambda e: e.c_name == name, self.entries)
1127 if res:
1128 return res
1129 else:
Andreas Wundsam27303462013-07-16 12:52:35 -07001130 raise KeyError("Enum %s: no entry with c_name %s" % (self.name, name))
1131
1132 @memoize
1133 def entry_by_version_value(self, version, value):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -07001134 res = find(lambda e: e.values[version] == value if version in e.values else False, self.entries)
1135 if res:
1136 return res
1137 else:
Andreas Wundsam27303462013-07-16 12:52:35 -07001138 raise KeyError("Enum %s: no entry with version %s, value %s" % (self.name, version, value))
1139
1140# values: Map JavaVersion->Value
1141class JavaEnumEntry(object):
1142 def __init__(self, enum, name, values):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001143 self.enum = enum
Andreas Wundsam27303462013-07-16 12:52:35 -07001144 self.name = enum.name_prefix + "_".join(name.split("_")[1:]).upper()
1145 self.values = values
1146
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -07001147 @property
1148 def constructor_params(self):
Andreas Wundsamf299baa2014-10-23 20:15:32 -07001149 return [ (m.type, m.value(self)) for m in self.enum.metadata.properties ]
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -07001150
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001151 def has_value(self, version):
1152 return version in self.values
1153
Andreas Wundsam27303462013-07-16 12:52:35 -07001154 def value(self, version):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001155 return self.values[version]
1156
1157 def format_value(self, version):
1158 res = self.enum.wire_type(version).format_value(self.values[version])
Andreas Wundsam27303462013-07-16 12:52:35 -07001159 return res
1160
Andreas Wundsam27303462013-07-16 12:52:35 -07001161 def all_values(self, versions, not_present=None):
1162 return [ self.values[version] if version in self.values else not_present for version in versions ]
Andreas Wundsamc1acfcc2013-09-20 13:10:20 -07001163
1164 @property
Andreas Wundsamf299baa2014-10-23 20:15:32 -07001165 def stable_value(self):
1166 if self.enum.stable:
1167 return self.values.values()[0]
1168 else:
1169 raise Exception("Enum {} not stable".format(self.enum.name))
1170
1171 @property
Andreas Wundsamc1acfcc2013-09-20 13:10:20 -07001172 @memoize
1173 def masked_enum_group(self):
1174 group = find(lambda g: self.name in g.members, model.masked_enum_groups[self.enum.name])
1175 return group
1176
1177 @property
1178 @memoize
1179 def is_mask(self):
1180 return any(self.name == g.mask for g in model.masked_enum_groups[self.enum.name])