OLD | NEW |
1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 from file_system import FileNotFoundError | 5 from copy import deepcopy |
6 import logging | 6 import logging |
7 import re | 7 import re |
8 import string | 8 import string |
9 | 9 |
| 10 from file_system import FileNotFoundError |
| 11 |
| 12 |
10 def _ClassifySchemaNode(node_name, api): | 13 def _ClassifySchemaNode(node_name, api): |
11 """Attempt to classify |node_name| in an API, determining whether |node_name| | 14 """Attempt to classify |node_name| in an API, determining whether |node_name| |
12 refers to a type, function, event, or property in |api|. | 15 refers to a type, function, event, or property in |api|. |
13 """ | 16 """ |
14 if '.' in node_name: | 17 if '.' in node_name: |
15 node_name, rest = node_name.split('.', 1) | 18 node_name, rest = node_name.split('.', 1) |
16 else: | 19 else: |
17 rest = None | 20 rest = None |
18 for key, group in [('types', 'type'), | 21 for key, group in [('types', 'type'), |
19 ('functions', 'method'), | 22 ('functions', 'method'), |
20 ('events', 'event'), | 23 ('events', 'event'), |
21 ('properties', 'property')]: | 24 ('properties', 'property')]: |
22 for item in api.get(key, []): | 25 for item in api.get(key, []): |
23 if item['name'] == node_name: | 26 if item['name'] == node_name: |
24 if rest is not None: | 27 if rest is not None: |
25 ret = _ClassifySchemaNode(rest, item) | 28 ret = _ClassifySchemaNode(rest, item) |
26 if ret is not None: | 29 if ret is not None: |
27 return ret | 30 return ret |
28 else: | 31 else: |
29 return group, node_name | 32 return group, node_name |
30 return None | 33 return None |
31 | 34 |
32 def _MakeKey(namespace, ref, title): | 35 |
33 return '%s.%s.%s' % (namespace, ref, title) | 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 |
| 46 |
34 | 47 |
35 class ReferenceResolver(object): | 48 class ReferenceResolver(object): |
36 """Resolves references to $ref's by searching through the APIs to find the | 49 """Resolves references to $ref's by searching through the APIs to find the |
37 correct node. | 50 correct node. |
38 | 51 |
39 $ref's have two forms: | 52 $ref's have two forms: |
40 | 53 |
41 $ref:api.node - Replaces the $ref with a link to node on the API page. The | 54 $ref:api.node - Replaces the $ref with a link to node on the API page. The |
42 title is set to the name of the node. | 55 title is set to the name of the node. |
43 | 56 |
(...skipping 17 matching lines...) Expand all Loading... |
61 return ReferenceResolver( | 74 return ReferenceResolver( |
62 self._api_data_source_factory.Create(None), | 75 self._api_data_source_factory.Create(None), |
63 self._api_models, | 76 self._api_models, |
64 self._object_store_creator.Create(ReferenceResolver)) | 77 self._object_store_creator.Create(ReferenceResolver)) |
65 | 78 |
66 def __init__(self, api_data_source, api_models, object_store): | 79 def __init__(self, api_data_source, api_models, object_store): |
67 self._api_data_source = api_data_source | 80 self._api_data_source = api_data_source |
68 self._api_models = api_models | 81 self._api_models = api_models |
69 self._object_store = object_store | 82 self._object_store = object_store |
70 | 83 |
71 def _GetRefLink(self, ref, api_list, namespace, title): | 84 def _GetRefLink(self, ref, api_list, namespace): |
72 # Check nodes within each API the ref might refer to. | 85 # Check nodes within each API the ref might refer to. |
73 parts = ref.split('.') | 86 parts = ref.split('.') |
74 for i, part in enumerate(parts): | 87 for i, part in enumerate(parts): |
75 api_name = '.'.join(parts[:i]) | 88 api_name = '.'.join(parts[:i]) |
76 if api_name not in api_list: | 89 if api_name not in api_list: |
77 continue | 90 continue |
78 try: | 91 try: |
79 api = self._api_data_source.get(api_name, disable_refs=True) | 92 api = self._api_data_source.get(api_name, disable_refs=True) |
80 except FileNotFoundError: | 93 except FileNotFoundError: |
81 continue | 94 continue |
(...skipping 15 matching lines...) Expand all Loading... |
97 break | 110 break |
98 if node_info is None: | 111 if node_info is None: |
99 continue | 112 continue |
100 else: | 113 else: |
101 text = ref | 114 text = ref |
102 category, node_name = node_info | 115 category, node_name = node_info |
103 if namespace is not None and text.startswith('%s.' % namespace): | 116 if namespace is not None and text.startswith('%s.' % namespace): |
104 text = text[len('%s.' % namespace):] | 117 text = text[len('%s.' % namespace):] |
105 return { | 118 return { |
106 'href': '%s.html#%s-%s' % (api_name, category, name.replace('.', '-')), | 119 'href': '%s.html#%s-%s' % (api_name, category, name.replace('.', '-')), |
107 'text': title if title else text, | 120 'text': text, |
108 'name': node_name | 121 'name': node_name |
109 } | 122 } |
110 | 123 |
111 # If it's not a reference to an API node it might just be a reference to an | 124 # If it's not a reference to an API node it might just be a reference to an |
112 # API. Check this last so that links within APIs take precedence over links | 125 # API. Check this last so that links within APIs take precedence over links |
113 # to other APIs. | 126 # to other APIs. |
114 if ref in api_list: | 127 if ref in api_list: |
115 return { | 128 return { |
116 'href': '%s.html' % ref, | 129 'href': '%s.html' % ref, |
117 'text': title if title else ref, | 130 'text': ref, |
118 'name': ref | 131 'name': ref |
119 } | 132 } |
120 | 133 |
121 return None | 134 return None |
122 | 135 |
123 def GetLink(self, ref, namespace=None, title=None): | 136 def GetLink(self, ref, namespace=None, title=None): |
124 """Resolve $ref |ref| in namespace |namespace| if not None, returning None | 137 """Resolve $ref |ref| in namespace |namespace| if not None, returning None |
125 if it cannot be resolved. | 138 if it cannot be resolved. |
126 """ | 139 """ |
127 link = self._object_store.Get(_MakeKey(namespace, ref, title)).Get() | 140 db_key = _MakeKey(namespace, ref) |
128 if link is not None: | 141 link = self._object_store.Get(db_key).Get() |
129 return link | 142 if link is None: |
130 | 143 api_list = self._api_models.GetNames() |
131 api_list = self._api_models.GetNames() | 144 link = self._GetRefLink(ref, api_list, namespace) |
132 link = self._GetRefLink(ref, api_list, namespace, title) | 145 if link is None and namespace is not None: |
133 | 146 # Try to resolve the ref in the current namespace if there is one. |
134 if link is None and namespace is not None: | 147 link = self._GetRefLink('%s.%s' % (namespace, ref), api_list, namespace) |
135 # Try to resolve the ref in the current namespace if there is one. | 148 if link is None: |
136 link = self._GetRefLink('%s.%s' % (namespace, ref), | 149 return None |
137 api_list, | 150 self._object_store.Set(db_key, link) |
138 namespace, | 151 else: |
139 title) | 152 link = deepcopy(link) |
140 | 153 if title is not None: |
141 if link is not None: | 154 link['text'] = title |
142 self._object_store.Set(_MakeKey(namespace, ref, title), link) | |
143 return link | 155 return link |
144 | 156 |
145 def SafeGetLink(self, ref, namespace=None, title=None): | 157 def SafeGetLink(self, ref, namespace=None, title=None): |
146 """Resolve $ref |ref| in namespace |namespace|, or globally if None. If it | 158 """Resolve $ref |ref| in namespace |namespace|, or globally if None. If it |
147 cannot be resolved, pretend like it is a link to a type. | 159 cannot be resolved, pretend like it is a link to a type. |
148 """ | 160 """ |
149 ref_data = self.GetLink(ref, namespace=namespace, title=title) | 161 ref_data = self.GetLink(ref, namespace=namespace, title=title) |
150 if ref_data is not None: | 162 if ref_data is not None: |
151 return ref_data | 163 return ref_data |
152 logging.error('$ref %s could not be resolved in namespace %s.' % | 164 logging.error('$ref %s could not be resolved in namespace %s.' % |
153 (ref, namespace)) | 165 (ref, namespace)) |
154 type_name = ref.rsplit('.', 1)[-1] | 166 type_name = ref.rsplit('.', 1)[-1] |
155 return { | 167 return { |
156 'href': '#type-%s' % type_name, | 168 'href': '#type-%s' % type_name, |
157 'text': title if title else ref, | 169 'text': title or ref, |
158 'name': ref | 170 'name': ref |
159 } | 171 } |
160 | 172 |
161 def ResolveAllLinks(self, text, namespace=None): | 173 def ResolveAllLinks(self, text, namespace=None): |
162 """This method will resolve all $ref links in |text| using namespace | 174 """This method will resolve all $ref links in |text| using namespace |
163 |namespace| if not None. Any links that cannot be resolved will be replaced | 175 |namespace| if not None. Any links that cannot be resolved will be replaced |
164 using the default link format that |SafeGetLink| uses. | 176 using the default link format that |SafeGetLink| uses. |
165 """ | 177 """ |
166 if text is None or '$ref:' not in text: | 178 if text is None or '$ref:' not in text: |
167 return text | 179 return text |
(...skipping 20 matching lines...) Expand all Loading... |
188 ref = '' | 200 ref = '' |
189 rest = ref_and_rest | 201 rest = ref_and_rest |
190 else: | 202 else: |
191 ref = match.group() | 203 ref = match.group() |
192 rest = ref_and_rest[match.end():] | 204 rest = ref_and_rest[match.end():] |
193 | 205 |
194 ref_dict = self.SafeGetLink(ref, namespace=namespace, title=title) | 206 ref_dict = self.SafeGetLink(ref, namespace=namespace, title=title) |
195 formatted_text.append('<a href="%(href)s">%(text)s</a>%(rest)s' % | 207 formatted_text.append('<a href="%(href)s">%(text)s</a>%(rest)s' % |
196 { 'href': ref_dict['href'], 'text': ref_dict['text'], 'rest': rest }) | 208 { 'href': ref_dict['href'], 'text': ref_dict['text'], 'rest': rest }) |
197 return ''.join(formatted_text) | 209 return ''.join(formatted_text) |
OLD | NEW |