Upstream version 10.38.222.0
[platform/framework/web/crosswalk.git] / src / chrome / common / extensions / docs / server2 / schema_util.py
1 # Copyright 2013 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
4
5 from collections import defaultdict, Mapping
6 import traceback
7
8 from third_party.json_schema_compiler import json_parse, idl_schema, idl_parser
9
10
11 def RemoveNoDocs(item):
12   '''Removes nodes that should not be rendered from an API schema.
13   '''
14   if json_parse.IsDict(item):
15     if item.get('nodoc', False):
16       return True
17     for key, value in item.items():
18       if RemoveNoDocs(value):
19         del item[key]
20   elif type(item) == list:
21     to_remove = []
22     for i in item:
23       if RemoveNoDocs(i):
24         to_remove.append(i)
25     for i in to_remove:
26       item.remove(i)
27   return False
28
29
30 def DetectInlineableTypes(schema):
31   '''Look for documents that are only referenced once and mark them as inline.
32   Actual inlining is done by _InlineDocs.
33   '''
34   if not schema.get('types'):
35     return
36
37   ignore = frozenset(('value', 'choices'))
38   refcounts = defaultdict(int)
39   # Use an explicit stack instead of recursion.
40   stack = [schema]
41
42   while stack:
43     node = stack.pop()
44     if isinstance(node, list):
45       stack.extend(node)
46     elif isinstance(node, Mapping):
47       if '$ref' in node:
48         refcounts[node['$ref']] += 1
49       stack.extend(v for k, v in node.iteritems() if k not in ignore)
50
51   for type_ in schema['types']:
52     if not 'noinline_doc' in type_:
53       if refcounts[type_['id']] == 1:
54         type_['inline_doc'] = True
55
56
57 def InlineDocs(schema, retain_inlined_types):
58   '''Replace '$ref's that refer to inline_docs with the json for those docs.
59   If |retain_inlined_types| is False, then the inlined nodes are removed
60   from the schema.
61   '''
62   types = schema.get('types')
63   if types is None:
64     return
65
66   inline_docs = {}
67   types_without_inline_doc = []
68
69   # Gather the types with inline_doc.
70   for type_ in types:
71     if type_.get('inline_doc'):
72       inline_docs[type_['id']] = type_
73       if not retain_inlined_types:
74         for k in ('description', 'id', 'inline_doc'):
75           type_.pop(k, None)
76     else:
77       types_without_inline_doc.append(type_)
78   if not retain_inlined_types:
79     schema['types'] = types_without_inline_doc
80
81   def apply_inline(node):
82     if isinstance(node, list):
83       for i in node:
84         apply_inline(i)
85     elif isinstance(node, Mapping):
86       ref = node.get('$ref')
87       if ref and ref in inline_docs:
88         node.update(inline_docs[ref])
89         del node['$ref']
90       for k, v in node.iteritems():
91         apply_inline(v)
92
93   apply_inline(schema)
94
95
96 def ProcessSchema(path, file_data, retain_inlined_types=False):
97   '''Parses |file_data| using a method determined by checking the
98   extension of the file at the given |path|. Then, trims 'nodoc' and if
99   |retain_inlined_types| is given and False, removes inlineable types from
100   the parsed schema data.
101   '''
102   def trim_and_inline(schema, is_idl=False):
103     '''Modifies an API schema in place by removing nodes that shouldn't be
104     documented and inlining schema types that are only referenced once.
105     '''
106     if RemoveNoDocs(schema):
107       # A return of True signifies that the entire schema should not be
108       # documented. Otherwise, only nodes that request 'nodoc' are removed.
109       return None
110     if is_idl:
111       DetectInlineableTypes(schema)
112     InlineDocs(schema, retain_inlined_types)
113     return schema
114
115   if path.endswith('.idl'):
116     idl = idl_schema.IDLSchema(idl_parser.IDLParser().ParseData(file_data))
117     # Wrap the result in a list so that it behaves like JSON API data.
118     return [trim_and_inline(idl.process()[0], is_idl=True)]
119
120   try:
121     schemas = json_parse.Parse(file_data)
122   except:
123     raise ValueError('Cannot parse "%s" as JSON:\n%s' %
124                      (path, traceback.format_exc()))
125   for schema in schemas:
126     # Schemas could consist of one API schema (data for a specific API file)
127     # or multiple (data from extension_api.json).
128     trim_and_inline(schema)
129   return schemas