summaryrefslogtreecommitdiffstats
path: root/tools/json_schema_compiler/model.py
blob: 2c356fff20d06f0815e7a2662586bdacbfa77e01 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# Copyright (c) 2012 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import os.path

class Model(object):
  """Model of all namespaces that comprise an API.
  """
  def __init__(self):
    self.namespaces = {}

  def AddNamespace(self, json, source_file):
    """Add a namespace's json to the model if it has a "compile" property set
    to true. Returns the new namespace or None if a namespace wasn't added.
    """
    if not json.get('compile'):
      return None
    namespace = Namespace(json, source_file)
    self.namespaces[namespace.name] = namespace
    return namespace

class Namespace(object):
  """An API namespace.
  """
  def __init__(self, json, source_file):
    self.name = json['namespace']
    self.source_file = source_file
    self.source_file_dir, self.source_file_filename = os.path.split(source_file)
    self.type_dependencies = {}
    self.types = {}
    self.functions = {}
    for type_json in json['types']:
      type_ = Type(type_json)
      self.types[type_.name] = type_
    for function_json in json['functions']:
      if not function_json.get('nocompile'):
        function = Function(function_json)
        self.functions[function.name] = function

class Type(object):
  """A Type defined in the json.
  """
  def __init__(self, json):
    self.name = json['id']
    self.description = json.get('description')
    self.properties = {}
    for prop_name, prop_json in json['properties'].items():
      self.properties[prop_name] = Property(prop_name, prop_json)

class Callback(object):
  """A callback parameter to a Function.
  """
  def __init__(self, json):
    params = json['parameters']
    if len(params) == 0:
      self.param = None
    elif len(params) == 1:
      param = params[0]
      self.param = Property(param['name'], param)
    else:
      raise AssertionError("Callbacks can have at most a single parameter")

class Function(object):
  """A Function defined in the API.
  """
  def __init__(self, json):
    self.name = json['name']
    self.params = []
    self.description = json['description']
    self.callback = None
    self.type_dependencies = {}
    for param in json['parameters']:
      if param.get('type') == 'function':
        assert (not self.callback), "Function has more than one callback"
        self.callback = Callback(param)
      else:
        self.params.append(Property(param['name'], param))
    assert (self.callback), "Function does not support callback"

# TODO(calamity): handle Enum/choices
class Property(object):
  """A property of a type OR a parameter to a function.

  Members will change based on PropertyType. Check self.type_ to determine which
  members actually exist.
  """
  def __init__(self, name, json):
    self.name = name
    self.optional = json.get('optional', False)
    self.description = json.get('description')
    # TODO(calamity) maybe check for circular refs? could that be a problem?
    if '$ref' in json:
      self.ref_type = json['$ref']
      self.type_ = PropertyType.REF
    elif 'type' in json:
      json_type = json['type']
      if json_type == 'string':
        self.type_ = PropertyType.STRING
      elif json_type ==  'boolean':
        self.type_ = PropertyType.BOOLEAN
      elif json_type == 'integer':
        self.type_ = PropertyType.INTEGER
      elif json_type == 'double':
        self.type_ = PropertyType.DOUBLE
      elif json_type == 'array':
        self.item_type = Property(name + "_inner", json['items'])
        self.type_ = PropertyType.ARRAY
      elif json_type == 'object':
        self.properties = {}
        self.type_ = PropertyType.OBJECT
        for key, val in json['properties'].items():
          self.properties[key] = Property(key, val)
      else:
        raise NotImplementedError(json_type)
    elif 'choices' in json:
      self.type_ = PropertyType.CHOICES
      self.choices = {}

class PropertyType(object):
  """Enum of different types of properties/parameters.
  """
  class _Info(object):
    def __init__(self, is_fundamental):
      self.is_fundamental = is_fundamental

  INTEGER = _Info(True)
  DOUBLE = _Info(True)
  BOOLEAN = _Info(True)
  STRING = _Info(True)
  ARRAY = _Info(False)
  REF = _Info(False)
  CHOICES = _Info(False)
  OBJECT = _Info(False)