Enable Enterprise enrollment on desktop builds.
[chromium-blink-merge.git] / chrome / common / extensions / docs / server2 / reference_resolver.py
blob93cfda2269c7b0f7f4352ab5b7b4f2f6f80bf8ef
1 # Copyright (c) 2012 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.
5 from copy import copy
6 import logging
7 import re
9 from file_system import FileNotFoundError
10 from third_party.json_schema_compiler.model import PropertyType
13 def _ClassifySchemaNode(node_name, node):
14 """Attempt to classify |node_name| in an API, determining whether |node_name|
15 refers to a type, function, event, or property in |api|.
16 """
17 if '.' in node_name:
18 node_name, rest = node_name.split('.', 1)
19 else:
20 rest = None
21 for key, group in [('types', 'type'),
22 ('functions', 'method'),
23 ('events', 'event'),
24 ('properties', 'property')]:
25 for item in getattr(node, key, {}).itervalues():
26 if item.simple_name == node_name:
27 if rest is not None:
28 ret = _ClassifySchemaNode(rest, item)
29 if ret is not None:
30 return ret
31 else:
32 return group, node_name
33 return None
36 def _MakeKey(namespace, ref):
37 key = '%s/%s' % (namespace, ref)
38 # AppEngine doesn't like keys > 500, but there will be some other stuff
39 # that goes into this key, so truncate it earlier. This shoudn't be
40 # happening anyway unless there's a bug, such as http://crbug.com/314102.
41 max_size = 256
42 if len(key) > max_size:
43 logging.error('Key was >%s characters: %s' % (max_size, key))
44 key = key[:max_size]
45 return key
48 class ReferenceResolver(object):
49 """Resolves references to $ref's by searching through the APIs to find the
50 correct node. See document_renderer.py for more information on $ref syntax.
51 """
52 def __init__(self, api_models, object_store):
53 self._api_models = api_models
54 self._object_store = object_store
56 def _GetRefLink(self, ref, api_list, namespace):
57 # Check nodes within each API the ref might refer to.
58 parts = ref.split('.')
59 for i in xrange(1, len(parts)):
60 api_name = '.'.join(parts[:i])
61 if api_name not in api_list:
62 continue
63 try:
64 api_model = self._api_models.GetModel(api_name).Get()
65 except FileNotFoundError:
66 continue
67 name = '.'.join(parts[i:])
68 # Attempt to find |name| in the API.
69 node_info = _ClassifySchemaNode(name, api_model)
70 if node_info is None:
71 # Check to see if this ref is a property. If it is, we want the ref to
72 # the underlying type the property is referencing.
73 for prop in api_model.properties.itervalues():
74 # If the name of this property is in the ref text, replace the
75 # property with its type, and attempt to classify it.
76 if prop.name in name and prop.type_.property_type == PropertyType.REF:
77 name_as_prop_type = name.replace(prop.name, prop.type_.ref_type)
78 node_info = _ClassifySchemaNode(name_as_prop_type, api_model)
79 if node_info is not None:
80 name = name_as_prop_type
81 text = ref.replace(prop.name, prop.type_.ref_type)
82 break
83 if node_info is None:
84 continue
85 else:
86 text = ref
87 category, node_name = node_info
88 if namespace is not None and text.startswith('%s.' % namespace):
89 text = text[len('%s.' % namespace):]
90 api_model = self._api_models.GetModel(api_name).Get()
91 filename = api_model.documentation_options.get('documented_in', api_name)
92 return {
93 'href': '%s#%s-%s' % (filename, category, name.replace('.', '-')),
94 'text': text,
95 'name': node_name
98 # If it's not a reference to an API node it might just be a reference to an
99 # API. Check this last so that links within APIs take precedence over links
100 # to other APIs.
101 if ref in api_list:
102 return {
103 'href': '%s' % ref,
104 'text': ref,
105 'name': ref
108 return None
110 def GetLink(self, ref, namespace=None, title=None):
111 """Resolve $ref |ref| in namespace |namespace| if not None, returning None
112 if it cannot be resolved.
114 db_key = _MakeKey(namespace, ref)
115 link = self._object_store.Get(db_key).Get()
116 if link is None:
117 api_list = self._api_models.GetNames()
118 link = self._GetRefLink(ref, api_list, namespace)
119 if link is None and namespace is not None:
120 # Try to resolve the ref in the current namespace if there is one.
121 link = self._GetRefLink('%s.%s' % (namespace, ref), api_list, namespace)
122 if link is None:
123 return None
124 self._object_store.Set(db_key, link)
126 if title is not None:
127 link = copy(link)
128 link['text'] = title
130 return link
132 def SafeGetLink(self, ref, namespace=None, title=None):
133 """Resolve $ref |ref| in namespace |namespace|, or globally if None. If it
134 cannot be resolved, pretend like it is a link to a type.
136 ref_data = self.GetLink(ref, namespace=namespace, title=title)
137 if ref_data is not None:
138 return ref_data
139 logging.error('$ref %s could not be resolved in namespace %s.' %
140 (ref, namespace))
141 type_name = ref.rsplit('.', 1)[-1]
142 return {
143 'href': '#type-%s' % type_name,
144 'text': title or ref,
145 'name': ref