blob: 944b536852413e71e31fc2e2d12be99f2b80b94d [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
Andreas Wundsam880a2a82013-08-22 07:55:14 -0700620 elif java_type == "OFOxmList":
621 return "OFOxmList.EMPTY"
Andreas Wundsam27303462013-07-16 12:52:35 -0700622 elif re.match(r'List.*', java_type):
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700623 return "Collections.emptyList()"
624 elif java_type == "boolean":
625 return "false";
Andreas Wundsam880a2a82013-08-22 07:55:14 -0700626 elif self.java_type.is_array:
627 return "new %s[0]" % java_type[:-2]
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700628 elif java_type in ("byte", "char", "short", "int", "long"):
629 return "({0}) 0".format(java_type);
630 else:
631 return "null";
632
633 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700634 def enum_value(self):
635 if self.name == "version":
636 return "OFVersion.%s" % self.msg.version.constant_version
637
638 java_type = self.java_type.public_type;
639 try:
640 global model
641 enum = model.enum_by_name(java_type)
642 entry = enum.entry_by_version_value(self.msg.version, self.value)
643 return "%s.%s" % ( enum.name, entry.name)
644 except KeyError, e:
645 print e.message
646 return self.value
647
648 @property
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700649 def is_pad(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700650 return isinstance(self.member, OFPadMember)
651
652 def is_type_value(self, version=None):
653 if(version==None):
654 return any(self.is_type_value(version) for version in self.msg.all_versions)
655 try:
656 return self.c_name in get_type_values(self.msg.c_name, version.int_version)
657 except:
658 return False
659
660 @property
661 def is_field_length_value(self):
662 return isinstance(self.member, OFFieldLengthMember)
663
664 @property
Andreas Wundsam001b1822013-08-02 22:25:55 -0700665 def is_discriminator(self):
666 return isinstance(self.member, OFDiscriminatorMember)
667
668 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700669 def is_length_value(self):
670 return isinstance(self.member, OFLengthMember)
671
672 @property
673 def is_public(self):
674 return not (self.is_pad or self.is_length_value)
675
676 @property
677 def is_data(self):
678 return isinstance(self.member, OFDataMember) and self.name != "version"
679
680 @property
681 def is_fixed_value(self):
682 return hasattr(self.member, "value") or self.name == "version" \
683 or ( self.name == "length" and self.msg.is_fixed_length) \
684 or ( self.name == "len" and self.msg.is_fixed_length)
685
686 @property
687 def value(self):
688 if self.name == "version":
689 return self.msg.version.int_version
690 elif self.name == "length" or self.name == "len":
691 return self.msg.length
Andreas Wundsam27303462013-07-16 12:52:35 -0700692 else:
Andreas Wundsam001b1822013-08-02 22:25:55 -0700693 return self.java_type.format_value(self.member.value)
694
695 @property
696 def priv_value(self):
697 if self.name == "version":
698 return self.msg.version.int_version
699 elif self.name == "length" or self.name == "len":
700 return self.msg.length
701 else:
702 return self.java_type.format_value(self.member.value, pub_type=False)
703
Andreas Wundsam27303462013-07-16 12:52:35 -0700704
705 @property
706 def is_writeable(self):
707 return self.is_data and not self.name in model.write_blacklist[self.msg.name]
708
709 def get_type_value_info(self, version):
710 return get_type_values(msg.c_name, version.int_version)[self.c_name]
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700711
712 @property
713 def length(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700714 if hasattr(self.member, "length"):
715 return self.member.length
716 else:
Andreas Wundsam5204de22013-07-30 11:34:45 -0700717 count, base = loxi_utils.type_dec_to_count_base(self.member.type)
Andreas Wundsam27303462013-07-16 12:52:35 -0700718 return of_g.of_base_types[base]['bytes'] * count
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700719
720 @staticmethod
Andreas Wundsam27303462013-07-16 12:52:35 -0700721 def for_of_member(java_class, member):
722 if isinstance(member, OFPadMember):
723 return JavaMember(None, "", None, member)
724 else:
725 if member.name == 'len':
726 name = 'length'
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700727 elif member.name == 'value_mask':
728 name = 'mask'
Andreas Wundsam27303462013-07-16 12:52:35 -0700729 else:
730 name = java_type.name_c_to_camel(member.name)
731 j_type = java_type.convert_to_jtype(java_class.c_name, member.name, member.oftype)
732 return JavaMember(java_class, name, j_type, member)
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700733
734 @property
735 def is_universal(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700736 if not self.msg.c_name in of_g.unified:
737 print("%s not self.unified" % self.msg.c_name)
738 return False
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700739 for version in of_g.unified[self.msg.c_name]:
740 if version == 'union' or version =='object_id':
741 continue
742 if 'use_version' in of_g.unified[self.msg.c_name][version]:
743 continue
744
Andreas Wundsam27303462013-07-16 12:52:35 -0700745 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 -0700746 return False
747 return True
748
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700749 @property
750 def is_virtual(self):
751 return False
752
Andreas Wundsam27303462013-07-16 12:52:35 -0700753 def __hash__(self):
754 return hash(self.name)
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700755
Andreas Wundsam27303462013-07-16 12:52:35 -0700756 def __eq__(self, other):
757 if other is None or type(self) != type(other):
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700758 return False
Andreas Wundsam27303462013-07-16 12:52:35 -0700759 return (self.name,) == (other.name,)
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700760
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700761class JavaVirtualMember(JavaMember):
762 """ Models a virtual property (member) of an openflow class that is not backed by a loxi ir member """
763 def __init__(self, msg, name, java_type, value=None):
764 JavaMember.__init__(self, msg, name, java_type, member=None)
765 self._value = value
766
767 @property
768 def is_fixed_value(self):
769 return True
770
771 @property
772 def value(self):
773 return self._value
774
775 @property
776 def priv_value(self):
777 return self._value
778
779
780 @property
781 def is_universal(self):
782 return True
783
784 @property
785 def is_virtual(self):
786 return True
Andreas Wundsame916d6f2013-07-30 11:33:58 -0700787
788#######################################################################
789### Unit Test
790#######################################################################
791
Yotam Harchol466b3212013-08-15 12:14:46 -0700792class JavaUnitTestSet(object):
Andreas Wundsame916d6f2013-07-30 11:33:58 -0700793 def __init__(self, java_class):
794 self.java_class = java_class
Yotam Harchol466b3212013-08-15 12:14:46 -0700795 first_data_file_name = "of{version}/{name}.data".format(version=java_class.version.of_version,
Andreas Wundsame916d6f2013-07-30 11:33:58 -0700796 name=java_class.c_name[3:])
Yotam Harchol466b3212013-08-15 12:14:46 -0700797 data_file_template = "of{version}/{name}.".format(version=java_class.version.of_version,
798 name=java_class.c_name[3:]) + "{i}.data"
799 test_class_name = self.java_class.name + "Test"
800 self.test_units = []
801 if test_data.exists(first_data_file_name):
802 self.test_units.append(JavaUnitTest(java_class, first_data_file_name, test_class_name))
803 i = 1
804 while test_data.exists(data_file_template.format(i=i)):
805 self.test_units.append(JavaUnitTest(java_class, data_file_template.format(i=i), test_class_name + str(i)))
806 i = i + 1
807
808 @property
809 def package(self):
810 return self.java_class.package
811
812 @property
813 def has_test_data(self):
814 return len(self.test_units) > 0
815
816 @property
817 def length(self):
818 return len(self.test_units)
819
820 def get_test_unit(self, i):
821 return self.test_units[i]
822
823
824class JavaUnitTest(object):
825 def __init__(self, java_class, file_name=None, test_class_name=None):
826 self.java_class = java_class
827 if file_name is None:
828 self.data_file_name = "of{version}/{name}.data".format(version=java_class.version.of_version,
829 name=java_class.c_name[3:])
830 else:
831 self.data_file_name = file_name
832 if test_class_name is None:
833 self.test_class_name = self.java_class.name + "Test"
834 else:
835 self.test_class_name = test_class_name
836
Andreas Wundsame916d6f2013-07-30 11:33:58 -0700837 @property
838 def package(self):
839 return self.java_class.package
840
841 @property
842 def name(self):
Yotam Harchol466b3212013-08-15 12:14:46 -0700843 return self.test_class_name
Andreas Wundsame916d6f2013-07-30 11:33:58 -0700844
845 @property
846 def has_test_data(self):
847 return test_data.exists(self.data_file_name)
848
849 @property
850 @memoize
851 def test_data(self):
852 return test_data.read(self.data_file_name)
853
854
Andreas Wundsam27303462013-07-16 12:52:35 -0700855#######################################################################
856### Enums
857#######################################################################
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700858
Andreas Wundsam27303462013-07-16 12:52:35 -0700859class JavaEnum(object):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700860 def __init__(self, c_name, version_enum_map):
Andreas Wundsam27303462013-07-16 12:52:35 -0700861 self.c_name = c_name
Andreas Wundsambe168f72013-08-03 22:49:35 -0700862
863 if c_name == "of_stats_types":
864 self.name = "OFStatsType"
865 else:
866 self.name = "OF" + java_type.name_c_to_caps_camel("_".join(c_name.split("_")[1:]))
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700867
Andreas Wundsam27303462013-07-16 12:52:35 -0700868 # Port_features has constants that start with digits
869 self.name_prefix = "PF_" if self.name == "OFPortFeatures" else ""
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700870
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700871 self.version_enums = version_enum_map
872
873 entry_name_version_value_map = OrderedDefaultDict(lambda: OrderedDict())
874 for version, ir_enum in version_enum_map.items():
875 for ir_entry in ir_enum.entries:
876 if "virtual" in ir_entry.params:
877 continue
878 entry_name_version_value_map[ir_entry.name][version] = ir_entry.value
879
Andreas Wundsam27303462013-07-16 12:52:35 -0700880 self.entries = [ JavaEnumEntry(self, name, version_value_map)
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700881 for (name, version_value_map) in entry_name_version_value_map.items() ]
Andreas Wundsam43526532013-08-01 22:03:50 -0700882
883 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 -0700884 self.package = "org.openflow.protocol"
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700885
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700886 def wire_type(self, version):
887 ir_enum = self.version_enums[version]
888 if "wire_type" in ir_enum.params:
889 return java_type.convert_enum_wire_type_to_jtype(ir_enum.params["wire_type"])
890 else:
891 return java_type.u8
892
893 @property
894 def versions(self):
895 return self.version_enums.keys()
896
Andreas Wundsam27303462013-07-16 12:52:35 -0700897 @memoize
898 def entry_by_name(self, name):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700899 res = find(lambda e: e.name == name, self.entries)
900 if res:
901 return res
902 else:
Andreas Wundsam27303462013-07-16 12:52:35 -0700903 raise KeyError("Enum %s: no entry with name %s" % (self.name, name))
904
905 @memoize
906 def entry_by_c_name(self, name):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700907 res = find(lambda e: e.c_name == name, self.entries)
908 if res:
909 return res
910 else:
Andreas Wundsam27303462013-07-16 12:52:35 -0700911 raise KeyError("Enum %s: no entry with c_name %s" % (self.name, name))
912
913 @memoize
914 def entry_by_version_value(self, version, value):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700915 res = find(lambda e: e.values[version] == value if version in e.values else False, self.entries)
916 if res:
917 return res
918 else:
Andreas Wundsam27303462013-07-16 12:52:35 -0700919 raise KeyError("Enum %s: no entry with version %s, value %s" % (self.name, version, value))
920
921# values: Map JavaVersion->Value
922class JavaEnumEntry(object):
923 def __init__(self, enum, name, values):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700924 self.enum = enum
Andreas Wundsam27303462013-07-16 12:52:35 -0700925 self.name = enum.name_prefix + "_".join(name.split("_")[1:]).upper()
926 self.values = values
927
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700928 def has_value(self, version):
929 return version in self.values
930
Andreas Wundsam27303462013-07-16 12:52:35 -0700931 def value(self, version):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700932 return self.values[version]
933
934 def format_value(self, version):
935 res = self.enum.wire_type(version).format_value(self.values[version])
Andreas Wundsam27303462013-07-16 12:52:35 -0700936 return res
937
Andreas Wundsam27303462013-07-16 12:52:35 -0700938 def all_values(self, versions, not_present=None):
939 return [ self.values[version] if version in self.values else not_present for version in versions ]