blob: fc64f80996487b9b1d187b799f3fd10df9b4e751 [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 ]
416 return []
417
418
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700419 def is_instance_of(self, other_class):
420 if self == other_class:
421 return True
422 parent = self.super_class
423 if parent is None:
424 return False
425 else:
426 return parent.is_instance_of(other_class)
427
428 @property
429 def super_class(self):
430 if not self.parent_interface:
431 return None
432 else:
433 return model.interface_by_name(self.parent_interface)
434
435
436 def inherited_declaration(self, type_spec="?"):
437 if self.type_annotation:
438 return "%s<%s>" % (self.name, type_spec)
439 else:
440 return "%s" % self.name
441
442 @property
443 def type_variable(self):
444 if self.type_annotation:
445 return "<T>"
446 else:
447 return "";
448
Andreas Wundsam27303462013-07-16 12:52:35 -0700449 def class_info(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700450 """ return tuple of (package_prefix, parent_class) for the current JavaOFInterface"""
Andreas Wundsam907c3612014-01-15 12:03:04 -0800451 # FIXME: This code could be cleaned up further. Maybe some of the exceptions
452 # here could be folded into ir, or the type arithmetic specified in a more general
453 # fashion
454 def calc_package(i):
455 if i.is_subclassof("of_error_msg"):
456 return "errormsg"
457 elif i.is_instanceof("of_action"):
458 return "action"
459 elif i.is_instanceof("of_action_id"):
460 return "actionid"
461 elif i.is_instanceof("of_instruction"):
462 return "instruction"
463 elif i.is_instanceof("of_instruction_id"):
464 return "instructionid"
465 elif i.is_instanceof("of_oxm"):
466 return "oxm"
467 elif i.is_instanceof("of_meter_band"):
468 return "meterband"
469 elif i.is_instanceof("of_queue_prop"):
470 return "queueprop"
471 elif i.is_instanceof("of_bsn_tlv"):
472 return "bsntlv"
473 else:
474 return ""
475
476 def calc_super_name(i):
477 if re.match('of_match_.*', i.name):
478 return "Match"
479 else:
480 ir_super_class = self.ir_class.superclass
481 return java_class_name(ir_super_class.name) if ir_super_class else ""
482
483 package = calc_package(self.ir_class)
484 super_name = calc_super_name(self.ir_class)
485
486 if self.name == "OFStatsRequest":
487 # stats_requests are special because of their type annotation
488 return (package, "OFMessage", "T extends OFStatsReply")
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800489 elif self.ir_class.is_subclassof('of_stats_request'):
Andreas Wundsam907c3612014-01-15 12:03:04 -0800490 # stats_request subclasses are special because of their type annotation
491 reply_name = re.sub(r'Request$', 'Reply', self.name)
492 super_type_annotation = "T" if self.ir_class.virtual else reply_name
493
494 type_annotation = "T extends {}".format(reply_name) if self.ir_class.virtual \
495 else ""
496
497 return (package, "{}<{}>".format(super_name, super_type_annotation),
498 type_annotation)
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700499 elif self.name == "OFOxm":
Andreas Wundsam907c3612014-01-15 12:03:04 -0800500 return (package, None, "T extends OFValueType<T>")
Andreas Wundsam5204de22013-07-30 11:34:45 -0700501 elif loxi_utils.class_is_oxm(self.c_name):
Andreas Wundsam907c3612014-01-15 12:03:04 -0800502 # look up type from member value for OFValueType type annotation
Andreas Wundsam83aebd32013-11-19 10:43:50 -0800503 if self.member_by_name("value") is not None:
Andreas Wundsam907c3612014-01-15 12:03:04 -0800504 return (package, "OFOxm<%s>" % self.member_by_name("value").java_type.public_type, None)
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700505 else:
Andreas Wundsam907c3612014-01-15 12:03:04 -0800506 return (package, "OFOxm", None)
Andreas Wundsam27303462013-07-16 12:52:35 -0700507 else:
Andreas Wundsam907c3612014-01-15 12:03:04 -0800508 return (package, super_name, None)
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700509
510 @property
511 @memoize
512 def writeable_members(self):
513 return [ m for m in self.members if m.is_writeable ]
Andreas Wundsam27303462013-07-16 12:52:35 -0700514
Andreas Wundsamcb17b232013-09-28 19:05:36 -0700515 @memoize
516 def member_by_name(self, name):
517 return find(lambda m: m.name == name, self.members)
518
Andreas Wundsam27303462013-07-16 12:52:35 -0700519 @property
520 @memoize
521 def members(self):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700522 return self.ir_model_members + self.virtual_members
523
524 @property
525 @memoize
526 def ir_model_members(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700527 """return a list of all members to be exposed by this interface. Corresponds to
528 the union of the members of the vesioned classes without length, fieldlength
529 and pads (those are handled automatically during (de)serialization and not exposed"""
Andreas Wundsam27303462013-07-16 12:52:35 -0700530 all_versions = []
531 member_map = collections.OrderedDict()
532
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800533 member_version_map = {}
Andreas Wundsam27303462013-07-16 12:52:35 -0700534 for (version, of_class) in self.version_map.items():
535 for of_member in of_class.members:
536 if isinstance(of_member, OFLengthMember) or \
537 isinstance(of_member, OFFieldLengthMember) or \
538 isinstance(of_member, OFPadMember):
539 continue
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800540 java_member = JavaMember.for_of_member(self, of_member)
Andreas Wundsam27303462013-07-16 12:52:35 -0700541 if of_member.name not in member_map:
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800542 member_map[of_member.name] = java_member
543 member_version_map[of_member.name] = version
544 else:
545 existing = member_map[of_member.name]
546
547 if existing.java_type.public_type != java_member.java_type.public_type:
548 raise Exception(
549 "Error constructing interface {}: type signatures do not match up between versions.\n"
550 " Member Name: {}\n"
551 " Existing: Version={}, Java={}, IR={}\n"
552 " New: Version={}, Java={}, IR={}"
553 .format(self.name, existing.name,
554 member_version_map[of_member.name], existing.java_type.public_type, existing.member.oftype,
555 version, java_member.java_type.public_type, java_member.member.oftype)
556 )
Andreas Wundsam27303462013-07-16 12:52:35 -0700557
Andreas Wundsamb1da3f62013-09-04 18:46:03 -0700558 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 -0700559
560 @property
561 def virtual_members(self):
Andreas Wundsama0981022013-10-02 18:15:06 -0700562 virtual_members = []
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700563 if self.name == "OFOxm":
Andreas Wundsama0981022013-10-02 18:15:06 -0700564 virtual_members += [
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700565 JavaVirtualMember(self, "value", java_type.generic_t),
566 JavaVirtualMember(self, "mask", java_type.generic_t),
567 JavaVirtualMember(self, "matchField", java_type.make_match_field_jtype("T")),
568 JavaVirtualMember(self, "masked", java_type.boolean),
Andreas Wundsam661a2222013-11-05 17:18:59 -0800569 JavaVirtualMember(self, "canonical", java_type.make_oxm_jtype("T"))
Andreas Wundsama0981022013-10-02 18:15:06 -0700570 ]
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800571 elif self.ir_class.is_subclassof("of_oxm"):
Andreas Wundsam83aebd32013-11-19 10:43:50 -0800572 value = find(lambda m: m.name=="value", self.ir_model_members)
573 if value:
574 field_type = java_type.make_match_field_jtype(value.java_type.public_type)
575 else:
576 field_type = java_type.make_match_field_jtype()
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700577
Andreas Wundsama0981022013-10-02 18:15:06 -0700578 virtual_members += [
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700579 JavaVirtualMember(self, "matchField", field_type),
580 JavaVirtualMember(self, "masked", java_type.boolean),
Andreas Wundsam83aebd32013-11-19 10:43:50 -0800581 JavaVirtualMember(self, "canonical", java_type.make_oxm_jtype(value.java_type.public_type),
Andreas Wundsam661a2222013-11-05 17:18:59 -0800582 custom_template=lambda builder: "OFOxm{}_getCanonical.java".format(".Builder" if builder else "")),
Andreas Wundsama0981022013-10-02 18:15:06 -0700583 ]
584 if not find(lambda x: x.name == "mask", self.ir_model_members):
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800585 virtual_members.append(
586 JavaVirtualMember(self, "mask", find(lambda x: x.name == "value", self.ir_model_members).java_type))
Andreas Wundsama0981022013-10-02 18:15:06 -0700587
588 if not find(lambda m: m.name == "version", self.ir_model_members):
589 virtual_members.append(JavaVirtualMember(self, "version", java_type.of_version))
590
591 return tuple(virtual_members)
Andreas Wundsam27303462013-07-16 12:52:35 -0700592
593 @property
594 @memoize
595 def is_virtual(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700596 """ Is this interface virtual. If so, do not generate a builder interface """
Andreas Wundsam001b1822013-08-02 22:25:55 -0700597 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 -0700598
599 @property
Andreas Wundsam5204de22013-07-30 11:34:45 -0700600 def is_universal(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700601 """ Is this interface universal, i.e., does it exist in all OF versions? """
Andreas Wundsam5204de22013-07-30 11:34:45 -0700602 return len(self.all_versions) == len(model.versions)
603
604 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700605 @memoize
606 def all_versions(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700607 """ return list of all versions that this interface exists in """
Andreas Wundsam27303462013-07-16 12:52:35 -0700608 return self.version_map.keys()
609
Andreas Wundsam5204de22013-07-30 11:34:45 -0700610 def has_version(self, version):
611 return version in self.version_map
612
Andreas Wundsam27303462013-07-16 12:52:35 -0700613 def versioned_class(self, version):
614 return JavaOFClass(self, version, self.version_map[version])
615
616 @property
617 @memoize
618 def versioned_classes(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700619 return [ self.versioned_class(version) for version in self.all_versions ]
620
621#######################################################################
622### (Versioned) Classes
623#######################################################################
624
625class JavaOFClass(object):
626 """ Models an OpenFlow Message class for the purpose of the java class.
Andreas Wundsamd40ddbf2013-07-25 15:40:47 -0700627 Version specific child of a JavaOFInterface
Andreas Wundsam27303462013-07-16 12:52:35 -0700628 """
629 def __init__(self, interface, version, ir_class):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700630 """
631 @param interface JavaOFInterface instance of the parent interface
632 @param version JavaOFVersion
633 @param ir_class OFClass from loxi_ir
634 """
Andreas Wundsam27303462013-07-16 12:52:35 -0700635 self.interface = interface
636 self.ir_class = ir_class
637 self.c_name = self.ir_class.name
638 self.version = version
639 self.constant_name = self.c_name.upper().replace("OF_", "")
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800640 self.package = "org.projectfloodlight.openflow.protocol.ver%s" % version.dotless_version
Andreas Wundsame916d6f2013-07-30 11:33:58 -0700641 self.generated = False
642
643 @property
644 @memoize
645 def unit_test(self):
Yotam Harchol466b3212013-08-15 12:14:46 -0700646 return JavaUnitTestSet(self)
Andreas Wundsam27303462013-07-16 12:52:35 -0700647
648 @property
649 def name(self):
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800650 return "%sVer%s" % (self.interface.name, self.version.dotless_version)
Andreas Wundsam27303462013-07-16 12:52:35 -0700651
652 @property
Andreas Wundsam5204de22013-07-30 11:34:45 -0700653 def variable_name(self):
Andreas Wundsam781d99f2013-10-23 15:25:56 -0700654 return self.name[2].lower() + self.name[3:]
Andreas Wundsam5204de22013-07-30 11:34:45 -0700655
656 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700657 def length(self):
658 if self.is_fixed_length:
659 return self.min_length
660 else:
661 raise Exception("No fixed length for class %s, version %s" % (self.name, self.version))
662
663 @property
664 def min_length(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700665 """ @return the minimum wire length of an instance of this class in bytes """
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800666 return self.ir_class.base_length
Andreas Wundsam27303462013-07-16 12:52:35 -0700667
668 @property
669 def is_fixed_length(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700670 """ true iff this class serializes to a fixed length on the wire """
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800671 return self.ir_class.is_fixed_length and not self.is_virtual
Andreas Wundsam27303462013-07-16 12:52:35 -0700672
673 def all_properties(self):
674 return self.interface.members
675
Andreas Wundsam27303462013-07-16 12:52:35 -0700676 @property
677 @memoize
678 def data_members(self):
679 return [ prop for prop in self.members if prop.is_data ]
680
681 @property
682 @memoize
683 def fixed_value_members(self):
684 return [ prop for prop in self.members if prop.is_fixed_value ]
685
686 @property
687 @memoize
688 def public_members(self):
689 return [ prop for prop in self.members if prop.is_public ]
690
691 @property
692 @memoize
693 def members(self):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700694 return self.ir_model_members + self.virtual_members
695
696 @property
Andreas Wundsama0981022013-10-02 18:15:06 -0700697 @memoize
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700698 def ir_model_members(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700699 members = [ JavaMember.for_of_member(self, of_member) for of_member in self.ir_class.members ]
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700700 return tuple(members)
701
702 @property
703 def virtual_members(self):
Andreas Wundsama0981022013-10-02 18:15:06 -0700704 virtual_members = []
Andreas Wundsam83aebd32013-11-19 10:43:50 -0800705 if self.ir_class.is_subclassof("of_oxm"):
706 value_member = find(lambda m: m.name, self.ir_model_members)
707 if value_member:
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700708 oxm_entry = model.oxm_map[self.interface.name]
Andreas Wundsama0981022013-10-02 18:15:06 -0700709 virtual_members += [
Andreas Wundsam83aebd32013-11-19 10:43:50 -0800710 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 -0700711 JavaVirtualMember(self, "masked", java_type.boolean, "true" if oxm_entry.masked else "false"),
Andreas Wundsam661a2222013-11-05 17:18:59 -0800712 ]
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700713 else:
Andreas Wundsama0981022013-10-02 18:15:06 -0700714 virtual_members += [
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700715 JavaVirtualMember(self, "matchField", java_type.make_match_field_jtype(), "null"),
716 JavaVirtualMember(self, "masked", java_type.boolean, "false"),
Andreas Wundsam661a2222013-11-05 17:18:59 -0800717 ]
Andreas Wundsama0981022013-10-02 18:15:06 -0700718 if not find(lambda m: m.name == "version", self.ir_model_members):
719 virtual_members.append(JavaVirtualMember(self, "version", java_type.of_version, "OFVersion.%s" % self.version.constant_version))
720
721 return tuple(virtual_members)
Andreas Wundsam27303462013-07-16 12:52:35 -0700722
Andreas Wundsam661a2222013-11-05 17:18:59 -0800723 @memoize
724 def member_by_name(self, name):
725 return find(lambda m: m.name == name, self.members)
726
Andreas Wundsam27303462013-07-16 12:52:35 -0700727 def all_versions(self):
728 return [ JavaOFVersion(int_version)
729 for int_version in of_g.unified[self.c_name]
730 if int_version != 'union' and int_version != 'object_id' ]
731
732 def version_is_inherited(self, version):
733 return 'use_version' in of_g.unified[self.ir_class.name][version.int_version]
734
735 def inherited_from(self, version):
736 return JavaOFVersion(of_g.unified[self.ir_class.name][version.int_version]['use_version'])
737
738 @property
739 def is_virtual(self):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700740 return self.ir_class.virtual # type_maps.class_is_virtual(self.c_name) or self.ir_class.virtual
741
742 @property
743 def discriminator(self):
744 return find(lambda m: isinstance(m, OFDiscriminatorMember), self.ir_class.members)
Andreas Wundsam27303462013-07-16 12:52:35 -0700745
746 @property
747 def is_extension(self):
748 return type_maps.message_is_extension(self.c_name, -1)
749
Andreas Wundsam758c9cc2013-08-01 22:16:06 -0700750 @property
751 def align(self):
752 return int(self.ir_class.params['align']) if 'align' in self.ir_class.params else 0
753
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700754 @property
Andreas Wundsam5da68512013-10-22 22:18:00 -0700755 def length_includes_align(self):
756 return self.ir_class.params['length_includes_align'] == "True" if 'length_includes_align' in self.ir_class.params else False
757
758 @property
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700759 @memoize
760 def superclass(self):
761 return find(lambda c: c.version == self.version and c.c_name == self.ir_class.superclass, model.all_classes)
762
763 @property
764 @memoize
765 def subclasses(self):
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800766 return [ c for c in model.all_classes if c.version == self.version and c.ir_class.superclass
767 and c.ir_class.superclass.name == self.c_name ]
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700768
Andreas Wundsam27303462013-07-16 12:52:35 -0700769#######################################################################
770### Member
771#######################################################################
772
773
774class JavaMember(object):
775 """ Models a property (member) of an openflow class. """
776 def __init__(self, msg, name, java_type, member):
777 self.msg = msg
778 self.name = name
779 self.java_type = java_type
780 self.member = member
781 self.c_name = self.member.name if(hasattr(self.member, "name")) else ""
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700782
783 @property
784 def title_name(self):
785 return self.name[0].upper() + self.name[1:]
786
787 @property
788 def constant_name(self):
789 return self.c_name.upper()
790
791 @property
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700792 def getter_name(self):
793 return ("is" if self.java_type.public_type == "boolean" else "get") + self.title_name
794
795 @property
796 def setter_name(self):
797 return "set" + self.title_name
798
799 @property
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700800 def default_name(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700801 if self.is_fixed_value:
802 return self.constant_name
803 else:
804 return "DEFAULT_"+self.constant_name
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700805
806 @property
807 def default_value(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700808 if self.is_fixed_value:
809 return self.enum_value
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700810 else:
Andreas Wundsam142dd2a2013-09-23 14:47:37 -0700811 default = self.java_type.default_op(self.msg.version)
812 if default == "null" and not self.is_nullable:
813 return None
814 else:
815 return default
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700816
817 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700818 def enum_value(self):
819 if self.name == "version":
820 return "OFVersion.%s" % self.msg.version.constant_version
821
822 java_type = self.java_type.public_type;
823 try:
824 global model
825 enum = model.enum_by_name(java_type)
826 entry = enum.entry_by_version_value(self.msg.version, self.value)
827 return "%s.%s" % ( enum.name, entry.name)
828 except KeyError, e:
Andreas Wundsamaa8a9182013-11-19 09:45:01 -0800829 logger.debug("No enum found", e)
Andreas Wundsam27303462013-07-16 12:52:35 -0700830 return self.value
831
832 @property
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700833 def is_pad(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700834 return isinstance(self.member, OFPadMember)
835
836 def is_type_value(self, version=None):
837 if(version==None):
838 return any(self.is_type_value(version) for version in self.msg.all_versions)
839 try:
840 return self.c_name in get_type_values(self.msg.c_name, version.int_version)
841 except:
842 return False
843
844 @property
845 def is_field_length_value(self):
846 return isinstance(self.member, OFFieldLengthMember)
847
848 @property
Andreas Wundsam001b1822013-08-02 22:25:55 -0700849 def is_discriminator(self):
850 return isinstance(self.member, OFDiscriminatorMember)
851
852 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700853 def is_length_value(self):
854 return isinstance(self.member, OFLengthMember)
855
856 @property
857 def is_public(self):
858 return not (self.is_pad or self.is_length_value)
859
860 @property
861 def is_data(self):
862 return isinstance(self.member, OFDataMember) and self.name != "version"
863
864 @property
865 def is_fixed_value(self):
866 return hasattr(self.member, "value") or self.name == "version" \
867 or ( self.name == "length" and self.msg.is_fixed_length) \
868 or ( self.name == "len" and self.msg.is_fixed_length)
869
870 @property
871 def value(self):
872 if self.name == "version":
873 return self.msg.version.int_version
874 elif self.name == "length" or self.name == "len":
875 return self.msg.length
Andreas Wundsam27303462013-07-16 12:52:35 -0700876 else:
Andreas Wundsam001b1822013-08-02 22:25:55 -0700877 return self.java_type.format_value(self.member.value)
878
879 @property
880 def priv_value(self):
881 if self.name == "version":
Andreas Wundsam22ba3af2013-10-04 16:00:30 -0700882 return self.java_type.format_value(self.msg.version.int_version, pub_type=False)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700883 elif self.name == "length" or self.name == "len":
Andreas Wundsam22ba3af2013-10-04 16:00:30 -0700884 return self.java_type.format_value(self.msg.length, pub_type=False)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700885 else:
886 return self.java_type.format_value(self.member.value, pub_type=False)
887
Andreas Wundsam27303462013-07-16 12:52:35 -0700888
889 @property
890 def is_writeable(self):
891 return self.is_data and not self.name in model.write_blacklist[self.msg.name]
892
893 def get_type_value_info(self, version):
894 return get_type_values(msg.c_name, version.int_version)[self.c_name]
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700895
896 @property
897 def length(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700898 if hasattr(self.member, "length"):
899 return self.member.length
900 else:
Andreas Wundsam5204de22013-07-30 11:34:45 -0700901 count, base = loxi_utils.type_dec_to_count_base(self.member.type)
Andreas Wundsam27303462013-07-16 12:52:35 -0700902 return of_g.of_base_types[base]['bytes'] * count
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700903
904 @staticmethod
Andreas Wundsam27303462013-07-16 12:52:35 -0700905 def for_of_member(java_class, member):
906 if isinstance(member, OFPadMember):
907 return JavaMember(None, "", None, member)
908 else:
909 if member.name == 'len':
910 name = 'length'
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700911 elif member.name == 'value_mask':
912 name = 'mask'
Andreas Wundsamacd57d52013-10-18 17:35:01 -0700913 elif member.name == 'group_id':
914 name = 'group'
Andreas Wundsam27303462013-07-16 12:52:35 -0700915 else:
916 name = java_type.name_c_to_camel(member.name)
917 j_type = java_type.convert_to_jtype(java_class.c_name, member.name, member.oftype)
918 return JavaMember(java_class, name, j_type, member)
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700919
920 @property
921 def is_universal(self):
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800922 for version, ir_class in self.msg.ir_class.version_classes.items():
923 if not ir_class.member_by_name(self.member.name):
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700924 return False
925 return True
926
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700927 @property
928 def is_virtual(self):
929 return False
930
Andreas Wundsam27303462013-07-16 12:52:35 -0700931 def __hash__(self):
932 return hash(self.name)
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700933
Andreas Wundsam27303462013-07-16 12:52:35 -0700934 def __eq__(self, other):
935 if other is None or type(self) != type(other):
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700936 return False
Andreas Wundsam27303462013-07-16 12:52:35 -0700937 return (self.name,) == (other.name,)
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700938
Andreas Wundsamf1949682013-09-23 14:48:31 -0700939 @property
940 def is_nullable(self):
941 return self.name in model.nullable_map[self.msg.name]
942
943
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700944class JavaVirtualMember(JavaMember):
945 """ 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 -0800946 def __init__(self, msg, name, java_type, value=None, custom_template=None):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700947 JavaMember.__init__(self, msg, name, java_type, member=None)
948 self._value = value
Andreas Wundsam4b8661f2013-11-05 17:17:28 -0800949 self.custom_template = custom_template
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700950
951 @property
952 def is_fixed_value(self):
953 return True
954
955 @property
956 def value(self):
957 return self._value
958
959 @property
960 def priv_value(self):
961 return self._value
962
963
964 @property
965 def is_universal(self):
966 return True
967
968 @property
969 def is_virtual(self):
970 return True
Andreas Wundsame916d6f2013-07-30 11:33:58 -0700971
972#######################################################################
973### Unit Test
974#######################################################################
975
Yotam Harchol466b3212013-08-15 12:14:46 -0700976class JavaUnitTestSet(object):
Andreas Wundsame916d6f2013-07-30 11:33:58 -0700977 def __init__(self, java_class):
978 self.java_class = java_class
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800979 first_data_file_name = "of{version}/{name}.data".format(version=java_class.version.dotless_version,
Andreas Wundsame916d6f2013-07-30 11:33:58 -0700980 name=java_class.c_name[3:])
Andreas Wundsamc8912c12013-11-15 13:44:48 -0800981 glob_file_name = "of{version}/{name}__*.data".format(version=java_class.version.dotless_version,
Andreas Wundsamaa4f3cd2013-10-22 22:19:08 -0700982 name=java_class.c_name[3:])
Yotam Harchol466b3212013-08-15 12:14:46 -0700983 test_class_name = self.java_class.name + "Test"
984 self.test_units = []
985 if test_data.exists(first_data_file_name):
986 self.test_units.append(JavaUnitTest(java_class, first_data_file_name, test_class_name))
Andreas Wundsamaa4f3cd2013-10-22 22:19:08 -0700987
Yotam Harchol466b3212013-08-15 12:14:46 -0700988 i = 1
Andreas Wundsamaa4f3cd2013-10-22 22:19:08 -0700989 for f in test_data.glob(glob_file_name):
990 m = re.match(".*__(.*).data", f)
991 if m:
992 suffix = java_type.name_c_to_caps_camel(m.group(1))
993 else:
994 suffix = str(i)
995 i += 1
996 test_class_name = self.java_class.name + suffix + "Test"
997 self.test_units.append(JavaUnitTest(java_class, f, test_class_name))
Andreas Wundsamf1949682013-09-23 14:48:31 -0700998
Yotam Harchol466b3212013-08-15 12:14:46 -0700999 @property
1000 def package(self):
1001 return self.java_class.package
1002
1003 @property
1004 def has_test_data(self):
1005 return len(self.test_units) > 0
1006
1007 @property
1008 def length(self):
1009 return len(self.test_units)
Andreas Wundsamf1949682013-09-23 14:48:31 -07001010
Yotam Harchol466b3212013-08-15 12:14:46 -07001011 def get_test_unit(self, i):
1012 return self.test_units[i]
1013
1014
1015class JavaUnitTest(object):
1016 def __init__(self, java_class, file_name=None, test_class_name=None):
1017 self.java_class = java_class
1018 if file_name is None:
Andreas Wundsamc8912c12013-11-15 13:44:48 -08001019 self.data_file_name = "of{version}/{name}.data".format(version=java_class.version.dotless_version,
Yotam Harchol466b3212013-08-15 12:14:46 -07001020 name=java_class.c_name[3:])
1021 else:
1022 self.data_file_name = file_name
1023 if test_class_name is None:
1024 self.test_class_name = self.java_class.name + "Test"
1025 else:
1026 self.test_class_name = test_class_name
Andreas Wundsamf1949682013-09-23 14:48:31 -07001027
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001028 @property
1029 def package(self):
1030 return self.java_class.package
1031
1032 @property
1033 def name(self):
Yotam Harchol466b3212013-08-15 12:14:46 -07001034 return self.test_class_name
Yotam Harchol595c6442013-09-27 16:29:08 -07001035
1036 @property
1037 def interface(self):
1038 return self.java_class.interface
1039
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001040 @property
1041 def has_test_data(self):
1042 return test_data.exists(self.data_file_name)
1043
1044 @property
1045 @memoize
1046 def test_data(self):
1047 return test_data.read(self.data_file_name)
1048
1049
Andreas Wundsam27303462013-07-16 12:52:35 -07001050#######################################################################
1051### Enums
1052#######################################################################
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001053
Andreas Wundsam27303462013-07-16 12:52:35 -07001054class JavaEnum(object):
Andreas Wundsamf299baa2014-10-23 20:15:32 -07001055 def __init__(self, c_name, stable, version_enum_map):
Andreas Wundsam27303462013-07-16 12:52:35 -07001056 self.c_name = c_name
Andreas Wundsamf299baa2014-10-23 20:15:32 -07001057 self.stable = stable
Andreas Wundsambe168f72013-08-03 22:49:35 -07001058
Rob Vaterlausfeee3712013-09-30 11:24:19 -07001059 self.name = "OF" + java_type.name_c_to_caps_camel("_".join(c_name.split("_")[1:]))
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001060
Andreas Wundsam27303462013-07-16 12:52:35 -07001061 # Port_features has constants that start with digits
1062 self.name_prefix = "PF_" if self.name == "OFPortFeatures" else ""
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001063
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001064 self.version_enums = version_enum_map
1065
1066 entry_name_version_value_map = OrderedDefaultDict(lambda: OrderedDict())
1067 for version, ir_enum in version_enum_map.items():
1068 for ir_entry in ir_enum.entries:
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001069 entry_name_version_value_map[ir_entry.name][version] = ir_entry.value
1070
Andreas Wundsam27303462013-07-16 12:52:35 -07001071 self.entries = [ JavaEnumEntry(self, name, version_value_map)
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001072 for (name, version_value_map) in entry_name_version_value_map.items() ]
Andreas Wundsam43526532013-08-01 22:03:50 -07001073
1074 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 -07001075 self.package = "org.projectfloodlight.openflow.protocol"
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001076
Andreas Wundsamf299baa2014-10-23 20:15:32 -07001077 static_metadata = model.enum_metadata_map[self.name]
1078 if self.stable:
1079 # need this to look up wire_type, which does not matter
1080 any_version = version_enum_map.keys()[0]
1081 # if this is a 'stable' enum, i.e., its value won't change, add
1082 # a "Metadata" (virtual) field "StableValue" to it that returns
1083 # its wirevalue.
1084 stable_value = JavaModel.OFEnumPropertyMetadata("StableValue",
1085 self.wire_type(any_version),
1086 value = lambda entry: entry.stable_value)
1087
1088 self.metadata = JavaModel.OFEnumMetadata(
1089 properties=static_metadata.properties + (stable_value, ),
1090 to_string=static_metadata.to_string
1091 )
1092 else:
1093 self.metadata = static_metadata
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -07001094
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001095 def wire_type(self, version):
1096 ir_enum = self.version_enums[version]
1097 if "wire_type" in ir_enum.params:
1098 return java_type.convert_enum_wire_type_to_jtype(ir_enum.params["wire_type"])
1099 else:
1100 return java_type.u8
1101
1102 @property
Andreas Wundsam7cfeac32013-09-17 13:53:48 -07001103 @memoize
1104 def is_bitmask(self):
1105 return any(ir_enum.is_bitmask for ir_enum in self.version_enums.values())
1106
1107 @property
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001108 def versions(self):
1109 return self.version_enums.keys()
1110
Andreas Wundsam27303462013-07-16 12:52:35 -07001111 @memoize
1112 def entry_by_name(self, name):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -07001113 res = find(lambda e: e.name == name, self.entries)
1114 if res:
1115 return res
1116 else:
Andreas Wundsam27303462013-07-16 12:52:35 -07001117 raise KeyError("Enum %s: no entry with name %s" % (self.name, name))
1118
1119 @memoize
1120 def entry_by_c_name(self, name):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -07001121 res = find(lambda e: e.c_name == name, self.entries)
1122 if res:
1123 return res
1124 else:
Andreas Wundsam27303462013-07-16 12:52:35 -07001125 raise KeyError("Enum %s: no entry with c_name %s" % (self.name, name))
1126
1127 @memoize
1128 def entry_by_version_value(self, version, value):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -07001129 res = find(lambda e: e.values[version] == value if version in e.values else False, self.entries)
1130 if res:
1131 return res
1132 else:
Andreas Wundsam27303462013-07-16 12:52:35 -07001133 raise KeyError("Enum %s: no entry with version %s, value %s" % (self.name, version, value))
1134
1135# values: Map JavaVersion->Value
1136class JavaEnumEntry(object):
1137 def __init__(self, enum, name, values):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001138 self.enum = enum
Andreas Wundsam27303462013-07-16 12:52:35 -07001139 self.name = enum.name_prefix + "_".join(name.split("_")[1:]).upper()
1140 self.values = values
1141
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -07001142 @property
1143 def constructor_params(self):
Andreas Wundsamf299baa2014-10-23 20:15:32 -07001144 return [ (m.type, m.value(self)) for m in self.enum.metadata.properties ]
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -07001145
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001146 def has_value(self, version):
1147 return version in self.values
1148
Andreas Wundsam27303462013-07-16 12:52:35 -07001149 def value(self, version):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001150 return self.values[version]
1151
1152 def format_value(self, version):
1153 res = self.enum.wire_type(version).format_value(self.values[version])
Andreas Wundsam27303462013-07-16 12:52:35 -07001154 return res
1155
Andreas Wundsam27303462013-07-16 12:52:35 -07001156 def all_values(self, versions, not_present=None):
1157 return [ self.values[version] if version in self.values else not_present for version in versions ]
Andreas Wundsamc1acfcc2013-09-20 13:10:20 -07001158
1159 @property
Andreas Wundsamf299baa2014-10-23 20:15:32 -07001160 def stable_value(self):
1161 if self.enum.stable:
1162 return self.values.values()[0]
1163 else:
1164 raise Exception("Enum {} not stable".format(self.enum.name))
1165
1166 @property
Andreas Wundsamc1acfcc2013-09-20 13:10:20 -07001167 @memoize
1168 def masked_enum_group(self):
1169 group = find(lambda g: self.name in g.members, model.masked_enum_groups[self.enum.name])
1170 return group
1171
1172 @property
1173 @memoize
1174 def is_mask(self):
1175 return any(self.name == g.mask for g in model.masked_enum_groups[self.enum.name])