1#!/usr/bin/env python
2# Copyright 2015 The Chromium Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""Usage: collect_idls_into_json.py path_file.txt json_file.json
7This script collects and organizes interface information and that information dumps into json file.
8"""
9
10import json
11import os
12import sys
13
14from blinkpy.common import path_finder
15path_finder.add_bindings_scripts_dir_to_sys_path()
16
17import utilities
18from blink_idl_parser import parse_file, BlinkIDLParser
19
20
21_INTERFACE = 'Interface'
22_IMPLEMENT = 'Implements'
23_PARTIAL = 'Partial'
24_NAME = 'Name'
25_TYPE = 'Type'
26_UNIONTYPE = 'UnionType'
27_ARRAY = 'Array'
28_ANY = 'Any'
29_SEQUENCE = 'Sequence'
30_PROP_VALUE = 'VALUE'
31_VALUE = 'Value'
32_PARENT = 'Parent'
33_FILEPATH = 'FilePath'
34_PROP_FILENAME = 'FILENAME'
35_PROP_READONLY = 'READONLY'
36_READONLY = 'Readonly'
37_PROP_STATIC = 'STATIC'
38_STATIC = 'Static'
39_CONSTS = 'Consts'
40_CONST = 'Const'
41_ATTRIBUTES = 'Attributes'
42_ATTRIBUTE = 'Attribute'
43_OPERATIONS = 'Operations'
44_OPERATION = 'Operation'
45_PROP_GETTER = 'GETTER'
46_NAMED_GETTER = '__getter__'
47_PROP_SETTER = 'SETTER'
48_NAMED_SETTER = '__setter__'
49_PROP_DELETER = 'DELETER'
50_NAMED_DELETER = '__deleter__'
51_ARGUMENTS = 'Arguments'
52_ARGUMENT = 'Argument'
53_EXTATTRIBUTES = 'ExtAttributes'
54_EXTATTRIBUTE = 'ExtAttribute'
55_INHERIT = 'Inherit'
56_PROP_REFERENCE = 'REFERENCE'
57_PARTIAL_FILEPATH = 'Partial_FilePaths'
58_MEMBERS = [_CONSTS, _ATTRIBUTES, _OPERATIONS]
59
60
61def get_definitions(paths):
62    """Returns a generator of IDL node.
63    Args:
64      paths: list of IDL file path
65    Returns:
66      a generator which yields IDL node objects
67    """
68    parser = BlinkIDLParser()
69    for path in paths:
70        definitions = parse_file(parser, path)
71        for definition in definitions.GetChildren():
72            yield definition
73
74
75def is_implements(definition):
76    """Returns True if class of |definition| is Implements, otherwise False.
77    Args:
78      definition: IDL node
79    Returns:
80      True if class of |definition| is Implements, otherwise False.
81    """
82    return definition.GetClass() == _IMPLEMENT
83
84
85def is_partial(definition):
86    """Returns True if |definition| is 'partial interface' class, otherwise False.
87    Args:
88      definition: IDL node
89    Return:
90      True if |definition| is 'partial interface' class, otherwise False.
91    """
92    return definition.GetClass() == _INTERFACE and definition.GetProperty(_PARTIAL)
93
94
95def get_filepath(interface_node):
96    """Returns relative path to the IDL in which |interface_node| is defined.
97    Args:
98      interface_node: IDL interface
99    Returns:
100      str which is |interface_node|'s file path
101    """
102    filename = interface_node.GetProperty(_PROP_FILENAME)
103    return os.path.relpath(filename)
104
105
106def get_const_node_list(interface_node):
107    """Returns a list of Const node.
108    Args:
109      interface_node: interface node
110    Returns:
111      A list of const node
112    """
113    return interface_node.GetListOf(_CONST)
114
115
116def get_const_type(const_node):
117    """Returns const's type.
118    Args:
119      const_node: const node
120    Returns:
121      str which is constant type.
122    """
123    return const_node.GetChildren()[0].GetName()
124
125
126def get_const_value(const_node):
127    """Returns const's value.
128    This function only supports primitive types.
129
130    Args:
131      const_node: const node
132    Returns:
133      str which is name of constant's value.
134    """
135    if const_node.GetChildren()[1].GetName():
136        return const_node.GetChildren()[1].GetName()
137    else:
138        for const_child in const_node.GetChildren():
139            if const_child.GetClass() == _VALUE and not const_child.GetName():
140                return const_child.GetProperty(_PROP_VALUE)
141        raise Exception('Constant value is empty')
142
143
144def const_node_to_dict(const_node):
145    """Returns dictionary of const's information.
146    Args:
147      const_node: const node
148    Returns:
149      dictionary of const's information
150    """
151    return {
152        _NAME: const_node.GetName(),
153        _TYPE: get_const_type(const_node),
154        _VALUE: get_const_value(const_node),
155        _EXTATTRIBUTES: [extattr_node_to_dict(extattr) for extattr in get_extattribute_node_list(const_node)],
156    }
157
158
159def get_attribute_node_list(interface_node):
160    """Returns list of Attribute if the interface have one.
161    Args:
162      interface_node: interface node
163    Returns:
164      list of attribute node
165    """
166    return interface_node.GetListOf(_ATTRIBUTE)
167
168
169def get_attribute_type(attribute_node):
170    """Returns type of attribute.
171    Args:
172      attribute_node: attribute node
173    Returns:
174      name of attribute's type
175    """
176    attr_type = attribute_node.GetOneOf(_TYPE).GetChildren()[0]
177    type_list = []
178    if attr_type.GetClass() == _UNIONTYPE:
179        union_member_list = attr_type.GetListOf(_TYPE)
180        for union_member in union_member_list:
181            for type_component in union_member.GetChildren():
182                if type_component.GetClass() == _ARRAY:
183                    type_list[-1] += '[]'
184                elif type_component.GetClass() == _SEQUENCE:
185                    for seq_type in type_component.GetOneOf(_TYPE).GetChildren():
186                        type_list.append('<' + seq_type.GetName() + '>')
187                else:
188                    type_list.append(type_component.GetName())
189        return type_list
190    elif attr_type.GetClass() == _SEQUENCE:
191        union_member_types = []
192        if attr_type.GetOneOf(_TYPE).GetChildren()[0].GetClass() == _UNIONTYPE:
193            for union_member in attr_type.GetOneOf(_TYPE).GetOneOf(_UNIONTYPE).GetListOf(_TYPE):
194                if len(union_member.GetChildren()) != 1:
195                    raise Exception('Complex type in a union in a sequence is not yet supported')
196                type_component = union_member.GetChildren()[0]
197                union_member_types.append(type_component.GetName())
198            return '<' + str(union_member_types) + '>'
199        else:
200            for type_component in attr_type.GetOneOf(_TYPE).GetChildren():
201                if type_component.GetClass() == _SEQUENCE:
202                    raise Exception('Sequence in another sequence is not yet supported')
203                else:
204                    if type_component.GetClass() == _ARRAY:
205                        type_list[-1] += []
206                    else:
207                        type_list.append(type_component.GetName())
208            return '<' + type_list[0] + '>'
209    elif attr_type.GetClass() == _ANY:
210        return _ANY
211    else:
212        for type_component in attribute_node.GetOneOf(_TYPE).GetChildren():
213            if type_component.GetClass() == _ARRAY:
214                type_list[-1] += '[]'
215            else:
216                type_list.append(type_component.GetName())
217        return type_list[0]
218
219
220get_operation_type = get_attribute_type
221get_argument_type = get_attribute_type
222
223
224def attribute_node_to_dict(attribute_node):
225    """Returns dictioary of attribute's information.
226    Args:
227      attribute_node: attribute node
228    Returns:
229      dictionary of attribute's information
230    """
231    return {
232        _NAME: attribute_node.GetName(),
233        _TYPE: get_attribute_type(attribute_node),
234        _EXTATTRIBUTES: [extattr_node_to_dict(extattr) for extattr in get_extattribute_node_list(attribute_node)],
235        _READONLY: attribute_node.GetProperty(_PROP_READONLY, default=False),
236        _STATIC: attribute_node.GetProperty(_PROP_STATIC, default=False),
237    }
238
239
240def get_operation_node_list(interface_node):
241    """Returns operations node list.
242    Args:
243      interface_node: interface node
244    Returns:
245      list of oparation node
246    """
247    return interface_node.GetListOf(_OPERATION)
248
249
250def get_argument_node_list(operation_node):
251    """Returns list of argument.
252    Args:
253      operation_node: operation node
254    Returns:
255      list of argument node
256    """
257    return operation_node.GetOneOf(_ARGUMENTS).GetListOf(_ARGUMENT)
258
259
260def argument_node_to_dict(argument_node):
261    """Returns dictionary of argument's information.
262    Args:
263      argument_node: argument node
264    Returns:
265      dictionary of argument's information
266    """
267    return {
268        _NAME: argument_node.GetName(),
269        _TYPE: get_argument_type(argument_node),
270    }
271
272
273def get_operation_name(operation_node):
274    """Returns openration's name.
275    Args:
276      operation_node: operation node
277    Returns:
278      name of operation
279    """
280    if operation_node.GetProperty(_PROP_GETTER):
281        return _NAMED_GETTER
282    elif operation_node.GetProperty(_PROP_SETTER):
283        return _NAMED_SETTER
284    elif operation_node.GetProperty(_PROP_DELETER):
285        return _NAMED_DELETER
286    else:
287        return operation_node.GetName()
288
289
290def operation_node_to_dict(operation_node):
291    """Returns dictionary of operation's information.
292    Args:
293      operation_node: operation node
294    Returns:
295      dictionary of operation's informantion
296    """
297    return {
298        _NAME: get_operation_name(operation_node),
299        _ARGUMENTS: [argument_node_to_dict(argument) for argument in get_argument_node_list(operation_node)
300                     if argument_node_to_dict(argument)],
301        _TYPE: get_operation_type(operation_node),
302        _EXTATTRIBUTES: [extattr_node_to_dict(extattr) for extattr in get_extattribute_node_list(operation_node)],
303        _STATIC: operation_node.GetProperty(_PROP_STATIC, default=False),
304    }
305
306
307def get_extattribute_node_list(node):
308    """Returns list of ExtAttribute.
309    Args:
310      node: IDL node
311    Returns:
312      list of ExtAttrbute
313    """
314    if node.GetOneOf(_EXTATTRIBUTES):
315        return node.GetOneOf(_EXTATTRIBUTES).GetListOf(_EXTATTRIBUTE)
316    else:
317        return []
318
319
320def extattr_node_to_dict(extattr):
321    """Returns dictionary of ExtAttribute's information.
322    Args:
323      extattr: ExtAttribute node
324    Returns:
325      dictionary of ExtAttribute's information
326    """
327    return {
328        _NAME: extattr.GetName(),
329    }
330
331
332def inherit_node_to_dict(interface_node):
333    """Returns a dictionary of inheritance information.
334    Args:
335      interface_node: interface node
336    Returns:
337      A dictioanry of inheritance information.
338    """
339    inherit = interface_node.GetOneOf(_INHERIT)
340    if inherit:
341        return {_PARENT: inherit.GetName()}
342    else:
343        return {_PARENT: None}
344
345
346def interface_node_to_dict(interface_node):
347    """Returns a dictioary of interface information.
348    Args:
349      interface_node: interface node
350    Returns:
351      A dictionary of the interface information.
352    """
353    return {
354        _NAME: interface_node.GetName(),
355        _FILEPATH: get_filepath(interface_node),
356        _CONSTS: [const_node_to_dict(const) for const in get_const_node_list(interface_node)],
357        _ATTRIBUTES: [attribute_node_to_dict(attr) for attr in get_attribute_node_list(interface_node) if attr],
358        _OPERATIONS: [operation_node_to_dict(operation) for operation in get_operation_node_list(interface_node) if operation],
359        _EXTATTRIBUTES: [extattr_node_to_dict(extattr) for extattr in get_extattribute_node_list(interface_node)],
360        _INHERIT: inherit_node_to_dict(interface_node)
361    }
362
363
364def merge_partial_dicts(interfaces_dict, partials_dict):
365    """Merges partial interface into non-partial interface.
366    Args:
367      interfaces_dict: A dict of the non-partial interfaces.
368      partial_dict: A dict of partial interfaces.
369    Returns:
370      A merged dictionary of |interface_dict| with |partial_dict|.
371    """
372    for interface_name, partial in partials_dict.iteritems():
373        interface = interfaces_dict.get(interface_name)
374        if not interface:
375            raise Exception('There is a partial interface, but the corresponding non-partial interface was not found.')
376        for member in _MEMBERS:
377            interface[member].extend(partial.get(member))
378            interface.setdefault(_PARTIAL_FILEPATH, []).append(partial[_FILEPATH])
379    return interfaces_dict
380
381
382def merge_implement_nodes(interfaces_dict, implement_node_list):
383    """Combines a dict of interface information with referenced interface information.
384    Args:
385      interfaces_dict: dict of interface information
386      implement_nodes: list of implemented interface node
387    Returns:
388      A dict of interface information combined with implements nodes.
389    """
390    for implement in implement_node_list:
391        reference = implement.GetProperty(_PROP_REFERENCE)
392        implement = implement.GetName()
393        if reference not in interfaces_dict.keys() or implement not in interfaces_dict.keys():
394            raise Exception('There is not corresponding implement or reference interface.')
395        for member in _MEMBERS:
396            interfaces_dict[implement][member].extend(interfaces_dict[reference].get(member))
397    return interfaces_dict
398
399
400def export_to_jsonfile(dictionary, json_file):
401    """Writes a Python dict into a JSON file.
402    Args:
403      dictioary: interface dictionary
404      json_file: json file for output
405    """
406    with open(json_file, 'w') as f:
407        json.dump(dictionary, f, sort_keys=True)
408
409
410def usage():
411    sys.stdout.write('Usage: collect_idls_into_json.py <path_file.txt> <output_file.json>\n')
412
413
414def main(args):
415    if len(args) != 2:
416        usage()
417        exit(1)
418    path_file = args[0]
419    json_file = args[1]
420    path_list = utilities.read_file_to_list(path_file)
421    implement_node_list = [definition
422                           for definition in get_definitions(path_list)
423                           if is_implements(definition)]
424    interfaces_dict = {definition.GetName(): interface_node_to_dict(definition)
425                       for definition in get_definitions(path_list)
426                       if not is_partial(definition)}
427    partials_dict = {definition.GetName(): interface_node_to_dict(definition)
428                     for definition in get_definitions(path_list)
429                     if is_partial(definition)}
430    dictionary = merge_partial_dicts(interfaces_dict, partials_dict)
431    interfaces_dict = merge_implement_nodes(interfaces_dict, implement_node_list)
432    export_to_jsonfile(dictionary, json_file)
433
434
435if __name__ == '__main__':
436    main(sys.argv[1:])
437