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.
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|.
18 node_name
, rest
= node_name
.split('.', 1)
21 for key
, group
in [('types', 'type'),
22 ('functions', 'method'),
24 ('properties', 'property')]:
25 for item
in getattr(node
, key
, {}).itervalues():
26 if item
.simple_name
== node_name
:
28 ret
= _ClassifySchemaNode(rest
, item
)
32 return group
, node_name
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.
42 if len(key
) > max_size
:
43 logging
.error('Key was >%s characters: %s' % (max_size
, 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.
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
:
64 api_model
= self
._api
_models
.GetModel(api_name
).Get()
65 except FileNotFoundError
:
67 name
= '.'.join(parts
[i
:])
68 # Attempt to find |name| in the API.
69 node_info
= _ClassifySchemaNode(name
, api_model
)
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
)
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
)
93 'href': '%s#%s-%s' % (filename
, category
, name
.replace('.', '-')),
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
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()
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
)
124 self
._object
_store
.Set(db_key
, link
)
126 if title
is not None:
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:
139 logging
.error('$ref %s could not be resolved in namespace %s.' %
141 type_name
= ref
.rsplit('.', 1)[-1]
143 'href': '#type-%s' % type_name
,
144 'text': title
or ref
,