Home | History | Annotate | Download | only in server2
      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):
     58   '''Replace '$ref's that refer to inline_docs with the json for those docs.
     59   '''
     60   types = schema.get('types')
     61   if types is None:
     62     return
     63 
     64   inline_docs = {}
     65   types_without_inline_doc = []
     66 
     67   # Gather the types with inline_doc.
     68   for type_ in types:
     69     if type_.get('inline_doc'):
     70       inline_docs[type_['id']] = type_
     71       for k in ('description', 'id', 'inline_doc'):
     72         type_.pop(k, None)
     73     else:
     74       types_without_inline_doc.append(type_)
     75   schema['types'] = types_without_inline_doc
     76 
     77   def apply_inline(node):
     78     if isinstance(node, list):
     79       for i in node:
     80         apply_inline(i)
     81     elif isinstance(node, Mapping):
     82       ref = node.get('$ref')
     83       if ref and ref in inline_docs:
     84         node.update(inline_docs[ref])
     85         del node['$ref']
     86       for k, v in node.iteritems():
     87         apply_inline(v)
     88 
     89   apply_inline(schema)
     90 
     91 
     92 def ProcessSchema(path, file_data):
     93   '''Parses |file_data| using a method determined by checking the
     94   extension of the file at the given |path|. Then, trims 'nodoc' and handles
     95   inlineable types from the parsed schema data.
     96   '''
     97   def trim_and_inline(schema, is_idl=False):
     98     '''Modifies an API schema in place by removing nodes that shouldn't be
     99     documented and inlining schema types that are only referenced once.
    100     '''
    101     if RemoveNoDocs(schema):
    102       # A return of True signifies that the entire schema should not be
    103       # documented. Otherwise, only nodes that request 'nodoc' are removed.
    104       return None
    105     if is_idl:
    106       DetectInlineableTypes(schema)
    107     InlineDocs(schema)
    108     return schema
    109 
    110   if path.endswith('.idl'):
    111     idl = idl_schema.IDLSchema(idl_parser.IDLParser().ParseData(file_data))
    112     # Wrap the result in a list so that it behaves like JSON API data.
    113     return [trim_and_inline(idl.process()[0], is_idl=True)]
    114 
    115   try:
    116     schemas = json_parse.Parse(file_data)
    117   except:
    118     raise ValueError('Cannot parse "%s" as JSON:\n%s' %
    119                      (path, traceback.format_exc()))
    120   for schema in schemas:
    121     # Schemas could consist of one API schema (data for a specific API file)
    122     # or multiple (data from extension_api.json).
    123     trim_and_inline(schema)
    124   return schemas
    125