## This Source Code Form is subject to the terms of the Mozilla Public ## License, v. 2.0. If a copy of the MPL was not distributed with this ## file, You can obtain one at https://mozilla.org/MPL/2.0/. ## ## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. ## from __future__ import nested_scopes, print_function import errno import re import sys import os from optparse import OptionParser try: try: import simplejson as json except ImportError as e: if sys.hexversion >= 0x20600f0: import json else: raise e except ImportError: print(" You don't appear to have simplejson.py installed", file = sys.stderr) print(" (an implementation of a JSON reader and writer in Python).", file = sys.stderr) print(" You can install it:", file = sys.stderr) print(" - by running 'apt-get install python-simplejson' on Debian-based systems,", file = sys.stderr) print(" - by running 'yum install python-simplejson' on Fedora/Red Hat system,", file = sys.stderr) print(" - by running 'port install py25-simplejson' on Macports on OS X", file = sys.stderr) print(" (you may need to say 'make PYTHON=python2.5', as well),", file = sys.stderr) print(" - from sources from 'https://pypi.python.org/pypi/simplejson'", file = sys.stderr) print(" - simplejson is a standard json library in the Python core since 2.6", file = sys.stderr) sys.exit(1) def insert_base_types(d): for t in ['octet', 'shortstr', 'longstr', 'short', 'long', 'longlong', 'bit', 'table', 'timestamp']: d[t] = t class AmqpSpecFileMergeConflict(Exception): pass # If ignore_conflicts is true, then we allow acc and new to conflict, # with whatever's already in acc winning and new being ignored. If # ignore_conflicts is false, acc and new must not conflict. def default_spec_value_merger(key, acc, new, ignore_conflicts): if acc is None or acc == new or ignore_conflicts: return new else: raise AmqpSpecFileMergeConflict(key, acc, new) def extension_info_merger(key, acc, new, ignore_conflicts): return acc + [new] def domains_merger(key, acc, new, ignore_conflicts): merged = dict((k, v) for [k, v] in acc) for [k, v] in new: if k in merged: if not ignore_conflicts: raise AmqpSpecFileMergeConflict(key, acc, new) else: merged[k] = v return [[k, v] for (k, v) in merged.items()] def merge_dict_lists_by(dict_key, acc, new, ignore_conflicts): acc_index = set(v[dict_key] for v in acc) result = list(acc) # shallow copy for v in new: if v[dict_key] in acc_index: if not ignore_conflicts: raise AmqpSpecFileMergeConflict(description, acc, new) else: result.append(v) return result def constants_merger(key, acc, new, ignore_conflicts): return merge_dict_lists_by("name", acc, new, ignore_conflicts) def methods_merger(classname, acc, new, ignore_conflicts): return merge_dict_lists_by("name", acc, new, ignore_conflicts) def properties_merger(classname, acc, new, ignore_conflicts): return merge_dict_lists_by("name", acc, new, ignore_conflicts) def class_merger(acc, new, ignore_conflicts): acc["methods"] = methods_merger(acc["name"], acc["methods"], new["methods"], ignore_conflicts) acc["properties"] = properties_merger(acc["name"], acc.get("properties", []), new.get("properties", []), ignore_conflicts) def classes_merger(key, acc, new, ignore_conflicts): acc_dict = dict((v["name"], v) for v in acc) result = list(acc) # shallow copy for w in new: if w["name"] in acc_dict: class_merger(acc_dict[w["name"]], w, ignore_conflicts) else: result.append(w) return result mergers = { "extension": (extension_info_merger, []), "domains": (domains_merger, []), "constants": (constants_merger, []), "classes": (classes_merger, []), } def merge_load_specs(filenames, ignore_conflicts): handles = [open(filename) for filename in filenames] docs = [json.load(handle) for handle in handles] spec = {} for doc in docs: for (key, value) in doc.items(): (merger, default_value) = mergers.get(key, (default_spec_value_merger, None)) spec[key] = merger(key, spec.get(key, default_value), value, ignore_conflicts) for handle in handles: handle.close() return spec class AmqpSpec: # Slight wart: use a class member rather than change the ctor signature # to avoid breaking everyone else's code. ignore_conflicts = False def __init__(self, filenames): self.spec = merge_load_specs(filenames, AmqpSpec.ignore_conflicts) self.major = self.spec['major-version'] self.minor = self.spec['minor-version'] self.revision = ('revision' in self.spec) and (self.spec['revision'] or 0) self.port = self.spec['port'] self.domains = {} insert_base_types(self.domains) for entry in self.spec['domains']: self.domains[ entry[0] ] = entry[1] self.constants = [] for d in self.spec['constants']: if 'class' in d: klass = d['class'] else: klass = '' self.constants.append((d['name'], d['value'], klass)) self.classes = [] for element in self.spec['classes']: self.classes.append(AmqpClass(self, element)) def allClasses(self): return self.classes def allMethods(self): return [m for c in self.classes for m in c.allMethods()] def resolveDomain(self, n): return self.domains[n] class AmqpEntity: def __init__(self, element): self.element = element self.name = element['name'] class AmqpClass(AmqpEntity): def __init__(self, spec, element): AmqpEntity.__init__(self, element) self.spec = spec self.index = int(self.element['id']) self.methods = [] for method_element in self.element['methods']: self.methods.append(AmqpMethod(self, method_element)) self.hasContentProperties = False for method in self.methods: if method.hasContent: self.hasContentProperties = True break self.fields = [] if 'properties' in self.element: index = 0 for e in self.element['properties']: self.fields.append(AmqpField(self, e, index)) index = index + 1 def allMethods(self): return self.methods def __repr__(self): return 'AmqpClass("' + self.name + '")' class AmqpMethod(AmqpEntity): def __init__(self, klass, element): AmqpEntity.__init__(self, element) self.klass = klass self.index = int(self.element['id']) if 'synchronous' in self.element: self.isSynchronous = self.element['synchronous'] else: self.isSynchronous = False if 'content' in self.element: self.hasContent = self.element['content'] else: self.hasContent = False self.arguments = [] index = 0 for argument in element['arguments']: self.arguments.append(AmqpField(self, argument, index)) index = index + 1 def __repr__(self): return 'AmqpMethod("' + self.klass.name + "." + self.name + '" ' + repr(self.arguments) + ')' class AmqpField(AmqpEntity): def __init__(self, method, element, index): AmqpEntity.__init__(self, element) self.method = method self.index = index if 'type' in self.element: self.domain = self.element['type'] else: self.domain = self.element['domain'] if 'default-value' in self.element: self.defaultvalue = self.element['default-value'] else: self.defaultvalue = None def __repr__(self): return 'AmqpField("' + self.name + '")' def do_main(header_fn, body_fn): do_main_dict({"header": header_fn, "body": body_fn}) def do_main_dict(funcDict): def usage(): print("Usage:", file = sys.stderr) print(" {0} ... ".format(sys.argv[0]), file = sys.stderr) print(" where is one of: {0}".format(", ".join([k for k in funcDict.keys()])), file = sys.stderr) def mkdir_p(path): try: os.makedirs(path) except OSError as exc: # Python >2.5 if exc.errno == errno.EEXIST and os.path.isdir(path): pass else: raise def execute(fn, amqp_specs, out_file): stdout = sys.stdout mkdir_p(os.path.dirname(out_file)) f = open(out_file, 'w') success = False try: sys.stdout = f fn(amqp_specs) success = True finally: sys.stdout = stdout f.close() if not success: os.remove(out_file) parser = OptionParser() parser.add_option("--ignore-conflicts", action="store_true", dest="ignore_conflicts", default=False) (options, args) = parser.parse_args() if len(args) < 3: usage() sys.exit(1) else: function = args[0] sources = args[1:-1] dest = args[-1] AmqpSpec.ignore_conflicts = options.ignore_conflicts if function in funcDict: execute(funcDict[function], sources, dest) else: usage() sys.exit(1)