blob: d7f7370824f4a9aa8f4ca2ba52e9147be555ad52 [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 Wundsam27303462013-07-16 12:52:35 -0700271 self.package = "org.openflow.protocol.%s" % pck_suffix if pck_suffix else "org.openflow.protocol"
272 if self.name != parent_interface:
273 self.parent_interface = parent_interface
274 else:
275 self.parent_interface = None
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700276
277 def is_instance_of(self, other_class):
278 if self == other_class:
279 return True
280 parent = self.super_class
281 if parent is None:
282 return False
283 else:
284 return parent.is_instance_of(other_class)
285
286 @property
287 def super_class(self):
288 if not self.parent_interface:
289 return None
290 else:
291 return model.interface_by_name(self.parent_interface)
292
293
294 def inherited_declaration(self, type_spec="?"):
295 if self.type_annotation:
296 return "%s<%s>" % (self.name, type_spec)
297 else:
298 return "%s" % self.name
299
300 @property
301 def type_variable(self):
302 if self.type_annotation:
303 return "<T>"
304 else:
305 return "";
306
Andreas Wundsam27303462013-07-16 12:52:35 -0700307 def class_info(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700308 """ return tuple of (package_prefix, parent_class) for the current JavaOFInterface"""
309 # FIXME: This duplicates inheritance information that is now available in the loxi_ir
310 # model (note, that the loxi model is on versioned classes). Should check/infer the
311 # inheritance information from the versioned lox_ir classes.
Andreas Wundsam001b1822013-08-02 22:25:55 -0700312 if re.match(r'OF.+StatsRequest$', self.name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700313 return ("", "OFStatsRequest", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700314 elif re.match(r'OF.+StatsReply$', self.name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700315 return ("", "OFStatsReply", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700316 elif re.match(r'OFFlow(Add|Modify(Strict)?|Delete(Strict)?)$', self.name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700317 return ("", "OFFlowMod", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700318 elif loxi_utils.class_is_message(self.c_name) and re.match(r'OFBsn.+$', self.name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700319 return ("", "OFBsnHeader", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700320 elif loxi_utils.class_is_message(self.c_name) and re.match(r'OFNicira.+$', self.name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700321 return ("", "OFNiciraHeader", None)
Andreas Wundsam43526532013-08-01 22:03:50 -0700322 elif re.match(r'OFMatch.*', self.name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700323 return ("", "Match", None)
Andreas Wundsam5204de22013-07-30 11:34:45 -0700324 elif loxi_utils.class_is_message(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700325 return ("", "OFMessage", None)
Andreas Wundsam5204de22013-07-30 11:34:45 -0700326 elif loxi_utils.class_is_action(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700327 if re.match(r'OFActionBsn.+', self.name):
328 return ("action", "OFActionBsn", None)
329 elif re.match(r'OFActionNicira.+', self.name):
330 return ("action", "OFActionNicira", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700331 else:
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700332 return ("action", "OFAction", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700333 elif re.match(r'OFBsnVport.+$', self.name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700334 return ("", "OFBsnVport", None)
335 elif self.name == "OFOxm":
336 return ("oxm", None, "T extends OFValueType<T>")
Andreas Wundsam5204de22013-07-30 11:34:45 -0700337 elif loxi_utils.class_is_oxm(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700338 if self.name in model.oxm_map:
339 return ("oxm", "OFOxm<%s>" % model.oxm_map[self.name].type_name, None)
340 else:
341 return ("oxm", "OFOxm", None)
Andreas Wundsam5204de22013-07-30 11:34:45 -0700342 elif loxi_utils.class_is_instruction(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700343 return ("instruction", "OFInstruction", None)
Andreas Wundsam5204de22013-07-30 11:34:45 -0700344 elif loxi_utils.class_is_meter_band(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700345 return ("meterband", "OFMeterBand", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700346 elif loxi_utils.class_is_queue_prop(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700347 return ("queueprop", "OFQueueProp", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700348 elif loxi_utils.class_is_hello_elem(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700349 return ("", "OFHelloElem", None)
Andreas Wundsam27303462013-07-16 12:52:35 -0700350 else:
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700351 return ("", None, None)
352
353 @property
354 @memoize
355 def writeable_members(self):
356 return [ m for m in self.members if m.is_writeable ]
Andreas Wundsam27303462013-07-16 12:52:35 -0700357
358 @property
359 @memoize
360 def members(self):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700361 return self.ir_model_members + self.virtual_members
362
363 @property
364 @memoize
365 def ir_model_members(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700366 """return a list of all members to be exposed by this interface. Corresponds to
367 the union of the members of the vesioned classes without length, fieldlength
368 and pads (those are handled automatically during (de)serialization and not exposed"""
Andreas Wundsam27303462013-07-16 12:52:35 -0700369 all_versions = []
370 member_map = collections.OrderedDict()
371
372 for (version, of_class) in self.version_map.items():
373 for of_member in of_class.members:
374 if isinstance(of_member, OFLengthMember) or \
375 isinstance(of_member, OFFieldLengthMember) or \
376 isinstance(of_member, OFPadMember):
377 continue
378 if of_member.name not in member_map:
379 member_map[of_member.name] = JavaMember.for_of_member(self, of_member)
380
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700381 return tuple(member_map.values())
382
383 @property
384 def virtual_members(self):
385 if self.name == "OFOxm":
386 return (
387 JavaVirtualMember(self, "value", java_type.generic_t),
388 JavaVirtualMember(self, "mask", java_type.generic_t),
389 JavaVirtualMember(self, "matchField", java_type.make_match_field_jtype("T")),
390 JavaVirtualMember(self, "masked", java_type.boolean),
391 )
392 elif self.parent_interface and self.parent_interface.startswith("OFOxm"):
393 field_type = java_type.make_match_field_jtype(model.oxm_map[self.name].type_name) \
394 if self.name in model.oxm_map \
395 else java_type.make_match_field_jtype()
396
397 return (
398 JavaVirtualMember(self, "matchField", field_type),
399 JavaVirtualMember(self, "masked", java_type.boolean),
400 ) \
401 + \
402 (
403 ( 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
404 ()
405 )
406 else:
407 return ()
Andreas Wundsam27303462013-07-16 12:52:35 -0700408
409 @property
410 @memoize
411 def is_virtual(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700412 """ Is this interface virtual. If so, do not generate a builder interface """
Andreas Wundsam001b1822013-08-02 22:25:55 -0700413 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 -0700414
415 @property
Andreas Wundsam5204de22013-07-30 11:34:45 -0700416 def is_universal(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700417 """ Is this interface universal, i.e., does it exist in all OF versions? """
Andreas Wundsam5204de22013-07-30 11:34:45 -0700418 return len(self.all_versions) == len(model.versions)
419
420 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700421 @memoize
422 def all_versions(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700423 """ return list of all versions that this interface exists in """
Andreas Wundsam27303462013-07-16 12:52:35 -0700424 return self.version_map.keys()
425
Andreas Wundsam5204de22013-07-30 11:34:45 -0700426 def has_version(self, version):
427 return version in self.version_map
428
Andreas Wundsam27303462013-07-16 12:52:35 -0700429 def versioned_class(self, version):
430 return JavaOFClass(self, version, self.version_map[version])
431
432 @property
433 @memoize
434 def versioned_classes(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700435 return [ self.versioned_class(version) for version in self.all_versions ]
436
437#######################################################################
438### (Versioned) Classes
439#######################################################################
440
441class JavaOFClass(object):
442 """ Models an OpenFlow Message class for the purpose of the java class.
Andreas Wundsamd40ddbf2013-07-25 15:40:47 -0700443 Version specific child of a JavaOFInterface
Andreas Wundsam27303462013-07-16 12:52:35 -0700444 """
445 def __init__(self, interface, version, ir_class):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700446 """
447 @param interface JavaOFInterface instance of the parent interface
448 @param version JavaOFVersion
449 @param ir_class OFClass from loxi_ir
450 """
Andreas Wundsam27303462013-07-16 12:52:35 -0700451 self.interface = interface
452 self.ir_class = ir_class
453 self.c_name = self.ir_class.name
454 self.version = version
455 self.constant_name = self.c_name.upper().replace("OF_", "")
456 self.package = "org.openflow.protocol.ver%s" % version.of_version
Andreas Wundsame916d6f2013-07-30 11:33:58 -0700457 self.generated = False
458
459 @property
460 @memoize
461 def unit_test(self):
Yotam Harchol466b3212013-08-15 12:14:46 -0700462 return JavaUnitTestSet(self)
Andreas Wundsam27303462013-07-16 12:52:35 -0700463
464 @property
465 def name(self):
466 return "%sVer%s" % (self.interface.name, self.version.of_version)
467
468 @property
Andreas Wundsam5204de22013-07-30 11:34:45 -0700469 def variable_name(self):
470 return self.name[3:]
471
472 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700473 def length(self):
474 if self.is_fixed_length:
475 return self.min_length
476 else:
477 raise Exception("No fixed length for class %s, version %s" % (self.name, self.version))
478
479 @property
480 def min_length(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700481 """ @return the minimum wire length of an instance of this class in bytes """
Andreas Wundsam27303462013-07-16 12:52:35 -0700482 id_tuple = (self.ir_class.name, self.version.int_version)
483 return of_g.base_length[id_tuple] if id_tuple in of_g.base_length else -1
484
485 @property
486 def is_fixed_length(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700487 """ true iff this class serializes to a fixed length on the wire """
Andreas Wundsam27303462013-07-16 12:52:35 -0700488 return (self.ir_class.name, self.version.int_version) in of_g.is_fixed_length
489
490 def all_properties(self):
491 return self.interface.members
492
493 def get_member(self, name):
494 for m in self.members:
495 if m.name == name:
496 return m
497
498 @property
499 @memoize
500 def data_members(self):
501 return [ prop for prop in self.members if prop.is_data ]
502
503 @property
504 @memoize
505 def fixed_value_members(self):
506 return [ prop for prop in self.members if prop.is_fixed_value ]
507
508 @property
509 @memoize
510 def public_members(self):
511 return [ prop for prop in self.members if prop.is_public ]
512
513 @property
514 @memoize
515 def members(self):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700516 return self.ir_model_members + self.virtual_members
517
518 @property
519 def ir_model_members(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700520 members = [ JavaMember.for_of_member(self, of_member) for of_member in self.ir_class.members ]
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700521 return tuple(members)
522
523 @property
524 def virtual_members(self):
525 if self.interface.parent_interface and self.interface.parent_interface.startswith("OFOxm"):
526 if self.interface.name in model.oxm_map:
527 oxm_entry = model.oxm_map[self.interface.name]
528 return (
529 JavaVirtualMember(self, "matchField", java_type.make_match_field_jtype(oxm_entry.type_name), "MatchField.%s" % oxm_entry.value),
530 JavaVirtualMember(self, "masked", java_type.boolean, "true" if oxm_entry.masked else "false"),
531 )
532 else:
533 return (
534 JavaVirtualMember(self, "matchField", java_type.make_match_field_jtype(), "null"),
535 JavaVirtualMember(self, "masked", java_type.boolean, "false"),
536 )
537 else:
538 return ()
Andreas Wundsam27303462013-07-16 12:52:35 -0700539
540 def all_versions(self):
541 return [ JavaOFVersion(int_version)
542 for int_version in of_g.unified[self.c_name]
543 if int_version != 'union' and int_version != 'object_id' ]
544
545 def version_is_inherited(self, version):
546 return 'use_version' in of_g.unified[self.ir_class.name][version.int_version]
547
548 def inherited_from(self, version):
549 return JavaOFVersion(of_g.unified[self.ir_class.name][version.int_version]['use_version'])
550
551 @property
552 def is_virtual(self):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700553 return self.ir_class.virtual # type_maps.class_is_virtual(self.c_name) or self.ir_class.virtual
554
555 @property
556 def discriminator(self):
557 return find(lambda m: isinstance(m, OFDiscriminatorMember), self.ir_class.members)
Andreas Wundsam27303462013-07-16 12:52:35 -0700558
559 @property
560 def is_extension(self):
561 return type_maps.message_is_extension(self.c_name, -1)
562
Andreas Wundsam758c9cc2013-08-01 22:16:06 -0700563 @property
564 def align(self):
565 return int(self.ir_class.params['align']) if 'align' in self.ir_class.params else 0
566
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700567 @property
568 @memoize
569 def superclass(self):
570 return find(lambda c: c.version == self.version and c.c_name == self.ir_class.superclass, model.all_classes)
571
572 @property
573 @memoize
574 def subclasses(self):
575 return [ c for c in model.all_classes if c.version == self.version and c.ir_class.superclass == self.c_name ]
576
Andreas Wundsam27303462013-07-16 12:52:35 -0700577#######################################################################
578### Member
579#######################################################################
580
581
582class JavaMember(object):
583 """ Models a property (member) of an openflow class. """
584 def __init__(self, msg, name, java_type, member):
585 self.msg = msg
586 self.name = name
587 self.java_type = java_type
588 self.member = member
589 self.c_name = self.member.name if(hasattr(self.member, "name")) else ""
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700590
591 @property
592 def title_name(self):
593 return self.name[0].upper() + self.name[1:]
594
595 @property
596 def constant_name(self):
597 return self.c_name.upper()
598
599 @property
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700600 def getter_name(self):
601 return ("is" if self.java_type.public_type == "boolean" else "get") + self.title_name
602
603 @property
604 def setter_name(self):
605 return "set" + self.title_name
606
607 @property
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700608 def default_name(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700609 if self.is_fixed_value:
610 return self.constant_name
611 else:
612 return "DEFAULT_"+self.constant_name
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700613
614 @property
615 def default_value(self):
616 java_type = self.java_type.public_type;
617
Andreas Wundsam27303462013-07-16 12:52:35 -0700618 if self.is_fixed_value:
619 return self.enum_value
620 elif re.match(r'List.*', java_type):
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700621 return "Collections.emptyList()"
622 elif java_type == "boolean":
623 return "false";
624 elif java_type in ("byte", "char", "short", "int", "long"):
625 return "({0}) 0".format(java_type);
626 else:
627 return "null";
628
629 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700630 def enum_value(self):
631 if self.name == "version":
632 return "OFVersion.%s" % self.msg.version.constant_version
633
634 java_type = self.java_type.public_type;
635 try:
636 global model
637 enum = model.enum_by_name(java_type)
638 entry = enum.entry_by_version_value(self.msg.version, self.value)
639 return "%s.%s" % ( enum.name, entry.name)
640 except KeyError, e:
641 print e.message
642 return self.value
643
644 @property
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700645 def is_pad(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700646 return isinstance(self.member, OFPadMember)
647
648 def is_type_value(self, version=None):
649 if(version==None):
650 return any(self.is_type_value(version) for version in self.msg.all_versions)
651 try:
652 return self.c_name in get_type_values(self.msg.c_name, version.int_version)
653 except:
654 return False
655
656 @property
657 def is_field_length_value(self):
658 return isinstance(self.member, OFFieldLengthMember)
659
660 @property
Andreas Wundsam001b1822013-08-02 22:25:55 -0700661 def is_discriminator(self):
662 return isinstance(self.member, OFDiscriminatorMember)
663
664 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700665 def is_length_value(self):
666 return isinstance(self.member, OFLengthMember)
667
668 @property
669 def is_public(self):
670 return not (self.is_pad or self.is_length_value)
671
672 @property
673 def is_data(self):
674 return isinstance(self.member, OFDataMember) and self.name != "version"
675
676 @property
677 def is_fixed_value(self):
678 return hasattr(self.member, "value") or self.name == "version" \
679 or ( self.name == "length" and self.msg.is_fixed_length) \
680 or ( self.name == "len" and self.msg.is_fixed_length)
681
682 @property
683 def value(self):
684 if self.name == "version":
685 return self.msg.version.int_version
686 elif self.name == "length" or self.name == "len":
687 return self.msg.length
Andreas Wundsam27303462013-07-16 12:52:35 -0700688 else:
Andreas Wundsam001b1822013-08-02 22:25:55 -0700689 return self.java_type.format_value(self.member.value)
690
691 @property
692 def priv_value(self):
693 if self.name == "version":
694 return self.msg.version.int_version
695 elif self.name == "length" or self.name == "len":
696 return self.msg.length
697 else:
698 return self.java_type.format_value(self.member.value, pub_type=False)
699
Andreas Wundsam27303462013-07-16 12:52:35 -0700700
701 @property
702 def is_writeable(self):
703 return self.is_data and not self.name in model.write_blacklist[self.msg.name]
704
705 def get_type_value_info(self, version):
706 return get_type_values(msg.c_name, version.int_version)[self.c_name]
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700707
708 @property
709 def length(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700710 if hasattr(self.member, "length"):
711 return self.member.length
712 else:
Andreas Wundsam5204de22013-07-30 11:34:45 -0700713 count, base = loxi_utils.type_dec_to_count_base(self.member.type)
Andreas Wundsam27303462013-07-16 12:52:35 -0700714 return of_g.of_base_types[base]['bytes'] * count
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700715
716 @staticmethod
Andreas Wundsam27303462013-07-16 12:52:35 -0700717 def for_of_member(java_class, member):
718 if isinstance(member, OFPadMember):
719 return JavaMember(None, "", None, member)
720 else:
721 if member.name == 'len':
722 name = 'length'
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700723 elif member.name == 'value_mask':
724 name = 'mask'
Andreas Wundsam27303462013-07-16 12:52:35 -0700725 else:
726 name = java_type.name_c_to_camel(member.name)
727 j_type = java_type.convert_to_jtype(java_class.c_name, member.name, member.oftype)
728 return JavaMember(java_class, name, j_type, member)
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700729
730 @property
731 def is_universal(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700732 if not self.msg.c_name in of_g.unified:
733 print("%s not self.unified" % self.msg.c_name)
734 return False
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700735 for version in of_g.unified[self.msg.c_name]:
736 if version == 'union' or version =='object_id':
737 continue
738 if 'use_version' in of_g.unified[self.msg.c_name][version]:
739 continue
740
Andreas Wundsam27303462013-07-16 12:52:35 -0700741 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 -0700742 return False
743 return True
744
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700745 @property
746 def is_virtual(self):
747 return False
748
Andreas Wundsam27303462013-07-16 12:52:35 -0700749 def __hash__(self):
750 return hash(self.name)
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700751
Andreas Wundsam27303462013-07-16 12:52:35 -0700752 def __eq__(self, other):
753 if other is None or type(self) != type(other):
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700754 return False
Andreas Wundsam27303462013-07-16 12:52:35 -0700755 return (self.name,) == (other.name,)
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700756
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700757class JavaVirtualMember(JavaMember):
758 """ Models a virtual property (member) of an openflow class that is not backed by a loxi ir member """
759 def __init__(self, msg, name, java_type, value=None):
760 JavaMember.__init__(self, msg, name, java_type, member=None)
761 self._value = value
762
763 @property
764 def is_fixed_value(self):
765 return True
766
767 @property
768 def value(self):
769 return self._value
770
771 @property
772 def priv_value(self):
773 return self._value
774
775
776 @property
777 def is_universal(self):
778 return True
779
780 @property
781 def is_virtual(self):
782 return True
Andreas Wundsame916d6f2013-07-30 11:33:58 -0700783
784#######################################################################
785### Unit Test
786#######################################################################
787
Yotam Harchol466b3212013-08-15 12:14:46 -0700788class JavaUnitTestSet(object):
Andreas Wundsame916d6f2013-07-30 11:33:58 -0700789 def __init__(self, java_class):
790 self.java_class = java_class
Yotam Harchol466b3212013-08-15 12:14:46 -0700791 first_data_file_name = "of{version}/{name}.data".format(version=java_class.version.of_version,
Andreas Wundsame916d6f2013-07-30 11:33:58 -0700792 name=java_class.c_name[3:])
Yotam Harchol466b3212013-08-15 12:14:46 -0700793 data_file_template = "of{version}/{name}.".format(version=java_class.version.of_version,
794 name=java_class.c_name[3:]) + "{i}.data"
795 test_class_name = self.java_class.name + "Test"
796 self.test_units = []
797 if test_data.exists(first_data_file_name):
798 self.test_units.append(JavaUnitTest(java_class, first_data_file_name, test_class_name))
799 i = 1
800 while test_data.exists(data_file_template.format(i=i)):
801 self.test_units.append(JavaUnitTest(java_class, data_file_template.format(i=i), test_class_name + str(i)))
802 i = i + 1
803
804 @property
805 def package(self):
806 return self.java_class.package
807
808 @property
809 def has_test_data(self):
810 return len(self.test_units) > 0
811
812 @property
813 def length(self):
814 return len(self.test_units)
815
816 def get_test_unit(self, i):
817 return self.test_units[i]
818
819
820class JavaUnitTest(object):
821 def __init__(self, java_class, file_name=None, test_class_name=None):
822 self.java_class = java_class
823 if file_name is None:
824 self.data_file_name = "of{version}/{name}.data".format(version=java_class.version.of_version,
825 name=java_class.c_name[3:])
826 else:
827 self.data_file_name = file_name
828 if test_class_name is None:
829 self.test_class_name = self.java_class.name + "Test"
830 else:
831 self.test_class_name = test_class_name
832
Andreas Wundsame916d6f2013-07-30 11:33:58 -0700833 @property
834 def package(self):
835 return self.java_class.package
836
837 @property
838 def name(self):
Yotam Harchol466b3212013-08-15 12:14:46 -0700839 return self.test_class_name
Andreas Wundsame916d6f2013-07-30 11:33:58 -0700840
841 @property
842 def has_test_data(self):
843 return test_data.exists(self.data_file_name)
844
845 @property
846 @memoize
847 def test_data(self):
848 return test_data.read(self.data_file_name)
849
850
Andreas Wundsam27303462013-07-16 12:52:35 -0700851#######################################################################
852### Enums
853#######################################################################
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700854
Andreas Wundsam27303462013-07-16 12:52:35 -0700855class JavaEnum(object):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700856 def __init__(self, c_name, version_enum_map):
Andreas Wundsam27303462013-07-16 12:52:35 -0700857 self.c_name = c_name
Andreas Wundsambe168f72013-08-03 22:49:35 -0700858
859 if c_name == "of_stats_types":
860 self.name = "OFStatsType"
861 else:
862 self.name = "OF" + java_type.name_c_to_caps_camel("_".join(c_name.split("_")[1:]))
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700863
Andreas Wundsam27303462013-07-16 12:52:35 -0700864 # Port_features has constants that start with digits
865 self.name_prefix = "PF_" if self.name == "OFPortFeatures" else ""
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700866
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700867 self.version_enums = version_enum_map
868
869 entry_name_version_value_map = OrderedDefaultDict(lambda: OrderedDict())
870 for version, ir_enum in version_enum_map.items():
871 for ir_entry in ir_enum.entries:
872 if "virtual" in ir_entry.params:
873 continue
874 entry_name_version_value_map[ir_entry.name][version] = ir_entry.value
875
Andreas Wundsam27303462013-07-16 12:52:35 -0700876 self.entries = [ JavaEnumEntry(self, name, version_value_map)
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700877 for (name, version_value_map) in entry_name_version_value_map.items() ]
Andreas Wundsam43526532013-08-01 22:03:50 -0700878
879 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 -0700880 self.package = "org.openflow.protocol"
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700881
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700882 def wire_type(self, version):
883 ir_enum = self.version_enums[version]
884 if "wire_type" in ir_enum.params:
885 return java_type.convert_enum_wire_type_to_jtype(ir_enum.params["wire_type"])
886 else:
887 return java_type.u8
888
889 @property
890 def versions(self):
891 return self.version_enums.keys()
892
Andreas Wundsam27303462013-07-16 12:52:35 -0700893 @memoize
894 def entry_by_name(self, name):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700895 res = find(lambda e: e.name == name, self.entries)
896 if res:
897 return res
898 else:
Andreas Wundsam27303462013-07-16 12:52:35 -0700899 raise KeyError("Enum %s: no entry with name %s" % (self.name, name))
900
901 @memoize
902 def entry_by_c_name(self, name):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700903 res = find(lambda e: e.c_name == name, self.entries)
904 if res:
905 return res
906 else:
Andreas Wundsam27303462013-07-16 12:52:35 -0700907 raise KeyError("Enum %s: no entry with c_name %s" % (self.name, name))
908
909 @memoize
910 def entry_by_version_value(self, version, value):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700911 res = find(lambda e: e.values[version] == value if version in e.values else False, self.entries)
912 if res:
913 return res
914 else:
Andreas Wundsam27303462013-07-16 12:52:35 -0700915 raise KeyError("Enum %s: no entry with version %s, value %s" % (self.name, version, value))
916
917# values: Map JavaVersion->Value
918class JavaEnumEntry(object):
919 def __init__(self, enum, name, values):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700920 self.enum = enum
Andreas Wundsam27303462013-07-16 12:52:35 -0700921 self.name = enum.name_prefix + "_".join(name.split("_")[1:]).upper()
922 self.values = values
923
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700924 def has_value(self, version):
925 return version in self.values
926
Andreas Wundsam27303462013-07-16 12:52:35 -0700927 def value(self, version):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700928 return self.values[version]
929
930 def format_value(self, version):
931 res = self.enum.wire_type(version).format_value(self.values[version])
Andreas Wundsam27303462013-07-16 12:52:35 -0700932 return res
933
Andreas Wundsam27303462013-07-16 12:52:35 -0700934 def all_values(self, versions, not_present=None):
935 return [ self.values[version] if version in self.values else not_present for version in versions ]