- add sources.
[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
7 from third_party.json_schema_compiler import json_parse, idl_schema, idl_parser
8
9
10 def RemoveNoDocs(item):
11   '''Removes nodes that should not be rendered from an API schema.
12   '''
13   if json_parse.IsDict(item):
14     if item.get('nodoc', False):
15       return True
16     for key, value in item.items():
17       if RemoveNoDocs(value):
18         del item[key]
19   elif type(item) == list:
20     to_remove = []
21     for i in item:
22       if RemoveNoDocs(i):
23         to_remove.append(i)
24     for i in to_remove:
25       item.remove(i)
26   return False
27
28
29 def DetectInlineableTypes(schema):
30   '''Look for documents that are only referenced once and mark them as inline.
31   Actual inlining is done by _InlineDocs.
32   '''
33   if not schema.get('types'):
34     return
35
36   ignore = frozenset(('value', 'choices'))
37   refcounts = defaultdict(int)
38   # Use an explicit stack instead of recursion.
39   stack = [schema]
40
41   while stack:
42     node = stack.pop()
43     if isinstance(node, list):
44       stack.extend(node)
45     elif isinstance(node, Mapping):
46       if '$ref' in node:
47         refcounts[node['$ref']] += 1
48       stack.extend(v for k, v in node.iteritems() if k not in ignore)
49
50   for type_ in schema['types']:
51     if not 'noinline_doc' in type_:
52       if refcounts[type_['id']] == 1:
53         type_['inline_doc'] = True
54
55
56 def InlineDocs(schema):
57   '''Replace '$ref's that refer to inline_docs with the json for those docs.
58   '''
59   types = schema.get('types')
60   if types is None:
61     return
62
63   inline_docs = {}
64   types_without_inline_doc = []
65
66   # Gather the types with inline_doc.
67   for type_ in types:
68     if type_.get('inline_doc'):
69       inline_docs[type_['id']] = type_
70       for k in ('description', 'id', 'inline_doc'):
71         type_.pop(k, None)
72     else:
73       types_without_inline_doc.append(type_)
74   schema['types'] = types_without_inline_doc
75
76   def apply_inline(node):
77     if isinstance(node, list):
78       for i in node:
79         apply_inline(i)
80     elif isinstance(node, Mapping):
81       ref = node.get('$ref')
82       if ref and ref in inline_docs:
83         node.update(inline_docs[ref])
84         del node['$ref']
85       for k, v in node.iteritems():
86         apply_inline(v)
87
88   apply_inline(schema)
89
90
91 def ProcessSchema(path, file_data):
92   '''Parses |file_data| using a method determined by checking the
93   extension of the file at the given |path|. Then, trims 'nodoc' and handles
94   inlineable types from the parsed schema data.
95   '''
96   def trim_and_inline(schema, is_idl=False):
97     '''Modifies an API schema in place by removing nodes that shouldn't be
98     documented and inlining schema types that are only referenced once.
99     '''
100     if RemoveNoDocs(schema):
101       # A return of True signifies that the entire schema should not be
102       # documented. Otherwise, only nodes that request 'nodoc' are removed.
103       return None
104     if is_idl:
105       DetectInlineableTypes(schema)
106     InlineDocs(schema)
107     return schema
108
109   if path.endswith('.idl'):
110     idl = idl_schema.IDLSchema(idl_parser.IDLParser().ParseData(file_data))
111     return trim_and_inline(idl.process()[0], is_idl=True)
112
113   schemas = json_parse.Parse(file_data)
114   for schema in schemas:
115     # Schemas could consist of one API schema (data for a specific API file)
116     # or multiple (data from extension_api.json).
117     trim_and_inline(schema)
118   return schemas