blob: 5e5779ef121b59ad53df73bd574a32caca928a03 [file] [log] [blame]
Andreas Wundsam27303462013-07-16 12:52:35 -07001# Copyright 2013, Big Switch Networks, Inc.
2#
3# LoxiGen is licensed under the Eclipse Public License, version 1.0 (EPL), with
4# the following special exception:
5#
6# LOXI Exception
7#
8# As a special exception to the terms of the EPL, you may distribute libraries
9# generated by LoxiGen (LoxiGen Libraries) under the terms of your choice, provided
10# that copyright and licensing notices generated by LoxiGen are not altered or removed
11# from the LoxiGen Libraries and the notice provided below is (i) included in
12# the LoxiGen Libraries, if distributed in source code form and (ii) included in any
13# documentation for the LoxiGen Libraries, if distributed in binary form.
14#
15# Notice: "Copyright 2013, Big Switch Networks, Inc. This library was generated by the LoxiGen Compiler."
16#
17# You may not use this file except in compliance with the EPL or LOXI Exception. You may obtain
18# a copy of the EPL at:
19#
20# http://www.eclipse.org/legal/epl-v10.html
21#
22# Unless required by applicable law or agreed to in writing, software
23# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
24# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
25# EPL for the specific language governing permissions and limitations
26# under the EPL.
27
Andreas Wundsam40e14f72013-05-06 14:49:08 -070028# Prototype of an Intermediate Object model for the java code generator
29# A lot of this stuff could/should probably be merged with the python utilities
30
Andreas Wundsam27303462013-07-16 12:52:35 -070031import collections
Andreas Wundsam5204de22013-07-30 11:34:45 -070032from collections import namedtuple, defaultdict, OrderedDict
Andreas Wundsam27303462013-07-16 12:52:35 -070033import logging
Andreas Wundsam40e14f72013-05-06 14:49:08 -070034import os
35import pdb
36import re
37
Andreas Wundsam5204de22013-07-30 11:34:45 -070038from generic_utils import find, memoize, OrderedSet, OrderedDefaultDict
Andreas Wundsam27303462013-07-16 12:52:35 -070039import of_g
40from loxi_ir import *
Andreas Wundsam40e14f72013-05-06 14:49:08 -070041import loxi_front_end.type_maps as type_maps
Andreas Wundsam5204de22013-07-30 11:34:45 -070042import loxi_utils.loxi_utils as loxi_utils
Andreas Wundsam40e14f72013-05-06 14:49:08 -070043import py_gen.util as py_utils
Andreas Wundsam5204de22013-07-30 11:34:45 -070044import test_data
Andreas Wundsam40e14f72013-05-06 14:49:08 -070045
Andreas Wundsam27303462013-07-16 12:52:35 -070046import java_gen.java_type as java_type
Andreas Wundsame0d52be2013-08-22 07:52:13 -070047from java_gen.java_type import erase_type_annotation
Andreas Wundsam40e14f72013-05-06 14:49:08 -070048
Andreas Wundsam27303462013-07-16 12:52:35 -070049class JavaModel(object):
Andreas Wundsam43526532013-08-01 22:03:50 -070050 enum_blacklist = set(("OFDefinitions",))
51 enum_entry_blacklist = defaultdict(lambda: set(), OFFlowWildcards=set([ "NW_DST_BITS", "NW_SRC_BITS", "NW_SRC_SHIFT", "NW_DST_SHIFT" ]))
Andreas Wundsambe168f72013-08-03 22:49:35 -070052 # OFUint structs are there for god-knows what in loci. We certainly don't need them.
53 interface_blacklist = set( ("OFUint8", "OFUint32",))
Andreas Wundsam001b1822013-08-02 22:25:55 -070054 write_blacklist = defaultdict(lambda: set(), OFOxm=set(('typeLen',)), OFAction=set(('type',)), OFInstruction=set(('type',)), OFFlowMod=set(('command', )))
55 virtual_interfaces = set(['OFOxm', 'OFInstruction', 'OFFlowMod', 'OFBsnVport' ])
Andreas Wundsam27303462013-07-16 12:52:35 -070056
Andreas Wundsam2be7da52013-08-22 07:34:25 -070057 OxmMapEntry = namedtuple("OxmMapEntry", ["type_name", "value", "masked" ])
58 oxm_map = { "OFOxmInPortMasked": OxmMapEntry("OFPort", "IN_PORT", True) }
59
Andreas Wundsam27303462013-07-16 12:52:35 -070060 @property
61 @memoize
62 def versions(self):
63 return OrderedSet( JavaOFVersion(raw_version) for raw_version in of_g.target_version_list )
64
65 @property
66 @memoize
67 def interfaces(self):
Andreas Wundsam5204de22013-07-30 11:34:45 -070068 version_map_per_class = collections.OrderedDict()
Andreas Wundsam27303462013-07-16 12:52:35 -070069
70 for raw_version, of_protocol in of_g.ir.items():
71 jversion = JavaOFVersion(of_protocol.wire_version)
72
73 for of_class in of_protocol.classes:
Andreas Wundsam5204de22013-07-30 11:34:45 -070074 if not of_class.name in version_map_per_class:
75 version_map_per_class[of_class.name] = collections.OrderedDict()
76
Andreas Wundsam27303462013-07-16 12:52:35 -070077 version_map_per_class[of_class.name][jversion] = of_class
78
79 interfaces = []
80 for class_name, version_map in version_map_per_class.items():
81 interfaces.append(JavaOFInterface(class_name, version_map))
82
Andreas Wundsambe168f72013-08-03 22:49:35 -070083 interfaces = [ i for i in interfaces if i.name not in self.interface_blacklist ]
84
Andreas Wundsam27303462013-07-16 12:52:35 -070085 return interfaces
86
Andreas Wundsame0d52be2013-08-22 07:52:13 -070087 @memoize
88 def interface_by_name(self, name):
89 return find(lambda i: erase_type_annotation(i.name) == erase_type_annotation(name), self.interfaces)
90
Andreas Wundsam27303462013-07-16 12:52:35 -070091 @property
92 @memoize
Andreas Wundsam001b1822013-08-02 22:25:55 -070093 def all_classes(self):
94 return [clazz for interface in self.interfaces for clazz in interface.versioned_classes]
95
96 @property
97 @memoize
Andreas Wundsam27303462013-07-16 12:52:35 -070098 def enums(self):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -070099 name_version_enum_map = OrderedDefaultDict(lambda: OrderedDict())
Andreas Wundsam27303462013-07-16 12:52:35 -0700100
101 for version in self.versions:
102 of_protocol = of_g.ir[version.int_version]
103 for enum in of_protocol.enums:
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700104 name_version_enum_map[enum.name][version] = enum
Andreas Wundsam27303462013-07-16 12:52:35 -0700105
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700106 enums = [ JavaEnum(name, version_enum_map) for name, version_enum_map,
107 in name_version_enum_map.items() ]
Andreas Wundsam27303462013-07-16 12:52:35 -0700108
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700109 # inelegant - need java name here
110 enums = [ enum for enum in enums if enum.name not in self.enum_blacklist ]
Andreas Wundsam27303462013-07-16 12:52:35 -0700111 return enums
112
113 @memoize
114 def enum_by_name(self, name):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700115 res = find(lambda e: e.name == name, self.enums)
116 if not res:
Andreas Wundsam27303462013-07-16 12:52:35 -0700117 raise KeyError("Could not find enum with name %s" % name)
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700118 return res
Andreas Wundsam27303462013-07-16 12:52:35 -0700119
Andreas Wundsam5204de22013-07-30 11:34:45 -0700120 @property
121 @memoize
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700122 def of_factories(self):
123 prefix = "org.openflow.protocol"
124
125 factories = OrderedDict()
126
127 sub_factory_classes = ("OFAction", "OFInstruction", "OFMeterBand", "OFOxm", "OFQueueProp")
128 for base_class in sub_factory_classes:
129 package = base_class[2:].lower()
130 remove_prefix = base_class[2].lower() + base_class[3:]
131
132 # HACK need to have a better way to deal with parameterized base classes
133 annotated_base_class = base_class + "<?>" if base_class == "OFOxm" else base_class
134
135 factories[base_class] = OFFactory(package="%s.%s" % (prefix, package),
136 name=base_class + "s", members=[], remove_prefix=remove_prefix, base_class=annotated_base_class, sub_factories={})
137
138 factories[""] = OFFactory(
139 package=prefix,
Andreas Wundsam5204de22013-07-30 11:34:45 -0700140 name="OFFactory",
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700141 remove_prefix="",
142 members=[], base_class="OFMessage", sub_factories=OrderedDict(
143 ("{}{}s".format(n[2].lower(), n[3:]), "{}s".format(n)) for n in sub_factory_classes ))
144
145 for i in self.interfaces:
146 for n, factory in factories.items():
147 if n == "":
148 factory.members.append(i)
149 break
150 else:
151 super_class = self.interface_by_name(n)
152 if i.is_instance_of(super_class):
153 factory.members.append(i)
154 break
155 return factories.values()
Andreas Wundsam5204de22013-07-30 11:34:45 -0700156
157 def generate_class(self, clazz):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700158 """ return wether or not to generate implementation class clazz.
159 Now true for everything except OFTableModVer10.
160 @param clazz JavaOFClass instance
161 """
Andreas Wundsam43526532013-08-01 22:03:50 -0700162 if clazz.interface.name.startswith("OFMatchV"):
163 return True
Andreas Wundsam001b1822013-08-02 22:25:55 -0700164 elif clazz.name == "OFTableModVer10":
165 # tablemod ver 10 is a hack and has no oftype defined
166 return False
Andreas Wundsam5204de22013-07-30 11:34:45 -0700167 if loxi_utils.class_is_message(clazz.interface.c_name):
168 return True
169 if loxi_utils.class_is_oxm(clazz.interface.c_name):
170 return True
Andreas Wundsam43526532013-08-01 22:03:50 -0700171 if loxi_utils.class_is_action(clazz.interface.c_name):
172 return True
173 if loxi_utils.class_is_instruction(clazz.interface.c_name):
174 return True
Andreas Wundsam5204de22013-07-30 11:34:45 -0700175 else:
Andreas Wundsam001b1822013-08-02 22:25:55 -0700176 return True
Andreas Wundsam5204de22013-07-30 11:34:45 -0700177
178
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700179class OFFactory(namedtuple("OFFactory", ("package", "name", "members", "remove_prefix", "base_class", "sub_factories"))):
Andreas Wundsam5204de22013-07-30 11:34:45 -0700180 @property
181 def factory_classes(self):
182 return [ OFFactoryClass(
183 package="org.openflow.protocol.ver{}".format(version.of_version),
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700184 name="{}Ver{}".format(self.name, version.of_version),
Andreas Wundsam5204de22013-07-30 11:34:45 -0700185 interface=self,
186 version=version
187 ) for version in model.versions ]
188
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700189 def method_name(self, member, builder=True):
190 n = member.variable_name
191 if n.startswith(self.remove_prefix):
192 n = n[len(self.remove_prefix):]
193 n = n[0].lower() + n[1:]
194 if builder:
195 return "build" + n[0].upper() + n[1:]
196 else:
197 return n
Andreas Wundsam5204de22013-07-30 11:34:45 -0700198
199OFGenericClass = namedtuple("OFGenericClass", ("package", "name"))
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700200class OFFactoryClass(namedtuple("OFFactoryClass", ("package", "name", "interface", "version"))):
201 @property
202 def base_class(self):
203 return self.interface.base_class
204
205 @property
206 def versioned_base_class(self):
207 base_class_interface = model.interface_by_name(self.interface.base_class)
208 if base_class_interface and base_class_interface.has_version(self.version):
209 return base_class_interface.versioned_class(self.version)
210 else:
211 return None
Andreas Wundsam5204de22013-07-30 11:34:45 -0700212
Andreas Wundsam27303462013-07-16 12:52:35 -0700213model = JavaModel()
214
215#######################################################################
216### OFVersion
217#######################################################################
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700218
219class JavaOFVersion(object):
220 """ Models a version of OpenFlow. contains methods to convert the internal
221 Loxi version to a java constant / a string """
222 def __init__(self, int_version):
223 self.int_version = int(int_version)
224
225 @property
226 def of_version(self):
227 return "1" + str(int(self.int_version) - 1)
228
229 @property
230 def constant_version(self):
231 return "OF_" + self.of_version
232
Andreas Wundsam27303462013-07-16 12:52:35 -0700233 def __repr__(self):
234 return "JavaOFVersion(%d)" % self.int_version
235
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700236 def __str__(self):
237 return of_g.param_version_names[self.int_version]
238
Andreas Wundsam27303462013-07-16 12:52:35 -0700239 def __hash__(self):
240 return hash(self.int_version)
241
242 def __eq__(self, other):
243 if other is None or type(self) != type(other):
244 return False
245 return (self.int_version,) == (other.int_version,)
246
247#######################################################################
248### Interface
249#######################################################################
250
251class JavaOFInterface(object):
252 """ Models an OpenFlow Message class for the purpose of the java class.
253 Version agnostic, in contrast to the loxi_ir python model.
254 """
255 def __init__(self, c_name, version_map):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700256 """"
257 @param c_name: loxi style name (e.g., of_flow_add)
258 @param version_map map of { JavaOFVersion: OFClass (from loxi_ir) }
259 """
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700260 self.c_name = c_name
Andreas Wundsam27303462013-07-16 12:52:35 -0700261 self.version_map = version_map
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700262 # name: the Java Type name, e.g., OFFlowAdd
Andreas Wundsam001b1822013-08-02 22:25:55 -0700263 self.name = java_type.name_c_to_caps_camel(c_name) if c_name != "of_header" else "OFMessage"
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700264 # variable_name name to use for variables of this type. i.e., flowAdd
Andreas Wundsam5204de22013-07-30 11:34:45 -0700265 self.variable_name = self.name[2].lower() + self.name[3:]
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700266 self.title_name = self.variable_name[0].upper() + self.variable_name[1:]
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700267 # name for use in constants: FLOW_ADD
Andreas Wundsam27303462013-07-16 12:52:35 -0700268 self.constant_name = c_name.upper().replace("OF_", "")
269
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700270 pck_suffix, parent_interface, self.type_annotation = self.class_info()
Andreas Wundsama705c6b2013-09-04 11:21:09 -0700271
Andreas Wundsam27303462013-07-16 12:52:35 -0700272 self.package = "org.openflow.protocol.%s" % pck_suffix if pck_suffix else "org.openflow.protocol"
273 if self.name != parent_interface:
274 self.parent_interface = parent_interface
275 else:
276 self.parent_interface = None
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700277
Andreas Wundsama705c6b2013-09-04 11:21:09 -0700278 @property
279 @memoize
280 def all_parent_interfaces(self):
281 return [ "OFObject" ] + \
282 ([ self.parent_interface ] if self.parent_interface else [] )+ \
283 self.additional_parent_interfaces
284 @property
285 @memoize
286 def additional_parent_interfaces(self):
287 if loxi_utils.class_is_message(self.c_name) and not self.is_virtual:
288 m = re.match(r'(.*)Request$', self.name)
289 if m:
290 reply_name = m.group(1) + "Reply"
291 if model.interface_by_name(reply_name):
292 return ["OFRequest<%s>" % reply_name ]
293 return []
294
295
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700296 def is_instance_of(self, other_class):
297 if self == other_class:
298 return True
299 parent = self.super_class
300 if parent is None:
301 return False
302 else:
303 return parent.is_instance_of(other_class)
304
305 @property
306 def super_class(self):
307 if not self.parent_interface:
308 return None
309 else:
310 return model.interface_by_name(self.parent_interface)
311
312
313 def inherited_declaration(self, type_spec="?"):
314 if self.type_annotation:
315 return "%s<%s>" % (self.name, type_spec)
316 else:
317 return "%s" % self.name
318
319 @property
320 def type_variable(self):
321 if self.type_annotation:
322 return "<T>"
323 else:
324 return "";
325
Andreas Wundsam27303462013-07-16 12:52:35 -0700326 def class_info(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700327 """ return tuple of (package_prefix, parent_class) for the current JavaOFInterface"""
328 # FIXME: This duplicates inheritance information that is now available in the loxi_ir
329 # model (note, that the loxi model is on versioned classes). Should check/infer the
330 # inheritance information from the versioned lox_ir classes.
Andreas Wundsam001b1822013-08-02 22:25:55 -0700331 if re.match(r'OF.+StatsRequest$', self.name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700332 return ("", "OFStatsRequest", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700333 elif re.match(r'OF.+StatsReply$', self.name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700334 return ("", "OFStatsReply", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700335 elif re.match(r'OFFlow(Add|Modify(Strict)?|Delete(Strict)?)$', self.name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700336 return ("", "OFFlowMod", None)
Andreas Wundsama705c6b2013-09-04 11:21:09 -0700337 elif loxi_utils.class_is_message(self.c_name) and re.match(r'OFBsn.+$', self.name) and self.name != "OFBsnHeader":
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700338 return ("", "OFBsnHeader", None)
Andreas Wundsama705c6b2013-09-04 11:21:09 -0700339 elif loxi_utils.class_is_message(self.c_name) and re.match(r'OFNicira.+$', self.name) and self.name != "OFNiciraHeader":
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700340 return ("", "OFNiciraHeader", None)
Andreas Wundsam43526532013-08-01 22:03:50 -0700341 elif re.match(r'OFMatch.*', self.name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700342 return ("", "Match", None)
Andreas Wundsam5204de22013-07-30 11:34:45 -0700343 elif loxi_utils.class_is_message(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700344 return ("", "OFMessage", None)
Andreas Wundsam5204de22013-07-30 11:34:45 -0700345 elif loxi_utils.class_is_action(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700346 if re.match(r'OFActionBsn.+', self.name):
347 return ("action", "OFActionBsn", None)
348 elif re.match(r'OFActionNicira.+', self.name):
349 return ("action", "OFActionNicira", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700350 else:
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700351 return ("action", "OFAction", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700352 elif re.match(r'OFBsnVport.+$', self.name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700353 return ("", "OFBsnVport", None)
354 elif self.name == "OFOxm":
355 return ("oxm", None, "T extends OFValueType<T>")
Andreas Wundsam5204de22013-07-30 11:34:45 -0700356 elif loxi_utils.class_is_oxm(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700357 if self.name in model.oxm_map:
358 return ("oxm", "OFOxm<%s>" % model.oxm_map[self.name].type_name, None)
359 else:
360 return ("oxm", "OFOxm", None)
Andreas Wundsam5204de22013-07-30 11:34:45 -0700361 elif loxi_utils.class_is_instruction(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700362 return ("instruction", "OFInstruction", None)
Andreas Wundsam5204de22013-07-30 11:34:45 -0700363 elif loxi_utils.class_is_meter_band(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700364 return ("meterband", "OFMeterBand", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700365 elif loxi_utils.class_is_queue_prop(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700366 return ("queueprop", "OFQueueProp", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700367 elif loxi_utils.class_is_hello_elem(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700368 return ("", "OFHelloElem", None)
Andreas Wundsam27303462013-07-16 12:52:35 -0700369 else:
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700370 return ("", None, None)
371
372 @property
373 @memoize
374 def writeable_members(self):
375 return [ m for m in self.members if m.is_writeable ]
Andreas Wundsam27303462013-07-16 12:52:35 -0700376
377 @property
378 @memoize
379 def members(self):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700380 return self.ir_model_members + self.virtual_members
381
382 @property
383 @memoize
384 def ir_model_members(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700385 """return a list of all members to be exposed by this interface. Corresponds to
386 the union of the members of the vesioned classes without length, fieldlength
387 and pads (those are handled automatically during (de)serialization and not exposed"""
Andreas Wundsam27303462013-07-16 12:52:35 -0700388 all_versions = []
389 member_map = collections.OrderedDict()
390
391 for (version, of_class) in self.version_map.items():
392 for of_member in of_class.members:
393 if isinstance(of_member, OFLengthMember) or \
394 isinstance(of_member, OFFieldLengthMember) or \
395 isinstance(of_member, OFPadMember):
396 continue
397 if of_member.name not in member_map:
398 member_map[of_member.name] = JavaMember.for_of_member(self, of_member)
399
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700400 return tuple(member_map.values())
401
402 @property
403 def virtual_members(self):
404 if self.name == "OFOxm":
405 return (
406 JavaVirtualMember(self, "value", java_type.generic_t),
407 JavaVirtualMember(self, "mask", java_type.generic_t),
408 JavaVirtualMember(self, "matchField", java_type.make_match_field_jtype("T")),
409 JavaVirtualMember(self, "masked", java_type.boolean),
410 )
411 elif self.parent_interface and self.parent_interface.startswith("OFOxm"):
412 field_type = java_type.make_match_field_jtype(model.oxm_map[self.name].type_name) \
413 if self.name in model.oxm_map \
414 else java_type.make_match_field_jtype()
415
416 return (
417 JavaVirtualMember(self, "matchField", field_type),
418 JavaVirtualMember(self, "masked", java_type.boolean),
419 ) \
420 + \
421 (
422 ( JavaVirtualMember(self, "mask", find(lambda x: x.name == "value", self.ir_model_members).java_type), ) if not find(lambda x: x.name == "mask", self.ir_model_members) else
423 ()
424 )
425 else:
426 return ()
Andreas Wundsam27303462013-07-16 12:52:35 -0700427
428 @property
429 @memoize
430 def is_virtual(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700431 """ Is this interface virtual. If so, do not generate a builder interface """
Andreas Wundsam001b1822013-08-02 22:25:55 -0700432 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 -0700433
434 @property
Andreas Wundsam5204de22013-07-30 11:34:45 -0700435 def is_universal(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700436 """ Is this interface universal, i.e., does it exist in all OF versions? """
Andreas Wundsam5204de22013-07-30 11:34:45 -0700437 return len(self.all_versions) == len(model.versions)
438
439 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700440 @memoize
441 def all_versions(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700442 """ return list of all versions that this interface exists in """
Andreas Wundsam27303462013-07-16 12:52:35 -0700443 return self.version_map.keys()
444
Andreas Wundsam5204de22013-07-30 11:34:45 -0700445 def has_version(self, version):
446 return version in self.version_map
447
Andreas Wundsam27303462013-07-16 12:52:35 -0700448 def versioned_class(self, version):
449 return JavaOFClass(self, version, self.version_map[version])
450
451 @property
452 @memoize
453 def versioned_classes(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700454 return [ self.versioned_class(version) for version in self.all_versions ]
455
456#######################################################################
457### (Versioned) Classes
458#######################################################################
459
460class JavaOFClass(object):
461 """ Models an OpenFlow Message class for the purpose of the java class.
Andreas Wundsamd40ddbf2013-07-25 15:40:47 -0700462 Version specific child of a JavaOFInterface
Andreas Wundsam27303462013-07-16 12:52:35 -0700463 """
464 def __init__(self, interface, version, ir_class):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700465 """
466 @param interface JavaOFInterface instance of the parent interface
467 @param version JavaOFVersion
468 @param ir_class OFClass from loxi_ir
469 """
Andreas Wundsam27303462013-07-16 12:52:35 -0700470 self.interface = interface
471 self.ir_class = ir_class
472 self.c_name = self.ir_class.name
473 self.version = version
474 self.constant_name = self.c_name.upper().replace("OF_", "")
475 self.package = "org.openflow.protocol.ver%s" % version.of_version
Andreas Wundsame916d6f2013-07-30 11:33:58 -0700476 self.generated = False
477
478 @property
479 @memoize
480 def unit_test(self):
Yotam Harchol466b3212013-08-15 12:14:46 -0700481 return JavaUnitTestSet(self)
Andreas Wundsam27303462013-07-16 12:52:35 -0700482
483 @property
484 def name(self):
485 return "%sVer%s" % (self.interface.name, self.version.of_version)
486
487 @property
Andreas Wundsam5204de22013-07-30 11:34:45 -0700488 def variable_name(self):
489 return self.name[3:]
490
491 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700492 def length(self):
493 if self.is_fixed_length:
494 return self.min_length
495 else:
496 raise Exception("No fixed length for class %s, version %s" % (self.name, self.version))
497
498 @property
499 def min_length(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700500 """ @return the minimum wire length of an instance of this class in bytes """
Andreas Wundsam27303462013-07-16 12:52:35 -0700501 id_tuple = (self.ir_class.name, self.version.int_version)
502 return of_g.base_length[id_tuple] if id_tuple in of_g.base_length else -1
503
504 @property
505 def is_fixed_length(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700506 """ true iff this class serializes to a fixed length on the wire """
Andreas Wundsam27303462013-07-16 12:52:35 -0700507 return (self.ir_class.name, self.version.int_version) in of_g.is_fixed_length
508
509 def all_properties(self):
510 return self.interface.members
511
512 def get_member(self, name):
513 for m in self.members:
514 if m.name == name:
515 return m
516
517 @property
518 @memoize
519 def data_members(self):
520 return [ prop for prop in self.members if prop.is_data ]
521
522 @property
523 @memoize
524 def fixed_value_members(self):
525 return [ prop for prop in self.members if prop.is_fixed_value ]
526
527 @property
528 @memoize
529 def public_members(self):
530 return [ prop for prop in self.members if prop.is_public ]
531
532 @property
533 @memoize
534 def members(self):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700535 return self.ir_model_members + self.virtual_members
536
537 @property
538 def ir_model_members(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700539 members = [ JavaMember.for_of_member(self, of_member) for of_member in self.ir_class.members ]
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700540 return tuple(members)
541
542 @property
543 def virtual_members(self):
544 if self.interface.parent_interface and self.interface.parent_interface.startswith("OFOxm"):
545 if self.interface.name in model.oxm_map:
546 oxm_entry = model.oxm_map[self.interface.name]
547 return (
548 JavaVirtualMember(self, "matchField", java_type.make_match_field_jtype(oxm_entry.type_name), "MatchField.%s" % oxm_entry.value),
549 JavaVirtualMember(self, "masked", java_type.boolean, "true" if oxm_entry.masked else "false"),
550 )
551 else:
552 return (
553 JavaVirtualMember(self, "matchField", java_type.make_match_field_jtype(), "null"),
554 JavaVirtualMember(self, "masked", java_type.boolean, "false"),
555 )
556 else:
557 return ()
Andreas Wundsam27303462013-07-16 12:52:35 -0700558
559 def all_versions(self):
560 return [ JavaOFVersion(int_version)
561 for int_version in of_g.unified[self.c_name]
562 if int_version != 'union' and int_version != 'object_id' ]
563
564 def version_is_inherited(self, version):
565 return 'use_version' in of_g.unified[self.ir_class.name][version.int_version]
566
567 def inherited_from(self, version):
568 return JavaOFVersion(of_g.unified[self.ir_class.name][version.int_version]['use_version'])
569
570 @property
571 def is_virtual(self):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700572 return self.ir_class.virtual # type_maps.class_is_virtual(self.c_name) or self.ir_class.virtual
573
574 @property
575 def discriminator(self):
576 return find(lambda m: isinstance(m, OFDiscriminatorMember), self.ir_class.members)
Andreas Wundsam27303462013-07-16 12:52:35 -0700577
578 @property
579 def is_extension(self):
580 return type_maps.message_is_extension(self.c_name, -1)
581
Andreas Wundsam758c9cc2013-08-01 22:16:06 -0700582 @property
583 def align(self):
584 return int(self.ir_class.params['align']) if 'align' in self.ir_class.params else 0
585
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700586 @property
587 @memoize
588 def superclass(self):
589 return find(lambda c: c.version == self.version and c.c_name == self.ir_class.superclass, model.all_classes)
590
591 @property
592 @memoize
593 def subclasses(self):
594 return [ c for c in model.all_classes if c.version == self.version and c.ir_class.superclass == self.c_name ]
595
Andreas Wundsam27303462013-07-16 12:52:35 -0700596#######################################################################
597### Member
598#######################################################################
599
600
601class JavaMember(object):
602 """ Models a property (member) of an openflow class. """
603 def __init__(self, msg, name, java_type, member):
604 self.msg = msg
605 self.name = name
606 self.java_type = java_type
607 self.member = member
608 self.c_name = self.member.name if(hasattr(self.member, "name")) else ""
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700609
610 @property
611 def title_name(self):
612 return self.name[0].upper() + self.name[1:]
613
614 @property
615 def constant_name(self):
616 return self.c_name.upper()
617
618 @property
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700619 def getter_name(self):
620 return ("is" if self.java_type.public_type == "boolean" else "get") + self.title_name
621
622 @property
623 def setter_name(self):
624 return "set" + self.title_name
625
626 @property
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700627 def default_name(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700628 if self.is_fixed_value:
629 return self.constant_name
630 else:
631 return "DEFAULT_"+self.constant_name
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700632
633 @property
634 def default_value(self):
635 java_type = self.java_type.public_type;
636
Andreas Wundsam27303462013-07-16 12:52:35 -0700637 if self.is_fixed_value:
638 return self.enum_value
Andreas Wundsam880a2a82013-08-22 07:55:14 -0700639 elif java_type == "OFOxmList":
640 return "OFOxmList.EMPTY"
Andreas Wundsam27303462013-07-16 12:52:35 -0700641 elif re.match(r'List.*', java_type):
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700642 return "Collections.emptyList()"
643 elif java_type == "boolean":
644 return "false";
Andreas Wundsam880a2a82013-08-22 07:55:14 -0700645 elif self.java_type.is_array:
646 return "new %s[0]" % java_type[:-2]
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700647 elif java_type in ("byte", "char", "short", "int", "long"):
648 return "({0}) 0".format(java_type);
649 else:
650 return "null";
651
652 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700653 def enum_value(self):
654 if self.name == "version":
655 return "OFVersion.%s" % self.msg.version.constant_version
656
657 java_type = self.java_type.public_type;
658 try:
659 global model
660 enum = model.enum_by_name(java_type)
661 entry = enum.entry_by_version_value(self.msg.version, self.value)
662 return "%s.%s" % ( enum.name, entry.name)
663 except KeyError, e:
664 print e.message
665 return self.value
666
667 @property
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700668 def is_pad(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700669 return isinstance(self.member, OFPadMember)
670
671 def is_type_value(self, version=None):
672 if(version==None):
673 return any(self.is_type_value(version) for version in self.msg.all_versions)
674 try:
675 return self.c_name in get_type_values(self.msg.c_name, version.int_version)
676 except:
677 return False
678
679 @property
680 def is_field_length_value(self):
681 return isinstance(self.member, OFFieldLengthMember)
682
683 @property
Andreas Wundsam001b1822013-08-02 22:25:55 -0700684 def is_discriminator(self):
685 return isinstance(self.member, OFDiscriminatorMember)
686
687 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700688 def is_length_value(self):
689 return isinstance(self.member, OFLengthMember)
690
691 @property
692 def is_public(self):
693 return not (self.is_pad or self.is_length_value)
694
695 @property
696 def is_data(self):
697 return isinstance(self.member, OFDataMember) and self.name != "version"
698
699 @property
700 def is_fixed_value(self):
701 return hasattr(self.member, "value") or self.name == "version" \
702 or ( self.name == "length" and self.msg.is_fixed_length) \
703 or ( self.name == "len" and self.msg.is_fixed_length)
704
705 @property
706 def value(self):
707 if self.name == "version":
708 return self.msg.version.int_version
709 elif self.name == "length" or self.name == "len":
710 return self.msg.length
Andreas Wundsam27303462013-07-16 12:52:35 -0700711 else:
Andreas Wundsam001b1822013-08-02 22:25:55 -0700712 return self.java_type.format_value(self.member.value)
713
714 @property
715 def priv_value(self):
716 if self.name == "version":
717 return self.msg.version.int_version
718 elif self.name == "length" or self.name == "len":
719 return self.msg.length
720 else:
721 return self.java_type.format_value(self.member.value, pub_type=False)
722
Andreas Wundsam27303462013-07-16 12:52:35 -0700723
724 @property
725 def is_writeable(self):
726 return self.is_data and not self.name in model.write_blacklist[self.msg.name]
727
728 def get_type_value_info(self, version):
729 return get_type_values(msg.c_name, version.int_version)[self.c_name]
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700730
731 @property
732 def length(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700733 if hasattr(self.member, "length"):
734 return self.member.length
735 else:
Andreas Wundsam5204de22013-07-30 11:34:45 -0700736 count, base = loxi_utils.type_dec_to_count_base(self.member.type)
Andreas Wundsam27303462013-07-16 12:52:35 -0700737 return of_g.of_base_types[base]['bytes'] * count
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700738
739 @staticmethod
Andreas Wundsam27303462013-07-16 12:52:35 -0700740 def for_of_member(java_class, member):
741 if isinstance(member, OFPadMember):
742 return JavaMember(None, "", None, member)
743 else:
744 if member.name == 'len':
745 name = 'length'
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700746 elif member.name == 'value_mask':
747 name = 'mask'
Andreas Wundsam27303462013-07-16 12:52:35 -0700748 else:
749 name = java_type.name_c_to_camel(member.name)
750 j_type = java_type.convert_to_jtype(java_class.c_name, member.name, member.oftype)
751 return JavaMember(java_class, name, j_type, member)
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700752
753 @property
754 def is_universal(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700755 if not self.msg.c_name in of_g.unified:
756 print("%s not self.unified" % self.msg.c_name)
757 return False
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700758 for version in of_g.unified[self.msg.c_name]:
759 if version == 'union' or version =='object_id':
760 continue
761 if 'use_version' in of_g.unified[self.msg.c_name][version]:
762 continue
763
Andreas Wundsam27303462013-07-16 12:52:35 -0700764 if not self.member.name in (f['name'] for f in of_g.unified[self.msg.c_name][version]['members']):
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700765 return False
766 return True
767
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700768 @property
769 def is_virtual(self):
770 return False
771
Andreas Wundsam27303462013-07-16 12:52:35 -0700772 def __hash__(self):
773 return hash(self.name)
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700774
Andreas Wundsam27303462013-07-16 12:52:35 -0700775 def __eq__(self, other):
776 if other is None or type(self) != type(other):
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700777 return False
Andreas Wundsam27303462013-07-16 12:52:35 -0700778 return (self.name,) == (other.name,)
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700779
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700780class JavaVirtualMember(JavaMember):
781 """ Models a virtual property (member) of an openflow class that is not backed by a loxi ir member """
782 def __init__(self, msg, name, java_type, value=None):
783 JavaMember.__init__(self, msg, name, java_type, member=None)
784 self._value = value
785
786 @property
787 def is_fixed_value(self):
788 return True
789
790 @property
791 def value(self):
792 return self._value
793
794 @property
795 def priv_value(self):
796 return self._value
797
798
799 @property
800 def is_universal(self):
801 return True
802
803 @property
804 def is_virtual(self):
805 return True
Andreas Wundsame916d6f2013-07-30 11:33:58 -0700806
807#######################################################################
808### Unit Test
809#######################################################################
810
Yotam Harchol466b3212013-08-15 12:14:46 -0700811class JavaUnitTestSet(object):
Andreas Wundsame916d6f2013-07-30 11:33:58 -0700812 def __init__(self, java_class):
813 self.java_class = java_class
Yotam Harchol466b3212013-08-15 12:14:46 -0700814 first_data_file_name = "of{version}/{name}.data".format(version=java_class.version.of_version,
Andreas Wundsame916d6f2013-07-30 11:33:58 -0700815 name=java_class.c_name[3:])
Yotam Harchol466b3212013-08-15 12:14:46 -0700816 data_file_template = "of{version}/{name}.".format(version=java_class.version.of_version,
817 name=java_class.c_name[3:]) + "{i}.data"
818 test_class_name = self.java_class.name + "Test"
819 self.test_units = []
820 if test_data.exists(first_data_file_name):
821 self.test_units.append(JavaUnitTest(java_class, first_data_file_name, test_class_name))
822 i = 1
823 while test_data.exists(data_file_template.format(i=i)):
824 self.test_units.append(JavaUnitTest(java_class, data_file_template.format(i=i), test_class_name + str(i)))
825 i = i + 1
826
827 @property
828 def package(self):
829 return self.java_class.package
830
831 @property
832 def has_test_data(self):
833 return len(self.test_units) > 0
834
835 @property
836 def length(self):
837 return len(self.test_units)
838
839 def get_test_unit(self, i):
840 return self.test_units[i]
841
842
843class JavaUnitTest(object):
844 def __init__(self, java_class, file_name=None, test_class_name=None):
845 self.java_class = java_class
846 if file_name is None:
847 self.data_file_name = "of{version}/{name}.data".format(version=java_class.version.of_version,
848 name=java_class.c_name[3:])
849 else:
850 self.data_file_name = file_name
851 if test_class_name is None:
852 self.test_class_name = self.java_class.name + "Test"
853 else:
854 self.test_class_name = test_class_name
855
Andreas Wundsame916d6f2013-07-30 11:33:58 -0700856 @property
857 def package(self):
858 return self.java_class.package
859
860 @property
861 def name(self):
Yotam Harchol466b3212013-08-15 12:14:46 -0700862 return self.test_class_name
Andreas Wundsame916d6f2013-07-30 11:33:58 -0700863
864 @property
865 def has_test_data(self):
866 return test_data.exists(self.data_file_name)
867
868 @property
869 @memoize
870 def test_data(self):
871 return test_data.read(self.data_file_name)
872
873
Andreas Wundsam27303462013-07-16 12:52:35 -0700874#######################################################################
875### Enums
876#######################################################################
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700877
Andreas Wundsam27303462013-07-16 12:52:35 -0700878class JavaEnum(object):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700879 def __init__(self, c_name, version_enum_map):
Andreas Wundsam27303462013-07-16 12:52:35 -0700880 self.c_name = c_name
Andreas Wundsambe168f72013-08-03 22:49:35 -0700881
882 if c_name == "of_stats_types":
883 self.name = "OFStatsType"
884 else:
885 self.name = "OF" + java_type.name_c_to_caps_camel("_".join(c_name.split("_")[1:]))
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700886
Andreas Wundsam27303462013-07-16 12:52:35 -0700887 # Port_features has constants that start with digits
888 self.name_prefix = "PF_" if self.name == "OFPortFeatures" else ""
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700889
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700890 self.version_enums = version_enum_map
891
892 entry_name_version_value_map = OrderedDefaultDict(lambda: OrderedDict())
893 for version, ir_enum in version_enum_map.items():
894 for ir_entry in ir_enum.entries:
895 if "virtual" in ir_entry.params:
896 continue
897 entry_name_version_value_map[ir_entry.name][version] = ir_entry.value
898
Andreas Wundsam27303462013-07-16 12:52:35 -0700899 self.entries = [ JavaEnumEntry(self, name, version_value_map)
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700900 for (name, version_value_map) in entry_name_version_value_map.items() ]
Andreas Wundsam43526532013-08-01 22:03:50 -0700901
902 self.entries = [ e for e in self.entries if e.name not in model.enum_entry_blacklist[self.name] ]
Andreas Wundsam27303462013-07-16 12:52:35 -0700903 self.package = "org.openflow.protocol"
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700904
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700905 def wire_type(self, version):
906 ir_enum = self.version_enums[version]
907 if "wire_type" in ir_enum.params:
908 return java_type.convert_enum_wire_type_to_jtype(ir_enum.params["wire_type"])
909 else:
910 return java_type.u8
911
912 @property
913 def versions(self):
914 return self.version_enums.keys()
915
Andreas Wundsam27303462013-07-16 12:52:35 -0700916 @memoize
917 def entry_by_name(self, name):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700918 res = find(lambda e: e.name == name, self.entries)
919 if res:
920 return res
921 else:
Andreas Wundsam27303462013-07-16 12:52:35 -0700922 raise KeyError("Enum %s: no entry with name %s" % (self.name, name))
923
924 @memoize
925 def entry_by_c_name(self, name):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700926 res = find(lambda e: e.c_name == name, self.entries)
927 if res:
928 return res
929 else:
Andreas Wundsam27303462013-07-16 12:52:35 -0700930 raise KeyError("Enum %s: no entry with c_name %s" % (self.name, name))
931
932 @memoize
933 def entry_by_version_value(self, version, value):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700934 res = find(lambda e: e.values[version] == value if version in e.values else False, self.entries)
935 if res:
936 return res
937 else:
Andreas Wundsam27303462013-07-16 12:52:35 -0700938 raise KeyError("Enum %s: no entry with version %s, value %s" % (self.name, version, value))
939
940# values: Map JavaVersion->Value
941class JavaEnumEntry(object):
942 def __init__(self, enum, name, values):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700943 self.enum = enum
Andreas Wundsam27303462013-07-16 12:52:35 -0700944 self.name = enum.name_prefix + "_".join(name.split("_")[1:]).upper()
945 self.values = values
946
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700947 def has_value(self, version):
948 return version in self.values
949
Andreas Wundsam27303462013-07-16 12:52:35 -0700950 def value(self, version):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700951 return self.values[version]
952
953 def format_value(self, version):
954 res = self.enum.wire_type(version).format_value(self.values[version])
Andreas Wundsam27303462013-07-16 12:52:35 -0700955 return res
956
Andreas Wundsam27303462013-07-16 12:52:35 -0700957 def all_values(self, versions, not_present=None):
958 return [ self.values[version] if version in self.values else not_present for version in versions ]