Chromium Code Reviews| 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): |
|
not at google - send to devlin
2013/11/01 17:12:04
caching the title here is silly. the expensive com
| |
| 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_list_data_source_factory.Create(), | 76 self._api_list_data_source_factory.Create(), |
| 64 self._object_store_creator.Create(ReferenceResolver)) | 77 self._object_store_creator.Create(ReferenceResolver)) |
| 65 | 78 |
| 66 def __init__(self, api_data_source, api_list_data_source, object_store): | 79 def __init__(self, api_data_source, api_list_data_source, object_store): |
| 67 self._api_data_source = api_data_source | 80 self._api_data_source = api_data_source |
| 68 self._api_list_data_source = api_list_data_source | 81 self._api_list_data_source = api_list_data_source |
| 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_list_data_source.GetAllNames() |
| 131 api_list = self._api_list_data_source.GetAllNames() | 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) |
|
Jeffrey Yasskin
2013/11/01 18:00:24
Does the object store actually return the same obj
not at google - send to devlin
2013/11/04 21:51:01
The in-memory one does. Copy-if-you-need-to[-mutat
| |
| 140 | 153 if title is not None: |
|
not at google - send to devlin
2013/11/01 17:12:04
this is all-but-identical code. rietveld is just b
| |
| 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 |