15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import copy
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import logging
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import os
890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)from collections import defaultdict, Mapping
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdochfrom branch_utility import BranchUtility
11ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdochimport svn_constants
12ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdochfrom third_party.handlebar import Handlebar
132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import third_party.json_schema_compiler.json_parse as json_parse
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import third_party.json_schema_compiler.model as model
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import third_party.json_schema_compiler.idl_schema as idl_schema
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import third_party.json_schema_compiler.idl_parser as idl_parser
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def _RemoveNoDocs(item):
192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if json_parse.IsDict(item):
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if item.get('nodoc', False):
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return True
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for key, value in item.items():
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if _RemoveNoDocs(value):
24c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        del item[key]
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  elif type(item) == list:
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    to_remove = []
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for i in item:
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if _RemoveNoDocs(i):
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        to_remove.append(i)
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for i in to_remove:
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      item.remove(i)
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return False
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)def _DetectInlineableTypes(schema):
3590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  """Look for documents that are only referenced once and mark them as inline.
3690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  Actual inlining is done by _InlineDocs.
3790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  """
3890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  if not schema.get('types'):
3990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    return
4090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
41868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  ignore = frozenset(('value', 'choices'))
4290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  refcounts = defaultdict(int)
4390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  # Use an explicit stack instead of recursion.
4490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  stack = [schema]
4590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
4690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  while stack:
4790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    node = stack.pop()
4890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    if isinstance(node, list):
4990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      stack.extend(node)
5090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    elif isinstance(node, Mapping):
5190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      if '$ref' in node:
5290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        refcounts[node['$ref']] += 1
53868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      stack.extend(v for k, v in node.iteritems() if k not in ignore)
5490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
5590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  for type_ in schema['types']:
56868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    if not 'noinline_doc' in type_:
5790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      if refcounts[type_['id']] == 1:
5890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        type_['inline_doc'] = True
59c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
60c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)def _InlineDocs(schema):
61c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  """Replace '$ref's that refer to inline_docs with the json for those docs.
62c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  """
63c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  types = schema.get('types')
64c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if types is None:
65c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return
66c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
67c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  inline_docs = {}
68c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  types_without_inline_doc = []
69c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
70c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  # Gather the types with inline_doc.
71c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  for type_ in types:
72c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if type_.get('inline_doc'):
73c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      inline_docs[type_['id']] = type_
7490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      for k in ('description', 'id', 'inline_doc'):
7590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        type_.pop(k, None)
76c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    else:
77c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      types_without_inline_doc.append(type_)
78c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  schema['types'] = types_without_inline_doc
79c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
80c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  def apply_inline(node):
81c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if isinstance(node, list):
82c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      for i in node:
83c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        apply_inline(i)
8490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    elif isinstance(node, Mapping):
85c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      ref = node.get('$ref')
86c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      if ref and ref in inline_docs:
87c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        node.update(inline_docs[ref])
88c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        del node['$ref']
89c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      for k, v in node.iteritems():
90c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        apply_inline(v)
91c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
92c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  apply_inline(schema)
93c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def _CreateId(node, prefix):
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if node.parent is not None and not isinstance(node.parent, model.Namespace):
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return '-'.join([prefix, node.parent.simple_name, node.simple_name])
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return '-'.join([prefix, node.simple_name])
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def _FormatValue(value):
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Inserts commas every three digits for integer values. It is magic.
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  s = str(value)
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return ','.join([s[max(0, i - 3):i] for i in range(len(s), 0, -3)][::-1])
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class _JSCModel(object):
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Uses a Model from the JSON Schema Compiler and generates a dict that
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  a Handlebar template can use for a data source.
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
109ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  def __init__(self,
110ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch               json,
111ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch               ref_resolver,
112ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch               disable_refs,
113ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch               availability_finder,
114558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch               parse_cache,
115ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch               template_data_source,
116ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch               idl=False):
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._ref_resolver = ref_resolver
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._disable_refs = disable_refs
119ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    self._availability_finder = availability_finder
120558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch    self._intro_tables = parse_cache.GetFromFile(
121ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch        '%s/intro_tables.json' % svn_constants.JSON_PATH)
122558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch    self._api_features = parse_cache.GetFromFile(
123558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch        '%s/_api_features.json' % svn_constants.API_PATH)
124ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    self._template_data_source = template_data_source
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    clean_json = copy.deepcopy(json)
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if _RemoveNoDocs(clean_json):
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self._namespace = None
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else:
12990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      if idl:
13090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        _DetectInlineableTypes(clean_json)
131c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      _InlineDocs(clean_json)
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self._namespace = model.Namespace(clean_json, clean_json['namespace'])
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def _FormatDescription(self, description):
1352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if self._disable_refs:
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return description
1372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return self._ref_resolver.ResolveAllLinks(description,
1382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                              namespace=self._namespace.name)
1392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def _GetLink(self, link):
1412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if self._disable_refs:
1422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      type_name = link.split('.', 1)[-1]
1432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return { 'href': '#type-%s' % type_name, 'text': link, 'name': link }
1442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return self._ref_resolver.SafeGetLink(link, namespace=self._namespace.name)
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def ToDict(self):
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if self._namespace is None:
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return {}
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return {
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'name': self._namespace.name,
1512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      'types': self._GenerateTypes(self._namespace.types.values()),
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'functions': self._GenerateFunctions(self._namespace.functions),
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'events': self._GenerateEvents(self._namespace.events),
154ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      'properties': self._GenerateProperties(self._namespace.properties),
155ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      'intro_list': self._GetIntroTableList(),
156ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      'channel_warning': self._GetChannelWarning()
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
159ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  def _GetIntroTableList(self):
160ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    """Create a generic data structure that can be traversed by the templates
161ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    to create an API intro table.
162ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    """
163558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch    intro_rows = [
164558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch      self._GetIntroDescriptionRow(),
165558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch      self._GetIntroAvailabilityRow()
166558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch    ] + self._GetIntroDependencyRows()
167558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch
168558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch    # Add rows using data from intro_tables.json, overriding any existing rows
169558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch    # if they share the same 'title' attribute.
170558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch    row_titles = [row['title'] for row in intro_rows]
171558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch    for misc_row in self._GetMiscIntroRows():
172558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch      if misc_row['title'] in row_titles:
173558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch        intro_rows[row_titles.index(misc_row['title'])] = misc_row
174558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch      else:
175558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch        intro_rows.append(misc_row)
176558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch
177558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch    return intro_rows
178558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch
179558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch  def _GetIntroDescriptionRow(self):
180558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch    """ Generates the 'Description' row data for an API intro table.
181558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch    """
182558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch    return {
183ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      'title': 'Description',
184ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      'content': [
185ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch        { 'text': self._FormatDescription(self._namespace.description) }
186ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      ]
187558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch    }
188ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
189558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch  def _GetIntroAvailabilityRow(self):
190558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch    """ Generates the 'Availability' row data for an API intro table.
191558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch    """
192ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    if self._IsExperimental():
193ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      status = 'experimental'
194ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      version = None
195ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    else:
196ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      availability = self._GetApiAvailability()
197ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      status = availability.channel
198ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      version = availability.version
199558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch    return {
200ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      'title': 'Availability',
201558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch      'content': [{
202558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch        'partial': self._template_data_source.get(
203558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch            'intro_tables/%s_message.html' % status),
204558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch        'version': version
205558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch      }]
206558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch    }
207ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
208558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch  def _GetIntroDependencyRows(self):
209558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch    # Devtools aren't in _api_features. If we're dealing with devtools, bail.
210558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch    if 'devtools' in self._namespace.name:
211558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch      return []
212558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch    feature = self._api_features.get(self._namespace.name)
213558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch    assert feature, ('"%s" not found in _api_features.json.'
214558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch                     % self._namespace.name)
215558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch
216558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch    dependencies = feature.get('dependencies')
217558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch    if dependencies is None:
218558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch      return []
219558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch
220558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch    def make_code_node(text):
221558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch      return { 'class': 'code', 'text': text }
222558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch
223558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch    permissions_content = []
224558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch    manifest_content = []
225558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch
2263240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch    def categorize_dependency(dependency):
227558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch      context, name = dependency.split(':', 1)
228558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch      if context == 'permission':
229558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch        permissions_content.append(make_code_node('"%s"' % name))
230558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch      elif context == 'manifest':
231558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch        manifest_content.append(make_code_node('"%s": {...}' % name))
2323240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch      elif context == 'api':
2333240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch        transitive_dependencies = (
2343240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch            self._api_features.get(context, {}).get('dependencies', []))
2353240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch        for transitive_dependency in transitive_dependencies:
2363240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch          categorize_dependency(transitive_dependency)
237558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch      else:
2383240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch        raise ValueError('Unrecognized dependency for %s: %s' % (
2393240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch            self._namespace.name, context))
2403240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch
2413240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch    for dependency in dependencies:
2423240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch      categorize_dependency(dependency)
243558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch
244558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch    dependency_rows = []
245558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch    if permissions_content:
246558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch      dependency_rows.append({
247558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch        'title': 'Permissions',
248558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch        'content': permissions_content
249558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch      })
250558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch    if manifest_content:
251558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch      dependency_rows.append({
252558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch        'title': 'Manifest',
253558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch        'content': manifest_content
254558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch      })
255558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch    return dependency_rows
256558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch
257558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch  def _GetMiscIntroRows(self):
258558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch    """ Generates miscellaneous intro table row data, such as 'Permissions',
259558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch    'Samples', and 'Learn More', using intro_tables.json.
260558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch    """
261558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch    misc_rows = []
262558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch    # Look up the API name in intro_tables.json, which is structured
263558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch    # similarly to the data structure being created. If the name is found, loop
264558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch    # through the attributes and add them to this structure.
265ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    table_info = self._intro_tables.get(self._namespace.name)
266ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    if table_info is None:
267558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch      return misc_rows
268ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
269558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch    for category in table_info.keys():
270558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch      content = copy.deepcopy(table_info[category])
271ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      for node in content:
272ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch        # If there is a 'partial' argument and it hasn't already been
273ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch        # converted to a Handlebar object, transform it to a template.
274558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch        if 'partial' in node:
275ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch          node['partial'] = self._template_data_source.get(node['partial'])
276558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch      misc_rows.append({ 'title': category, 'content': content })
277558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch    return misc_rows
278ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
279ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  def _GetApiAvailability(self):
280ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    return self._availability_finder.GetApiAvailability(self._namespace.name)
281ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
282ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  def _GetChannelWarning(self):
283ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    if not self._IsExperimental():
284ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      return { self._GetApiAvailability().channel: True }
285ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    return None
286ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
287ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  def _IsExperimental(self):
288ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch     return self._namespace.name.startswith('experimental')
289ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
2902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def _GenerateTypes(self, types):
2912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return [self._GenerateType(t) for t in types]
2922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def _GenerateType(self, type_):
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    type_dict = {
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'name': type_.simple_name,
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'description': self._FormatDescription(type_.description),
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'properties': self._GenerateProperties(type_.properties),
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'functions': self._GenerateFunctions(type_.functions),
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'events': self._GenerateEvents(type_.events),
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'id': _CreateId(type_, 'type')
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._RenderTypeInformation(type_, type_dict)
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return type_dict
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def _GenerateFunctions(self, functions):
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return [self._GenerateFunction(f) for f in functions.values()]
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def _GenerateFunction(self, function):
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    function_dict = {
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'name': function.simple_name,
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'description': self._FormatDescription(function.description),
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'callback': self._GenerateCallback(function.callback),
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'parameters': [],
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'returns': None,
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'id': _CreateId(function, 'method')
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (function.parent is not None and
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        not isinstance(function.parent, model.Namespace)):
319bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch      function_dict['parentName'] = function.parent.simple_name
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if function.returns:
3212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      function_dict['returns'] = self._GenerateType(function.returns)
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for param in function.params:
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      function_dict['parameters'].append(self._GenerateProperty(param))
3242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if function.callback is not None:
3252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      # Show the callback as an extra parameter.
3262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      function_dict['parameters'].append(
3272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          self._GenerateCallbackProperty(function.callback))
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if len(function_dict['parameters']) > 0:
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      function_dict['parameters'][-1]['last'] = True
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return function_dict
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def _GenerateEvents(self, events):
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return [self._GenerateEvent(e) for e in events.values()]
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def _GenerateEvent(self, event):
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    event_dict = {
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'name': event.simple_name,
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'description': self._FormatDescription(event.description),
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'parameters': [self._GenerateProperty(p) for p in event.params],
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'callback': self._GenerateCallback(event.callback),
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'filters': [self._GenerateProperty(f) for f in event.filters],
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'conditions': [self._GetLink(condition)
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     for condition in event.conditions],
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'actions': [self._GetLink(action) for action in event.actions],
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'supportsRules': event.supports_rules,
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'id': _CreateId(event, 'event')
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (event.parent is not None and
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        not isinstance(event.parent, model.Namespace)):
350bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch      event_dict['parentName'] = event.parent.simple_name
3512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if event.callback is not None:
3522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      # Show the callback as an extra parameter.
3532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      event_dict['parameters'].append(
3542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          self._GenerateCallbackProperty(event.callback))
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if len(event_dict['parameters']) > 0:
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      event_dict['parameters'][-1]['last'] = True
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return event_dict
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def _GenerateCallback(self, callback):
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not callback:
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return None
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    callback_dict = {
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'name': callback.simple_name,
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'simple_type': {'simple_type': 'function'},
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'optional': callback.optional,
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'parameters': []
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for param in callback.params:
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      callback_dict['parameters'].append(self._GenerateProperty(param))
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (len(callback_dict['parameters']) > 0):
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      callback_dict['parameters'][-1]['last'] = True
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return callback_dict
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def _GenerateProperties(self, properties):
3752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return [self._GenerateProperty(v) for v in properties.values()]
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def _GenerateProperty(self, property_):
3782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if not hasattr(property_, 'type_'):
3792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      for d in dir(property_):
3802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if not d.startswith('_'):
3812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          print ('%s -> %s' % (d, getattr(property_, d)))
3822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    type_ = property_.type_
3832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    # Make sure we generate property info for arrays, too.
3852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    # TODO(kalman): what about choices?
3862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if type_.property_type == model.PropertyType.ARRAY:
3872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      properties = type_.item_type.properties
3882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    else:
3892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      properties = type_.properties
3902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    property_dict = {
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'name': property_.simple_name,
3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'optional': property_.optional,
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'description': self._FormatDescription(property_.description),
3952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      'properties': self._GenerateProperties(type_.properties),
3962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      'functions': self._GenerateFunctions(type_.functions),
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'parameters': [],
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'returns': None,
3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'id': _CreateId(property_, 'property')
4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
4012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if type_.property_type == model.PropertyType.FUNCTION:
4032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      function = type_.function
4042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      for param in function.params:
4052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        property_dict['parameters'].append(self._GenerateProperty(param))
4062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if function.returns:
4072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        property_dict['returns'] = self._GenerateType(function.returns)
4082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (property_.parent is not None and
4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        not isinstance(property_.parent, model.Namespace)):
411bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch      property_dict['parentName'] = property_.parent.simple_name
4122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    value = property_.value
4142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if value is not None:
4152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if isinstance(value, int):
4162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        property_dict['value'] = _FormatValue(value)
4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      else:
4182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        property_dict['value'] = value
4195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else:
4202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      self._RenderTypeInformation(type_, property_dict)
4212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return property_dict
4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def _GenerateCallbackProperty(self, callback):
4252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    property_dict = {
4262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      'name': callback.simple_name,
4272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      'description': self._FormatDescription(callback.description),
4282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      'optional': callback.optional,
4292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      'id': _CreateId(callback, 'property'),
4302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      'simple_type': 'function',
4312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
4322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (callback.parent is not None and
4332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        not isinstance(callback.parent, model.Namespace)):
434bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch      property_dict['parentName'] = callback.parent.simple_name
4352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return property_dict
4362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def _RenderTypeInformation(self, type_, dst_dict):
438c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    dst_dict['is_object'] = type_.property_type == model.PropertyType.OBJECT
4392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if type_.property_type == model.PropertyType.CHOICES:
4402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      dst_dict['choices'] = self._GenerateTypes(type_.choices)
441c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      # We keep track of which == last for knowing when to add "or" between
4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # choices in templates.
4435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if len(dst_dict['choices']) > 0:
4445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        dst_dict['choices'][-1]['last'] = True
4452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    elif type_.property_type == model.PropertyType.REF:
4462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      dst_dict['link'] = self._GetLink(type_.ref_type)
4472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    elif type_.property_type == model.PropertyType.ARRAY:
4482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      dst_dict['array'] = self._GenerateType(type_.item_type)
4492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    elif type_.property_type == model.PropertyType.ENUM:
4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      dst_dict['enum_values'] = []
4512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      for enum_value in type_.enum_values:
4525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        dst_dict['enum_values'].append({'name': enum_value})
4535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if len(dst_dict['enum_values']) > 0:
4545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        dst_dict['enum_values'][-1]['last'] = True
4552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    elif type_.instance_of is not None:
4562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      dst_dict['simple_type'] = type_.instance_of.lower()
4575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else:
4582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      dst_dict['simple_type'] = type_.property_type.name.lower()
4595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class _LazySamplesGetter(object):
4615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """This class is needed so that an extensions API page does not have to fetch
4625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  the apps samples page and vice versa.
4635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
4645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __init__(self, api_name, samples):
4655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._api_name = api_name
4665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._samples = samples
4675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def get(self, key):
4695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return self._samples.FilterSamples(key, self._api_name)
4705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class APIDataSource(object):
4725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """This class fetches and loads JSON APIs from the FileSystem passed in with
473c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  |compiled_fs_factory|, so the APIs can be plugged into templates.
4745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
4755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  class Factory(object):
476ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    def __init__(self,
477ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch                 compiled_fs_factory,
478ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch                 base_path,
479ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch                 availability_finder_factory):
480c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      def create_compiled_fs(fn, category):
481c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        return compiled_fs_factory.Create(fn, APIDataSource, category=category)
482c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
483c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      self._json_cache = create_compiled_fs(
4842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          lambda api_name, api: self._LoadJsonAPI(api, False),
485c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          'json')
486c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      self._idl_cache = create_compiled_fs(
4872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          lambda api_name, api: self._LoadIdlAPI(api, False),
488c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          'idl')
4895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # These caches are used if an APIDataSource does not want to resolve the
4915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # $refs in an API. This is needed to prevent infinite recursion in
4925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # ReferenceResolver.
493c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      self._json_cache_no_refs = create_compiled_fs(
4942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          lambda api_name, api: self._LoadJsonAPI(api, True),
495c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          'json-no-refs')
496c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      self._idl_cache_no_refs = create_compiled_fs(
4972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          lambda api_name, api: self._LoadIdlAPI(api, True),
498c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          'idl-no-refs')
499c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
500c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      self._idl_names_cache = create_compiled_fs(self._GetIDLNames, 'idl-names')
501c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      self._names_cache = create_compiled_fs(self._GetAllNames, 'names')
502c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
5035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self._base_path = base_path
504ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      self._availability_finder = availability_finder_factory.Create()
505558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch      self._parse_cache = create_compiled_fs(
506ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch          lambda _, json: json_parse.Parse(json),
507ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch          'intro-cache')
5085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # These must be set later via the SetFooDataSourceFactory methods.
5095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self._ref_resolver_factory = None
5105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self._samples_data_source_factory = None
5115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    def SetSamplesDataSourceFactory(self, samples_data_source_factory):
5135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self._samples_data_source_factory = samples_data_source_factory
5145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    def SetReferenceResolverFactory(self, ref_resolver_factory):
5165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self._ref_resolver_factory = ref_resolver_factory
5175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
518ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    def SetTemplateDataSource(self, template_data_source_factory):
519ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      # This TemplateDataSource is only being used for fetching template data.
520ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      self._template_data_source = template_data_source_factory.Create(None, '')
521ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
5225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    def Create(self, request, disable_refs=False):
5235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      """Create an APIDataSource. |disable_refs| specifies whether $ref's in
5245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      APIs being processed by the |ToDict| method of _JSCModel follows $ref's
5255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      in the API. This prevents endless recursion in ReferenceResolver.
5265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      """
5275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if self._samples_data_source_factory is None:
5285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # Only error if there is a request, which means this APIDataSource is
5295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # actually being used to render a page.
5305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if request is not None:
5315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          logging.error('SamplesDataSource.Factory was never set in '
5325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        'APIDataSource.Factory.')
5335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        samples = None
5345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      else:
5355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        samples = self._samples_data_source_factory.Create(request)
5365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if not disable_refs and self._ref_resolver_factory is None:
5375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        logging.error('ReferenceResolver.Factory was never set in '
5385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      'APIDataSource.Factory.')
539ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      return APIDataSource(self._json_cache,
5405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           self._idl_cache,
5415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           self._json_cache_no_refs,
5425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           self._idl_cache_no_refs,
5435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           self._names_cache,
5445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           self._idl_names_cache,
5455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           self._base_path,
5465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           samples,
5475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           disable_refs)
5485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    def _LoadJsonAPI(self, api, disable_refs):
5505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return _JSCModel(
5512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          json_parse.Parse(api)[0],
5525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          self._ref_resolver_factory.Create() if not disable_refs else None,
553ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch          disable_refs,
554ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch          self._availability_finder,
555558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch          self._parse_cache,
556ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch          self._template_data_source).ToDict()
5575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    def _LoadIdlAPI(self, api, disable_refs):
5595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      idl = idl_parser.IDLParser().ParseData(api)
5605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return _JSCModel(
5615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          idl_schema.IDLSchema(idl).process()[0],
5625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          self._ref_resolver_factory.Create() if not disable_refs else None,
56390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)          disable_refs,
564ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch          self._availability_finder,
565558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch          self._parse_cache,
566ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch          self._template_data_source,
56790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)          idl=True).ToDict()
5685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    def _GetIDLNames(self, base_dir, apis):
570c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      return self._GetExtNames(apis, ['idl'])
5715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    def _GetAllNames(self, base_dir, apis):
573c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      return self._GetExtNames(apis, ['json', 'idl'])
574c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
575c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    def _GetExtNames(self, apis, exts):
576c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      return [model.UnixName(os.path.splitext(api)[0]) for api in apis
577c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)              if os.path.splitext(api)[1][1:] in exts]
5785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __init__(self,
5805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               json_cache,
5815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               idl_cache,
5825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               json_cache_no_refs,
5835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               idl_cache_no_refs,
5845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               names_cache,
5855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               idl_names_cache,
5865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               base_path,
5875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               samples,
5885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               disable_refs):
5895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._base_path = base_path
5905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._json_cache = json_cache
5915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._idl_cache = idl_cache
5925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._json_cache_no_refs = json_cache_no_refs
5935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._idl_cache_no_refs = idl_cache_no_refs
5945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._names_cache = names_cache
5955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._idl_names_cache = idl_names_cache
5965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._samples = samples
5975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._disable_refs = disable_refs
5985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def _GenerateHandlebarContext(self, handlebar_dict, path):
6002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    handlebar_dict['samples'] = _LazySamplesGetter(path, self._samples)
6012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return handlebar_dict
6025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def _GetAsSubdirectory(self, name):
6045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if name.startswith('experimental_'):
6055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      parts = name[len('experimental_'):].split('_', 1)
606ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      if len(parts) > 1:
607ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch        parts[1] = 'experimental_%s' % parts[1]
608ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch        return '/'.join(parts)
609ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      return '%s/%s' % (parts[0], name)
6105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return name.replace('_', '/', 1)
6115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def get(self, key):
6135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if key.endswith('.html') or key.endswith('.json') or key.endswith('.idl'):
6145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      path, ext = os.path.splitext(key)
6155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else:
6165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      path = key
6175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    unix_name = model.UnixName(path)
6185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    idl_names = self._idl_names_cache.GetFromFileListing(self._base_path)
6195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    names = self._names_cache.GetFromFileListing(self._base_path)
6205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if unix_name not in names and self._GetAsSubdirectory(unix_name) in names:
6215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      unix_name = self._GetAsSubdirectory(unix_name)
6225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if self._disable_refs:
6245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      cache, ext = (
6255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          (self._idl_cache_no_refs, '.idl') if (unix_name in idl_names) else
6265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          (self._json_cache_no_refs, '.json'))
6275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else:
6285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      cache, ext = ((self._idl_cache, '.idl') if (unix_name in idl_names) else
6295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    (self._json_cache, '.json'))
6305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return self._GenerateHandlebarContext(
6315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        cache.GetFromFile('%s/%s%s' % (self._base_path, unix_name, ext)),
6325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        path)
633