SEBA-271 Fix xproto field numbers not passed through
Change-Id: Ib5abee510e1a02f025d3011699d9f34d59e201c1
diff --git a/lib/xos-genx/xos-genx-tests/helpers.py b/lib/xos-genx/xos-genx-tests/helpers.py
index 232cae1..ca73402 100644
--- a/lib/xos-genx/xos-genx-tests/helpers.py
+++ b/lib/xos-genx/xos-genx-tests/helpers.py
@@ -23,6 +23,7 @@
# Store in this class the args to pass at the generator
class FakeArgs:
+ verbosity=0
pass
class XProtoTestHelpers:
diff --git a/lib/xos-genx/xosgenx/generator.py b/lib/xos-genx/xosgenx/generator.py
index cc00742..dc1ff9e 100644
--- a/lib/xos-genx/xosgenx/generator.py
+++ b/lib/xos-genx/xosgenx/generator.py
@@ -42,7 +42,7 @@
v = Proto2XProto()
ast.accept(v)
- v = XOS2Jinja()
+ v = XOS2Jinja(args)
ast.accept(v)
return v
diff --git a/lib/xos-genx/xosgenx/jinja2_extensions/base.py b/lib/xos-genx/xosgenx/jinja2_extensions/base.py
index 1293863..561aeea 100644
--- a/lib/xos-genx/xosgenx/jinja2_extensions/base.py
+++ b/lib/xos-genx/xosgenx/jinja2_extensions/base.py
@@ -120,7 +120,10 @@
if accessor:
base_fields = xproto_base_fields(table[accessor], table)
- model_fields = table[accessor]['fields']
+ model_fields = [x.copy() for x in table[accessor]['fields']]
+ for field in model_fields:
+ field["accessor"] = accessor
+
fields.extend(base_fields)
fields.extend(model_fields)
@@ -132,6 +135,53 @@
return fields
+def xproto_fields(m, table):
+ """ Generate the full list of models for the xproto message `m` including fields from the classes it inherits.
+
+ Inserts the special field "id" at the very beginning.
+
+ Each time we descend a new level of inheritance, increment the offset field numbers by 100. The base
+ class's fields will be numbered from 1-99, the first descendant will be number 100-199, the second
+ descdendant numbered from 200-299, and so on. This assumes any particular model as at most 100
+ fields.
+ """
+
+ model_fields = [x.copy() for x in m["fields"]]
+ for field in model_fields:
+ field["accessor"] = m["fqn"]
+
+ fields = xproto_base_fields(m, table) + model_fields
+
+ # The "id" field is a special field. Every model has one. Put it up front and pretend it's part of the
+
+ id_field = {'type': 'int32', 'name': 'id', 'options': {}, "id": "1", "accessor": fields[0]["accessor"]}
+
+ fields = [id_field] + fields
+
+ # Walk through the list of fields. They will be in depth-first search order from the base model forward. Each time
+ # the model changes, offset the protobuf field numbers by 100.
+ offset = 0
+ last_accessor = fields[0]["accessor"]
+ for field in fields:
+ if (field["accessor"] != last_accessor):
+ last_accessor = field["accessor"]
+ offset += 100
+ field_id = int(field["id"])
+ if (field_id < 1) or (field_id >= 100):
+ raise Exception("Only field numbers from 1 to 99 are permitted, field %s in model %s" % (field["name"], field["accessor"]))
+ field["id"] = int(field["id"]) + offset
+
+ # Check for duplicates
+ fields_by_number = {}
+ for field in fields:
+ id = field["id"]
+ dup = fields_by_number.get(id)
+ if dup:
+ raise Exception("Field %s has duplicate number %d with field %s in model %s" % (field["name"], id, dup["name"], field["accessor"]))
+ fields_by_number[id] = field
+
+ return fields
+
def xproto_base_rlinks(m, table):
links = []
@@ -146,6 +196,38 @@
return links
+def xproto_rlinks(m, table):
+ """ Return the reverse links for the xproto message `m`.
+
+ If the link includes a reverse_id, then it will be used for the protobuf field id. If there is no
+ reverse_id, then one will automatically be allocated started at id 1900. It is incouraged that all links
+ include reverse_ids, so that field identifiers are deterministic across all protobuf messages.
+ """
+
+ index = 1900
+ links = xproto_base_rlinks(m, table) + m["rlinks"]
+
+ links = [x for x in links if ("+" not in x["src_port"]) and ("+" not in x["dst_port"])]
+
+ for link in links:
+ if link["reverse_id"]:
+ link["id"] = int(link["reverse_id"])
+ else:
+ link["id"] = index
+ index += 1
+
+ # check for duplicates
+ links_by_number={}
+ for link in links:
+ id = link["id"]
+ dup=links_by_number.get(id)
+ if dup:
+ raise Exception("Field %s has duplicate number %d with field %s in model %s" % (link["src_port"], id, link["src_port"], m["name"]))
+ links_by_number[id] = link
+
+ return links
+
+
def xproto_base_links(m, table):
links = []
diff --git a/lib/xos-genx/xosgenx/targets/fieldlist.xtarget b/lib/xos-genx/xosgenx/targets/fieldlist.xtarget
new file mode 100644
index 0000000..3c0dea4
--- /dev/null
+++ b/lib/xos-genx/xosgenx/targets/fieldlist.xtarget
@@ -0,0 +1,11 @@
+
+{% for object in proto.messages|sort(attribute='name') %}
+{{ object.name }}
+{%- for field in xproto_fields(object, proto.message_table) | sort(attribute='id') %}
+ {{ field.name }}, {{ field.id }}, {{ field.accessor }}
+{%- endfor %}
+{%- for field in xproto_rlinks(object, proto.message_table) | sort(attribute='id') %}
+ {{ field.src_port }}_ids, {{ field.id }}, {{ field.accessor }}
+{%- endfor %}
+
+{% endfor %}
diff --git a/lib/xos-genx/xosgenx/targets/protoapi.xtarget b/lib/xos-genx/xosgenx/targets/protoapi.xtarget
index 183bacd..2311b98 100644
--- a/lib/xos-genx/xosgenx/targets/protoapi.xtarget
+++ b/lib/xos-genx/xosgenx/targets/protoapi.xtarget
@@ -20,23 +20,23 @@
option (contentTypeId) = "{{ xproto_unquote(xproto_first_non_empty([object.options.name, object.options.app_label, options.name, context.app_label])) }}.{{ object.name | lower }}";
{%- endif %}
{%- set id_field = {'type':'int32', 'name':'id', 'options':{}} -%}
- {%- for field in (xproto_base_fields(object, proto.message_table) + object.fields + [id_field]) | sort(attribute='name')%}
+ {%- for field in xproto_fields(object, proto.message_table) | sort(attribute='id')%}
{%- if field.options.type == "link" and field.options.link_type == "manytomany" %}
- repeated int32 {{ field.name }}_ids = {{ loop.index }} [(manyToManyForeignKey).modelName = "{{ field.options.model }}"];
+ repeated int32 {{ field.name }}_ids = {{ field.id }} [(manyToManyForeignKey).modelName = "{{ field.options.model }}"];
{%- else %}
oneof {{ field.name }}_present {
- {{ xproto_api_type(field) }} {{ field.name }}{% if field.link -%}_id{% endif %} = {{ loop.index }}{{ xproto_api_opts(field) }};
+ {{ xproto_api_type(field) }} {{ field.name }}{% if field.link -%}_id{% endif %} = {{ field.id }} {{ xproto_api_opts(field) }};
}
{%- endif -%}
{%- endfor -%}
- {%- for ref in xproto_base_rlinks(object, proto.message_table) + object.rlinks | sort(attribute='src_port') %}
+ {%- for ref in xproto_rlinks(object, proto.message_table) | sort(attribute='id') %}
{%- if '+' not in ref.src_port and '+' not in ref.dst_port %}
- repeated int32 {{ ref.src_port }}_ids = {{ loop.index + 100 }} [(reverseForeignKey).modelName = "{{ ref.peer.name }}"];
+ repeated int32 {{ ref.src_port }}_ids = {{ ref["id"] }} [(reverseForeignKey).modelName = "{{ ref.peer.name }}"];
{%- endif -%}
{%- endfor %}
- string class_names = 201;
- string self_content_type_id = 202;
+ string class_names = 2046;
+ string self_content_type_id = 2047;
}
message {{ xproto_pluralize(object) }} {
diff --git a/lib/xos-genx/xosgenx/xos2jinja.py b/lib/xos-genx/xosgenx/xos2jinja.py
index 42e83c3..aca2468 100644
--- a/lib/xos-genx/xosgenx/xos2jinja.py
+++ b/lib/xos-genx/xosgenx/xos2jinja.py
@@ -72,41 +72,6 @@
count += 1
return count
-def compute_rlinks(messages, message_dict):
- rev_links = {}
-
- link_opposite = {
- 'manytomany': 'manytomany',
- 'manytoone': 'onetomany',
- 'onetoone': 'onetoone',
- 'onetomany': 'manytoone'
- }
-
- for m in messages:
- for l in m['links']:
- rlink = copy.deepcopy(l)
-
- rlink['_type'] = 'rlink' # An implicit link, not declared in the model
- rlink['src_port'] = l['dst_port']
- rlink['dst_port'] = l['src_port']
- rlink['peer'] = {'name': m['name'], 'package': m['package'], 'fqn': m['fqn']}
- rlink['link_type'] = link_opposite[l['link_type']]
-
- try:
- try:
- rev_links[l['peer']['fqn']].append(rlink)
- except TypeError:
- pass
- except KeyError:
- rev_links[l['peer']['fqn']] = [rlink]
-
- for m in messages:
- try:
- m['rlinks'] = rev_links[m['name']]
- message_dict[m['name']]['rlinks'] = m['rlinks']
- except KeyError:
- pass
-
def name_to_value(obj):
try:
@@ -129,7 +94,7 @@
in addition to traversing it '''
class XOS2Jinja(Visitor):
- def __init__(self):
+ def __init__(self, args):
super(XOS2Jinja, self).__init__()
self.stack = Stack()
@@ -145,6 +110,7 @@
self.verbose = 0
self.first_field = True
self.first_method = True
+ self.args = args
def visit_PolicyDefinition(self, obj):
if self.package:
@@ -241,6 +207,11 @@
except AttributeError:
s['peer'] = obj.name
+ try:
+ s['reverse_id'] = obj.reverse_id.pval
+ except AttributeError:
+ s['reverse_id'] = obj.reverse_id
+
s['_type'] = 'link'
s['options'] = {'modifier': 'optional'}
@@ -397,7 +368,7 @@
messages.insert(0, m)
- compute_rlinks(messages, self.models)
+ self.compute_rlinks(messages, self.models)
self.messages = messages
return True
@@ -406,3 +377,45 @@
count = self.count_stack.pop()
self.count_stack.push(count + 1)
return True
+
+ def compute_rlinks(self, messages, message_dict):
+ rev_links = {}
+
+ link_opposite = {
+ 'manytomany': 'manytomany',
+ 'manytoone': 'onetomany',
+ 'onetoone': 'onetoone',
+ 'onetomany': 'manytoone'
+ }
+
+ for m in messages:
+ for l in m['links']:
+ rlink = copy.deepcopy(l)
+
+ rlink['_type'] = 'rlink' # An implicit link, not declared in the model
+ rlink['src_port'] = l['dst_port']
+ rlink['dst_port'] = l['src_port']
+ rlink['peer'] = {'name': m['name'], 'package': m['package'], 'fqn': m['fqn']}
+ rlink['link_type'] = link_opposite[l['link_type']]
+ rlink["reverse_id"] = l['reverse_id']
+
+ if (not l['reverse_id']) and (self.args.verbosity >= 1):
+ print >> sys.stderr, "WARNING: Field %s in model %s has no reverse_id" % (l["src_port"], m["name"])
+
+ if l["reverse_id"] and ((int(l["reverse_id"]) < 1000) or (int(l["reverse_id"]) >= 1900)):
+ raise Exception("reverse id for field %s in model %s should be between 1000 and 1899" % (l["src_port"], m["name"]))
+
+ try:
+ try:
+ rev_links[l['peer']['fqn']].append(rlink)
+ except TypeError:
+ pass
+ except KeyError:
+ rev_links[l['peer']['fqn']] = [rlink]
+
+ for m in messages:
+ try:
+ m['rlinks'] = rev_links[m['name']]
+ message_dict[m['name']]['rlinks'] = m['rlinks']
+ except KeyError:
+ pass
diff --git a/lib/xos-genx/xosgenx/xosgen.py b/lib/xos-genx/xosgenx/xosgen.py
index ec1ba68..8259d58 100755
--- a/lib/xos-genx/xosgenx/xosgen.py
+++ b/lib/xos-genx/xosgenx/xosgen.py
@@ -27,6 +27,7 @@
parse.add_argument('--kvpairs', dest='kv', action='store',default=None, help='Key value pairs to make available to the target')
parse.add_argument('--write-to-file', dest='write_to_file', choices = ['single', 'model', 'target'], action='store',default=None, help='Single output file (single) or output file per model (model) or let target decide (target)')
parse.add_argument('--version', action='version', version=__version__)
+parse.add_argument("-v", "--verbosity", action="count", default=0, help="increase output verbosity")
group = parse.add_mutually_exclusive_group()
group.add_argument('--dest-file', dest='dest_file', action='store',default=None, help='Output file name (if write-to-file is set to single)')