blob: 79cea7a45f79ae250db1ebd68322ddc20d934ae7 [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 Wundsama0981022013-10-02 18:15:06 -0700590
591 if not find(lambda m: m.name == "version", self.ir_model_members):
592 virtual_members.append(JavaVirtualMember(self, "version", java_type.of_version))
593
594 return tuple(virtual_members)
Andreas Wundsam27303462013-07-16 12:52:35 -0700595
596 @property
597 @memoize
598 def is_virtual(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700599 """ Is this interface virtual. If so, do not generate a builder interface """
Andreas Wundsam001b1822013-08-02 22:25:55 -0700600 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 -0700601
602 @property
Andreas Wundsam5204de22013-07-30 11:34:45 -0700603 def is_universal(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700604 """ Is this interface universal, i.e., does it exist in all OF versions? """
Andreas Wundsam5204de22013-07-30 11:34:45 -0700605 return len(self.all_versions) == len(model.versions)
606
607 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700608 @memoize
609 def all_versions(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700610 """ return list of all versions that this interface exists in """
Andreas Wundsam27303462013-07-16 12:52:35 -0700611 return self.version_map.keys()
612
Andreas Wundsam5204de22013-07-30 11:34:45 -0700613 def has_version(self, version):
614 return version in self.version_map
615
Andreas Wundsam27303462013-07-16 12:52:35 -0700616 def versioned_class(self, version):
617 return JavaOFClass(self, version, self.version_map[version])
618
619 @property
620 @memoize
621 def versioned_classes(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700622 return [ self.versioned_class(version) for version in self.all_versions ]
623
624#######################################################################
625### (Versioned) Classes
626#######################################################################
627
628class JavaOFClass(object):
629 """ Models an OpenFlow Message class for the purpose of the java class.
Andreas Wundsamd40ddbf2013-07-25 15:40:47 -0700630 Version specific child of a JavaOFInterface
Andreas Wundsam27303462013-07-16 12:52:35 -0700631 """
632 def __init__(self, interface, version, ir_class):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700633 """
634 @param interface JavaOFInterface instance of the parent interface
635 @param version JavaOFVersion
636 @param ir_class OFClass from loxi_ir
637 """
Andreas Wundsam27303462013-07-16 12:52:35 -0700638 self.interface = interface
639 self.ir_class = ir_class
640 self.c_name = self.ir_class.name
641 self.version = version
642 self.constant_name = self.c_name.upper().replace("OF_", "")
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800643 self.package = "org.projectfloodlight.openflow.protocol.ver%s" % version.dotless_version
Andreas Wundsame916d6f2013-07-30 11:33:58 -0700644 self.generated = False
645
646 @property
647 @memoize
648 def unit_test(self):
Yotam Harchol466b3212013-08-15 12:14:46 -0700649 return JavaUnitTestSet(self)
Andreas Wundsam27303462013-07-16 12:52:35 -0700650
651 @property
652 def name(self):
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800653 return "%sVer%s" % (self.interface.name, self.version.dotless_version)
Andreas Wundsam27303462013-07-16 12:52:35 -0700654
655 @property
Andreas Wundsam5204de22013-07-30 11:34:45 -0700656 def variable_name(self):
Andreas Wundsam781d99f2013-10-23 15:25:56 -0700657 return self.name[2].lower() + self.name[3:]
Andreas Wundsam5204de22013-07-30 11:34:45 -0700658
659 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700660 def length(self):
661 if self.is_fixed_length:
662 return self.min_length
663 else:
664 raise Exception("No fixed length for class %s, version %s" % (self.name, self.version))
665
666 @property
667 def min_length(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700668 """ @return the minimum wire length of an instance of this class in bytes """
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800669 return self.ir_class.base_length
Andreas Wundsam27303462013-07-16 12:52:35 -0700670
671 @property
672 def is_fixed_length(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700673 """ true iff this class serializes to a fixed length on the wire """
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800674 return self.ir_class.is_fixed_length and not self.is_virtual
Andreas Wundsam27303462013-07-16 12:52:35 -0700675
676 def all_properties(self):
677 return self.interface.members
678
Andreas Wundsam27303462013-07-16 12:52:35 -0700679 @property
680 @memoize
681 def data_members(self):
682 return [ prop for prop in self.members if prop.is_data ]
683
684 @property
685 @memoize
686 def fixed_value_members(self):
687 return [ prop for prop in self.members if prop.is_fixed_value ]
688
689 @property
690 @memoize
691 def public_members(self):
692 return [ prop for prop in self.members if prop.is_public ]
693
694 @property
695 @memoize
696 def members(self):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700697 return self.ir_model_members + self.virtual_members
698
699 @property
Andreas Wundsama0981022013-10-02 18:15:06 -0700700 @memoize
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700701 def ir_model_members(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700702 members = [ JavaMember.for_of_member(self, of_member) for of_member in self.ir_class.members ]
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700703 return tuple(members)
704
705 @property
706 def virtual_members(self):
Andreas Wundsama0981022013-10-02 18:15:06 -0700707 virtual_members = []
Andreas Wundsam83aebd32013-11-19 10:43:50 -0800708 if self.ir_class.is_subclassof("of_oxm"):
709 value_member = find(lambda m: m.name, self.ir_model_members)
710 if value_member:
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700711 oxm_entry = model.oxm_map[self.interface.name]
Andreas Wundsama0981022013-10-02 18:15:06 -0700712 virtual_members += [
Andreas Wundsam83aebd32013-11-19 10:43:50 -0800713 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 -0700714 JavaVirtualMember(self, "masked", java_type.boolean, "true" if oxm_entry.masked else "false"),
Andreas Wundsam661a2222013-11-05 17:18:59 -0800715 ]
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700716 else:
Andreas Wundsama0981022013-10-02 18:15:06 -0700717 virtual_members += [
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700718 JavaVirtualMember(self, "matchField", java_type.make_match_field_jtype(), "null"),
719 JavaVirtualMember(self, "masked", java_type.boolean, "false"),
Andreas Wundsam661a2222013-11-05 17:18:59 -0800720 ]
Andreas Wundsama0981022013-10-02 18:15:06 -0700721 if not find(lambda m: m.name == "version", self.ir_model_members):
722 virtual_members.append(JavaVirtualMember(self, "version", java_type.of_version, "OFVersion.%s" % self.version.constant_version))
723
724 return tuple(virtual_members)
Andreas Wundsam27303462013-07-16 12:52:35 -0700725
Andreas Wundsam661a2222013-11-05 17:18:59 -0800726 @memoize
727 def member_by_name(self, name):
728 return find(lambda m: m.name == name, self.members)
729
Andreas Wundsam27303462013-07-16 12:52:35 -0700730 def all_versions(self):
731 return [ JavaOFVersion(int_version)
732 for int_version in of_g.unified[self.c_name]
733 if int_version != 'union' and int_version != 'object_id' ]
734
735 def version_is_inherited(self, version):
736 return 'use_version' in of_g.unified[self.ir_class.name][version.int_version]
737
738 def inherited_from(self, version):
739 return JavaOFVersion(of_g.unified[self.ir_class.name][version.int_version]['use_version'])
740
741 @property
742 def is_virtual(self):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700743 return self.ir_class.virtual # type_maps.class_is_virtual(self.c_name) or self.ir_class.virtual
744
745 @property
746 def discriminator(self):
747 return find(lambda m: isinstance(m, OFDiscriminatorMember), self.ir_class.members)
Andreas Wundsam27303462013-07-16 12:52:35 -0700748
749 @property
750 def is_extension(self):
751 return type_maps.message_is_extension(self.c_name, -1)
752
Andreas Wundsam758c9cc2013-08-01 22:16:06 -0700753 @property
754 def align(self):
755 return int(self.ir_class.params['align']) if 'align' in self.ir_class.params else 0
756
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700757 @property
Andreas Wundsam5da68512013-10-22 22:18:00 -0700758 def length_includes_align(self):
759 return self.ir_class.params['length_includes_align'] == "True" if 'length_includes_align' in self.ir_class.params else False
760
761 @property
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700762 @memoize
763 def superclass(self):
764 return find(lambda c: c.version == self.version and c.c_name == self.ir_class.superclass, model.all_classes)
765
766 @property
767 @memoize
768 def subclasses(self):
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800769 return [ c for c in model.all_classes if c.version == self.version and c.ir_class.superclass
770 and c.ir_class.superclass.name == self.c_name ]
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700771
Andreas Wundsam27303462013-07-16 12:52:35 -0700772#######################################################################
773### Member
774#######################################################################
775
776
777class JavaMember(object):
778 """ Models a property (member) of an openflow class. """
779 def __init__(self, msg, name, java_type, member):
780 self.msg = msg
781 self.name = name
782 self.java_type = java_type
783 self.member = member
784 self.c_name = self.member.name if(hasattr(self.member, "name")) else ""
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700785
786 @property
787 def title_name(self):
788 return self.name[0].upper() + self.name[1:]
789
790 @property
791 def constant_name(self):
792 return self.c_name.upper()
793
794 @property
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700795 def getter_name(self):
796 return ("is" if self.java_type.public_type == "boolean" else "get") + self.title_name
797
798 @property
799 def setter_name(self):
800 return "set" + self.title_name
801
802 @property
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700803 def default_name(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700804 if self.is_fixed_value:
805 return self.constant_name
806 else:
807 return "DEFAULT_"+self.constant_name
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700808
809 @property
810 def default_value(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700811 if self.is_fixed_value:
812 return self.enum_value
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700813 else:
Andreas Wundsam142dd2a2013-09-23 14:47:37 -0700814 default = self.java_type.default_op(self.msg.version)
815 if default == "null" and not self.is_nullable:
816 return None
817 else:
818 return default
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700819
820 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700821 def enum_value(self):
822 if self.name == "version":
823 return "OFVersion.%s" % self.msg.version.constant_version
824
825 java_type = self.java_type.public_type;
826 try:
827 global model
828 enum = model.enum_by_name(java_type)
829 entry = enum.entry_by_version_value(self.msg.version, self.value)
830 return "%s.%s" % ( enum.name, entry.name)
831 except KeyError, e:
Andreas Wundsam9f8657a2014-10-10 21:28:17 -0700832 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 -0700833 return self.value
834
835 @property
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700836 def is_pad(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700837 return isinstance(self.member, OFPadMember)
838
839 def is_type_value(self, version=None):
840 if(version==None):
841 return any(self.is_type_value(version) for version in self.msg.all_versions)
842 try:
843 return self.c_name in get_type_values(self.msg.c_name, version.int_version)
844 except:
845 return False
846
847 @property
848 def is_field_length_value(self):
849 return isinstance(self.member, OFFieldLengthMember)
850
851 @property
Andreas Wundsam001b1822013-08-02 22:25:55 -0700852 def is_discriminator(self):
853 return isinstance(self.member, OFDiscriminatorMember)
854
855 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700856 def is_length_value(self):
857 return isinstance(self.member, OFLengthMember)
858
859 @property
860 def is_public(self):
861 return not (self.is_pad or self.is_length_value)
862
863 @property
864 def is_data(self):
865 return isinstance(self.member, OFDataMember) and self.name != "version"
866
867 @property
868 def is_fixed_value(self):
869 return hasattr(self.member, "value") or self.name == "version" \
870 or ( self.name == "length" and self.msg.is_fixed_length) \
871 or ( self.name == "len" and self.msg.is_fixed_length)
872
873 @property
874 def value(self):
875 if self.name == "version":
876 return self.msg.version.int_version
877 elif self.name == "length" or self.name == "len":
878 return self.msg.length
Andreas Wundsam27303462013-07-16 12:52:35 -0700879 else:
Andreas Wundsam001b1822013-08-02 22:25:55 -0700880 return self.java_type.format_value(self.member.value)
881
882 @property
883 def priv_value(self):
884 if self.name == "version":
Andreas Wundsam22ba3af2013-10-04 16:00:30 -0700885 return self.java_type.format_value(self.msg.version.int_version, pub_type=False)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700886 elif self.name == "length" or self.name == "len":
Andreas Wundsam22ba3af2013-10-04 16:00:30 -0700887 return self.java_type.format_value(self.msg.length, pub_type=False)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700888 else:
889 return self.java_type.format_value(self.member.value, pub_type=False)
890
Andreas Wundsam27303462013-07-16 12:52:35 -0700891
892 @property
893 def is_writeable(self):
894 return self.is_data and not self.name in model.write_blacklist[self.msg.name]
895
896 def get_type_value_info(self, version):
897 return get_type_values(msg.c_name, version.int_version)[self.c_name]
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700898
899 @property
900 def length(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700901 if hasattr(self.member, "length"):
902 return self.member.length
903 else:
Andreas Wundsam5204de22013-07-30 11:34:45 -0700904 count, base = loxi_utils.type_dec_to_count_base(self.member.type)
Andreas Wundsam27303462013-07-16 12:52:35 -0700905 return of_g.of_base_types[base]['bytes'] * count
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700906
907 @staticmethod
Andreas Wundsam27303462013-07-16 12:52:35 -0700908 def for_of_member(java_class, member):
909 if isinstance(member, OFPadMember):
910 return JavaMember(None, "", None, member)
911 else:
912 if member.name == 'len':
913 name = 'length'
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700914 elif member.name == 'value_mask':
915 name = 'mask'
Andreas Wundsamacd57d52013-10-18 17:35:01 -0700916 elif member.name == 'group_id':
917 name = 'group'
Andreas Wundsam27303462013-07-16 12:52:35 -0700918 else:
919 name = java_type.name_c_to_camel(member.name)
920 j_type = java_type.convert_to_jtype(java_class.c_name, member.name, member.oftype)
921 return JavaMember(java_class, name, j_type, member)
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700922
923 @property
924 def is_universal(self):
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800925 for version, ir_class in self.msg.ir_class.version_classes.items():
926 if not ir_class.member_by_name(self.member.name):
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700927 return False
928 return True
929
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700930 @property
931 def is_virtual(self):
932 return False
933
Andreas Wundsam27303462013-07-16 12:52:35 -0700934 def __hash__(self):
935 return hash(self.name)
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700936
Andreas Wundsam27303462013-07-16 12:52:35 -0700937 def __eq__(self, other):
938 if other is None or type(self) != type(other):
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700939 return False
Andreas Wundsam27303462013-07-16 12:52:35 -0700940 return (self.name,) == (other.name,)
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700941
Andreas Wundsamf1949682013-09-23 14:48:31 -0700942 @property
943 def is_nullable(self):
944 return self.name in model.nullable_map[self.msg.name]
945
946
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700947class JavaVirtualMember(JavaMember):
948 """ 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 -0800949 def __init__(self, msg, name, java_type, value=None, custom_template=None):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700950 JavaMember.__init__(self, msg, name, java_type, member=None)
951 self._value = value
Andreas Wundsam4b8661f2013-11-05 17:17:28 -0800952 self.custom_template = custom_template
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700953
954 @property
955 def is_fixed_value(self):
956 return True
957
958 @property
959 def value(self):
960 return self._value
961
962 @property
963 def priv_value(self):
964 return self._value
965
966
967 @property
968 def is_universal(self):
969 return True
970
971 @property
972 def is_virtual(self):
973 return True
Andreas Wundsame916d6f2013-07-30 11:33:58 -0700974
975#######################################################################
976### Unit Test
977#######################################################################
978
Yotam Harchol466b3212013-08-15 12:14:46 -0700979class JavaUnitTestSet(object):
Andreas Wundsame916d6f2013-07-30 11:33:58 -0700980 def __init__(self, java_class):
981 self.java_class = java_class
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800982 first_data_file_name = "of{version}/{name}.data".format(version=java_class.version.dotless_version,
Andreas Wundsame916d6f2013-07-30 11:33:58 -0700983 name=java_class.c_name[3:])
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800984 glob_file_name = "of{version}/{name}__*.data".format(version=java_class.version.dotless_version,
Andreas Wundsamaa4f3cd2013-10-22 22:19:08 -0700985 name=java_class.c_name[3:])
Yotam Harchol466b3212013-08-15 12:14:46 -0700986 test_class_name = self.java_class.name + "Test"
987 self.test_units = []
988 if test_data.exists(first_data_file_name):
989 self.test_units.append(JavaUnitTest(java_class, first_data_file_name, test_class_name))
Andreas Wundsamaa4f3cd2013-10-22 22:19:08 -0700990
Yotam Harchol466b3212013-08-15 12:14:46 -0700991 i = 1
Andreas Wundsamaa4f3cd2013-10-22 22:19:08 -0700992 for f in test_data.glob(glob_file_name):
993 m = re.match(".*__(.*).data", f)
994 if m:
995 suffix = java_type.name_c_to_caps_camel(m.group(1))
996 else:
997 suffix = str(i)
998 i += 1
999 test_class_name = self.java_class.name + suffix + "Test"
1000 self.test_units.append(JavaUnitTest(java_class, f, test_class_name))
Andreas Wundsamf1949682013-09-23 14:48:31 -07001001
Yotam Harchol466b3212013-08-15 12:14:46 -07001002 @property
1003 def package(self):
1004 return self.java_class.package
1005
1006 @property
1007 def has_test_data(self):
1008 return len(self.test_units) > 0
1009
1010 @property
1011 def length(self):
1012 return len(self.test_units)
Andreas Wundsamf1949682013-09-23 14:48:31 -07001013
Yotam Harchol466b3212013-08-15 12:14:46 -07001014 def get_test_unit(self, i):
1015 return self.test_units[i]
1016
1017
1018class JavaUnitTest(object):
1019 def __init__(self, java_class, file_name=None, test_class_name=None):
1020 self.java_class = java_class
1021 if file_name is None:
Andreas Wundsamc8912c12013-11-15 13:44:48 -08001022 self.data_file_name = "of{version}/{name}.data".format(version=java_class.version.dotless_version,
Yotam Harchol466b3212013-08-15 12:14:46 -07001023 name=java_class.c_name[3:])
1024 else:
1025 self.data_file_name = file_name
1026 if test_class_name is None:
1027 self.test_class_name = self.java_class.name + "Test"
1028 else:
1029 self.test_class_name = test_class_name
Andreas Wundsamf1949682013-09-23 14:48:31 -07001030
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001031 @property
1032 def package(self):
1033 return self.java_class.package
1034
1035 @property
1036 def name(self):
Yotam Harchol466b3212013-08-15 12:14:46 -07001037 return self.test_class_name
Yotam Harchol595c6442013-09-27 16:29:08 -07001038
1039 @property
1040 def interface(self):
1041 return self.java_class.interface
1042
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001043 @property
1044 def has_test_data(self):
1045 return test_data.exists(self.data_file_name)
1046
1047 @property
1048 @memoize
1049 def test_data(self):
1050 return test_data.read(self.data_file_name)
1051
1052
Andreas Wundsam27303462013-07-16 12:52:35 -07001053#######################################################################
1054### Enums
1055#######################################################################
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001056
Andreas Wundsam27303462013-07-16 12:52:35 -07001057class JavaEnum(object):
Andreas Wundsamf299baa2014-10-23 20:15:32 -07001058 def __init__(self, c_name, stable, version_enum_map):
Andreas Wundsam27303462013-07-16 12:52:35 -07001059 self.c_name = c_name
Andreas Wundsamf299baa2014-10-23 20:15:32 -07001060 self.stable = stable
Andreas Wundsambe168f72013-08-03 22:49:35 -07001061
Rob Vaterlausfeee3712013-09-30 11:24:19 -07001062 self.name = "OF" + java_type.name_c_to_caps_camel("_".join(c_name.split("_")[1:]))
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001063
Andreas Wundsam27303462013-07-16 12:52:35 -07001064 # Port_features has constants that start with digits
1065 self.name_prefix = "PF_" if self.name == "OFPortFeatures" else ""
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001066
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001067 self.version_enums = version_enum_map
1068
1069 entry_name_version_value_map = OrderedDefaultDict(lambda: OrderedDict())
1070 for version, ir_enum in version_enum_map.items():
1071 for ir_entry in ir_enum.entries:
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001072 entry_name_version_value_map[ir_entry.name][version] = ir_entry.value
1073
Andreas Wundsam27303462013-07-16 12:52:35 -07001074 self.entries = [ JavaEnumEntry(self, name, version_value_map)
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001075 for (name, version_value_map) in entry_name_version_value_map.items() ]
Andreas Wundsam43526532013-08-01 22:03:50 -07001076
1077 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 -07001078 self.package = "org.projectfloodlight.openflow.protocol"
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001079
Andreas Wundsamf299baa2014-10-23 20:15:32 -07001080 static_metadata = model.enum_metadata_map[self.name]
1081 if self.stable:
1082 # need this to look up wire_type, which does not matter
1083 any_version = version_enum_map.keys()[0]
1084 # if this is a 'stable' enum, i.e., its value won't change, add
1085 # a "Metadata" (virtual) field "StableValue" to it that returns
1086 # its wirevalue.
1087 stable_value = JavaModel.OFEnumPropertyMetadata("StableValue",
1088 self.wire_type(any_version),
1089 value = lambda entry: entry.stable_value)
1090
1091 self.metadata = JavaModel.OFEnumMetadata(
1092 properties=static_metadata.properties + (stable_value, ),
1093 to_string=static_metadata.to_string
1094 )
1095 else:
1096 self.metadata = static_metadata
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -07001097
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001098 def wire_type(self, version):
1099 ir_enum = self.version_enums[version]
1100 if "wire_type" in ir_enum.params:
1101 return java_type.convert_enum_wire_type_to_jtype(ir_enum.params["wire_type"])
1102 else:
1103 return java_type.u8
1104
1105 @property
Andreas Wundsam7cfeac32013-09-17 13:53:48 -07001106 @memoize
1107 def is_bitmask(self):
1108 return any(ir_enum.is_bitmask for ir_enum in self.version_enums.values())
1109
1110 @property
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001111 def versions(self):
1112 return self.version_enums.keys()
1113
Andreas Wundsam27303462013-07-16 12:52:35 -07001114 @memoize
1115 def entry_by_name(self, name):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -07001116 res = find(lambda e: e.name == name, self.entries)
1117 if res:
1118 return res
1119 else:
Andreas Wundsam27303462013-07-16 12:52:35 -07001120 raise KeyError("Enum %s: no entry with name %s" % (self.name, name))
1121
1122 @memoize
1123 def entry_by_c_name(self, name):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -07001124 res = find(lambda e: e.c_name == name, self.entries)
1125 if res:
1126 return res
1127 else:
Andreas Wundsam27303462013-07-16 12:52:35 -07001128 raise KeyError("Enum %s: no entry with c_name %s" % (self.name, name))
1129
1130 @memoize
1131 def entry_by_version_value(self, version, value):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -07001132 res = find(lambda e: e.values[version] == value if version in e.values else False, self.entries)
1133 if res:
1134 return res
1135 else:
Andreas Wundsam27303462013-07-16 12:52:35 -07001136 raise KeyError("Enum %s: no entry with version %s, value %s" % (self.name, version, value))
1137
1138# values: Map JavaVersion->Value
1139class JavaEnumEntry(object):
1140 def __init__(self, enum, name, values):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001141 self.enum = enum
Andreas Wundsam27303462013-07-16 12:52:35 -07001142 self.name = enum.name_prefix + "_".join(name.split("_")[1:]).upper()
1143 self.values = values
1144
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -07001145 @property
1146 def constructor_params(self):
Andreas Wundsamf299baa2014-10-23 20:15:32 -07001147 return [ (m.type, m.value(self)) for m in self.enum.metadata.properties ]
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -07001148
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001149 def has_value(self, version):
1150 return version in self.values
1151
Andreas Wundsam27303462013-07-16 12:52:35 -07001152 def value(self, version):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001153 return self.values[version]
1154
1155 def format_value(self, version):
1156 res = self.enum.wire_type(version).format_value(self.values[version])
Andreas Wundsam27303462013-07-16 12:52:35 -07001157 return res
1158
Andreas Wundsam27303462013-07-16 12:52:35 -07001159 def all_values(self, versions, not_present=None):
1160 return [ self.values[version] if version in self.values else not_present for version in versions ]
Andreas Wundsamc1acfcc2013-09-20 13:10:20 -07001161
1162 @property
Andreas Wundsamf299baa2014-10-23 20:15:32 -07001163 def stable_value(self):
1164 if self.enum.stable:
1165 return self.values.values()[0]
1166 else:
1167 raise Exception("Enum {} not stable".format(self.enum.name))
1168
1169 @property
Andreas Wundsamc1acfcc2013-09-20 13:10:20 -07001170 @memoize
1171 def masked_enum_group(self):
1172 group = find(lambda g: self.name in g.members, model.masked_enum_groups[self.enum.name])
1173 return group
1174
1175 @property
1176 @memoize
1177 def is_mask(self):
1178 return any(self.name == g.mask for g in model.masked_enum_groups[self.enum.name])