• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
5from collections import defaultdict, Mapping
6
7from third_party.json_schema_compiler import json_parse, idl_schema, idl_parser
8
9
10def 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
29def 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
56def 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
91def 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    # Wrap the result in a list so that it behaves like JSON API data.
112    return [trim_and_inline(idl.process()[0], is_idl=True)]
113
114  schemas = json_parse.Parse(file_data)
115  for schema in schemas:
116    # Schemas could consist of one API schema (data for a specific API file)
117    # or multiple (data from extension_api.json).
118    trim_and_inline(schema)
119  return schemas
120