Index: pkg/analysis_server/tool/spec/from_html.dart |
diff --git a/pkg/analysis_server/tool/spec/from_html.dart b/pkg/analysis_server/tool/spec/from_html.dart |
index 2d04bed46e4f95df4d706137b07a829b053a3ec8..aac8269c6da5fbf7dcb83e9138decd9c4336e5ea 100644 |
--- a/pkg/analysis_server/tool/spec/from_html.dart |
+++ b/pkg/analysis_server/tool/spec/from_html.dart |
@@ -14,548 +14,593 @@ import 'package:path/path.dart'; |
import 'api.dart'; |
-const List<String> specialElements = const [ |
- 'domain', |
- 'feedback', |
- 'object', |
- 'refactorings', |
- 'refactoring', |
- 'type', |
- 'types', |
- 'request', |
- 'notification', |
- 'params', |
- 'result', |
- 'field', |
- 'list', |
- 'map', |
- 'enum', |
- 'key', |
- 'value', |
- 'options', |
- 'ref', |
- 'code', |
- 'version', |
- 'union', |
- 'index' |
-]; |
- |
/** |
- * Create an [Api] object from an HTML representation such as: |
- * |
- * <html> |
- * ... |
- * <body> |
- * ... <version>1.0</version> ... |
- * <domain name="...">...</domain> <!-- zero or more --> |
- * <types>...</types> |
- * <refactorings>...</refactorings> |
- * </body> |
- * </html> |
- * |
- * Child elements of <api> can occur in any order. |
+ * Read the API description from the file 'plugin_spec.html'. [pkgPath] is the |
+ * path to the current package. |
*/ |
-Api apiFromHtml(dom.Element html) { |
- Api api; |
- List<String> versions = <String>[]; |
- List<Domain> domains = <Domain>[]; |
- Types types = null; |
- Refactorings refactorings = null; |
- recurse(html, 'api', { |
- 'domain': (dom.Element element) { |
- domains.add(domainFromHtml(element)); |
- }, |
- 'refactorings': (dom.Element element) { |
- refactorings = refactoringsFromHtml(element); |
- }, |
- 'types': (dom.Element element) { |
- types = typesFromHtml(element); |
- }, |
- 'version': (dom.Element element) { |
- versions.add(innerText(element)); |
- }, |
- 'index': (dom.Element element) { |
- /* Ignore; generated dynamically. */ |
+Api readApi(String pkgPath) { |
+ ApiReader reader = |
+ new ApiReader(join(pkgPath, 'tool', 'spec', 'spec_input.html')); |
+ return reader.readApi(); |
+} |
+ |
+typedef void ElementProcessor(dom.Element element); |
+ |
+typedef void TextProcessor(dom.Text text); |
+ |
+class ApiReader { |
+ static const List<String> specialElements = const [ |
+ 'domain', |
+ 'feedback', |
+ 'object', |
+ 'refactorings', |
+ 'refactoring', |
+ 'type', |
+ 'types', |
+ 'request', |
+ 'notification', |
+ 'params', |
+ 'result', |
+ 'field', |
+ 'list', |
+ 'map', |
+ 'enum', |
+ 'key', |
+ 'value', |
+ 'options', |
+ 'ref', |
+ 'code', |
+ 'version', |
+ 'union', |
+ 'index', |
+ 'include' |
+ ]; |
+ |
+ /** |
+ * The absolute and normalized path to the file being read. |
+ */ |
+ final String filePath; |
+ |
+ /** |
+ * Initialize a newly created API reader to read from the file with the given |
+ * [filePath]. |
+ */ |
+ ApiReader(this.filePath); |
+ |
+ /** |
+ * Create an [Api] object from an HTML representation such as: |
+ * |
+ * <html> |
+ * ... |
+ * <body> |
+ * ... <version>1.0</version> ... |
+ * <domain name="...">...</domain> <!-- zero or more --> |
+ * <types>...</types> |
+ * <refactorings>...</refactorings> |
+ * </body> |
+ * </html> |
+ * |
+ * Child elements of <api> can occur in any order. |
+ */ |
+ Api apiFromHtml(dom.Element html) { |
+ Api api; |
+ List<String> versions = <String>[]; |
+ List<Domain> domains = <Domain>[]; |
+ Types types = null; |
+ Refactorings refactorings = null; |
+ recurse(html, 'api', { |
+ 'domain': (dom.Element element) { |
+ domains.add(domainFromHtml(element)); |
+ }, |
+ 'refactorings': (dom.Element element) { |
+ refactorings = refactoringsFromHtml(element); |
+ }, |
+ 'types': (dom.Element element) { |
+ types = typesFromHtml(element); |
+ }, |
+ 'version': (dom.Element element) { |
+ versions.add(innerText(element)); |
+ }, |
+ 'index': (dom.Element element) { |
+ /* Ignore; generated dynamically. */ |
+ } |
+ }); |
+ if (versions.length != 1) { |
+ throw new Exception('The API must contain exactly one <version> element'); |
} |
- }); |
- if (versions.length != 1) { |
- throw new Exception('The API must contain exactly one <version> element'); |
+ api = new Api(versions[0], domains, types, refactorings, html); |
+ return api; |
} |
- api = new Api(versions[0], domains, types, refactorings, html); |
- return api; |
-} |
-/** |
- * Check that the given [element] has all of the attributes in |
- * [requiredAttributes], possibly some of the attributes in |
- * [optionalAttributes], and no others. |
- */ |
-void checkAttributes( |
- dom.Element element, List<String> requiredAttributes, String context, |
- {List<String> optionalAttributes: const []}) { |
- Set<String> attributesFound = new Set<String>(); |
- element.attributes.forEach((String name, String value) { |
- if (!requiredAttributes.contains(name) && |
- !optionalAttributes.contains(name)) { |
- throw new Exception( |
- '$context: Unexpected attribute in ${element.localName}: $name'); |
+ /** |
+ * Check that the given [element] has all of the attributes in |
+ * [requiredAttributes], possibly some of the attributes in |
+ * [optionalAttributes], and no others. |
+ */ |
+ void checkAttributes( |
+ dom.Element element, List<String> requiredAttributes, String context, |
+ {List<String> optionalAttributes: const []}) { |
+ Set<String> attributesFound = new Set<String>(); |
+ element.attributes.forEach((String name, String value) { |
+ if (!requiredAttributes.contains(name) && |
+ !optionalAttributes.contains(name)) { |
+ throw new Exception( |
+ '$context: Unexpected attribute in ${element.localName}: $name'); |
+ } |
+ attributesFound.add(name); |
+ }); |
+ for (String expectedAttribute in requiredAttributes) { |
+ if (!attributesFound.contains(expectedAttribute)) { |
+ throw new Exception( |
+ '$context: ${element.localName} must contain attribute $expectedAttribute'); |
+ } |
} |
- attributesFound.add(name); |
- }); |
- for (String expectedAttribute in requiredAttributes) { |
- if (!attributesFound.contains(expectedAttribute)) { |
+ } |
+ |
+ /** |
+ * Check that the given [element] has the given [expectedName]. |
+ */ |
+ void checkName(dom.Element element, String expectedName, [String context]) { |
+ if (element.localName != expectedName) { |
+ if (context == null) { |
+ context = element.localName; |
+ } |
throw new Exception( |
- '$context: ${element.localName} must contain attribute $expectedAttribute'); |
+ '$context: Expected $expectedName, found ${element.localName}'); |
} |
} |
-} |
-/** |
- * Check that the given [element] has the given [expectedName]. |
- */ |
-void checkName(dom.Element element, String expectedName, [String context]) { |
- if (element.localName != expectedName) { |
- if (context == null) { |
- context = element.localName; |
- } |
- throw new Exception( |
- '$context: Expected $expectedName, found ${element.localName}'); |
+ /** |
+ * Create a [Domain] object from an HTML representation such as: |
+ * |
+ * <domain name="domainName"> |
+ * <request method="...">...</request> <!-- zero or more --> |
+ * <notification event="...">...</notification> <!-- zero or more --> |
+ * </domain> |
+ * |
+ * Child elements can occur in any order. |
+ */ |
+ Domain domainFromHtml(dom.Element html) { |
+ checkName(html, 'domain'); |
+ String name = html.attributes['name']; |
+ String context = name ?? 'domain'; |
+ bool experimental = html.attributes['experimental'] == 'true'; |
+ checkAttributes(html, ['name'], context, |
+ optionalAttributes: ['experimental']); |
+ List<Request> requests = <Request>[]; |
+ List<Notification> notifications = <Notification>[]; |
+ recurse(html, context, { |
+ 'request': (dom.Element child) { |
+ requests.add(requestFromHtml(child, context)); |
+ }, |
+ 'notification': (dom.Element child) { |
+ notifications.add(notificationFromHtml(child, context)); |
+ } |
+ }); |
+ return new Domain(name, requests, notifications, html, |
+ experimental: experimental); |
} |
-} |
-/** |
- * Create a [Domain] object from an HTML representation such as: |
- * |
- * <domain name="domainName"> |
- * <request method="...">...</request> <!-- zero or more --> |
- * <notification event="...">...</notification> <!-- zero or more --> |
- * </domain> |
- * |
- * Child elements can occur in any order. |
- */ |
-Domain domainFromHtml(dom.Element html) { |
- checkName(html, 'domain'); |
- String name = html.attributes['name']; |
- String context = name ?? 'domain'; |
- bool experimental = html.attributes['experimental'] == 'true'; |
- checkAttributes(html, ['name'], context, |
- optionalAttributes: ['experimental']); |
- List<Request> requests = <Request>[]; |
- List<Notification> notifications = <Notification>[]; |
- recurse(html, context, { |
- 'request': (dom.Element child) { |
- requests.add(requestFromHtml(child, context)); |
- }, |
- 'notification': (dom.Element child) { |
- notifications.add(notificationFromHtml(child, context)); |
+ dom.Element getAncestor(dom.Element html, String name, String context) { |
+ dom.Element ancestor = html.parent; |
+ while (ancestor != null) { |
+ if (ancestor.localName == name) { |
+ return ancestor; |
+ } |
+ ancestor = ancestor.parent; |
} |
- }); |
- return new Domain(name, requests, notifications, html, |
- experimental: experimental); |
-} |
+ throw new Exception( |
+ '$context: <${html.localName}> must be nested within <$name>'); |
+ } |
-dom.Element getAncestor(dom.Element html, String name, String context) { |
- dom.Element ancestor = html.parent; |
- while (ancestor != null) { |
- if (ancestor.localName == name) { |
- return ancestor; |
- } |
- ancestor = ancestor.parent; |
+ /** |
+ * Create a [Notification] object from an HTML representation such as: |
+ * |
+ * <notification event="methodName"> |
+ * <params>...</params> <!-- optional --> |
+ * </notification> |
+ * |
+ * Note that the event name should not include the domain name. |
+ * |
+ * <params> has the same form as <object>, as described in [typeDeclFromHtml]. |
+ * |
+ * Child elements can occur in any order. |
+ */ |
+ Notification notificationFromHtml(dom.Element html, String context) { |
+ String domainName = getAncestor(html, 'domain', context).attributes['name']; |
+ checkName(html, 'notification', context); |
+ String event = html.attributes['event']; |
+ context = '$context.${event != null ? event : 'event'}'; |
+ checkAttributes(html, ['event'], context); |
+ TypeDecl params; |
+ recurse(html, context, { |
+ 'params': (dom.Element child) { |
+ params = typeObjectFromHtml(child, '$context.params'); |
+ } |
+ }); |
+ return new Notification(domainName, event, params, html); |
} |
- throw new Exception( |
- '$context: <${html.localName}> must be nested within <$name>'); |
-} |
-/** |
- * Create a [Notification] object from an HTML representation such as: |
- * |
- * <notification event="methodName"> |
- * <params>...</params> <!-- optional --> |
- * </notification> |
- * |
- * Note that the event name should not include the domain name. |
- * |
- * <params> has the same form as <object>, as described in [typeDeclFromHtml]. |
- * |
- * Child elements can occur in any order. |
- */ |
-Notification notificationFromHtml(dom.Element html, String context) { |
- String domainName = getAncestor(html, 'domain', context).attributes['name']; |
- checkName(html, 'notification', context); |
- String event = html.attributes['event']; |
- context = '$context.${event != null ? event : 'event'}'; |
- checkAttributes(html, ['event'], context); |
- TypeDecl params; |
- recurse(html, context, { |
- 'params': (dom.Element child) { |
- params = typeObjectFromHtml(child, '$context.params'); |
+ /** |
+ * Create a single of [TypeDecl] corresponding to the type defined inside the |
+ * given HTML element. |
+ */ |
+ TypeDecl processContentsAsType(dom.Element html, String context) { |
+ List<TypeDecl> types = processContentsAsTypes(html, context); |
+ if (types.length != 1) { |
+ throw new Exception('$context: Exactly one type must be specified'); |
} |
- }); |
- return new Notification(domainName, event, params, html); |
-} |
- |
-/** |
- * Create a single of [TypeDecl] corresponding to the type defined inside the |
- * given HTML element. |
- */ |
-TypeDecl processContentsAsType(dom.Element html, String context) { |
- List<TypeDecl> types = processContentsAsTypes(html, context); |
- if (types.length != 1) { |
- throw new Exception('$context: Exactly one type must be specified'); |
+ return types[0]; |
} |
- return types[0]; |
-} |
-/** |
- * Create a list of [TypeDecl]s corresponding to the types defined inside the |
- * given HTML element. The following forms are supported. |
- * |
- * To refer to a type declared elsewhere (or a built-in type): |
- * |
- * <ref>typeName</ref> |
- * |
- * For a list: <list>ItemType</list> |
- * |
- * For a map: <map><key>KeyType</key><value>ValueType</value></map> |
- * |
- * For a JSON object: |
- * |
- * <object> |
- * <field name="...">...</field> <!-- zero or more --> |
- * </object> |
- * |
- * For an enum: |
- * |
- * <enum> |
- * <value>...</value> <!-- zero or more --> |
- * </enum> |
- * |
- * For a union type: |
- * <union> |
- * TYPE <!-- zero or more --> |
- * </union> |
- */ |
-List<TypeDecl> processContentsAsTypes(dom.Element html, String context) { |
- List<TypeDecl> types = <TypeDecl>[]; |
- recurse(html, context, { |
- 'object': (dom.Element child) { |
- types.add(typeObjectFromHtml(child, context)); |
- }, |
- 'list': (dom.Element child) { |
- checkAttributes(child, [], context); |
- types.add(new TypeList(processContentsAsType(child, context), child)); |
- }, |
- 'map': (dom.Element child) { |
- checkAttributes(child, [], context); |
- TypeDecl keyType; |
- TypeDecl valueType; |
- recurse(child, context, { |
- 'key': (dom.Element child) { |
- if (keyType != null) { |
- throw new Exception('$context: Key type already specified'); |
+ /** |
+ * Create a list of [TypeDecl]s corresponding to the types defined inside the |
+ * given HTML element. The following forms are supported. |
+ * |
+ * To refer to a type declared elsewhere (or a built-in type): |
+ * |
+ * <ref>typeName</ref> |
+ * |
+ * For a list: <list>ItemType</list> |
+ * |
+ * For a map: <map><key>KeyType</key><value>ValueType</value></map> |
+ * |
+ * For a JSON object: |
+ * |
+ * <object> |
+ * <field name="...">...</field> <!-- zero or more --> |
+ * </object> |
+ * |
+ * For an enum: |
+ * |
+ * <enum> |
+ * <value>...</value> <!-- zero or more --> |
+ * </enum> |
+ * |
+ * For a union type: |
+ * <union> |
+ * TYPE <!-- zero or more --> |
+ * </union> |
+ */ |
+ List<TypeDecl> processContentsAsTypes(dom.Element html, String context) { |
+ List<TypeDecl> types = <TypeDecl>[]; |
+ recurse(html, context, { |
+ 'object': (dom.Element child) { |
+ types.add(typeObjectFromHtml(child, context)); |
+ }, |
+ 'list': (dom.Element child) { |
+ checkAttributes(child, [], context); |
+ types.add(new TypeList(processContentsAsType(child, context), child)); |
+ }, |
+ 'map': (dom.Element child) { |
+ checkAttributes(child, [], context); |
+ TypeDecl keyType; |
+ TypeDecl valueType; |
+ recurse(child, context, { |
+ 'key': (dom.Element child) { |
+ if (keyType != null) { |
+ throw new Exception('$context: Key type already specified'); |
+ } |
+ keyType = processContentsAsType(child, '$context.key'); |
+ }, |
+ 'value': (dom.Element child) { |
+ if (valueType != null) { |
+ throw new Exception('$context: Value type already specified'); |
+ } |
+ valueType = processContentsAsType(child, '$context.value'); |
} |
- keyType = processContentsAsType(child, '$context.key'); |
- }, |
- 'value': (dom.Element child) { |
- if (valueType != null) { |
- throw new Exception('$context: Value type already specified'); |
- } |
- valueType = processContentsAsType(child, '$context.value'); |
+ }); |
+ if (keyType == null) { |
+ throw new Exception('$context: Key type not specified'); |
} |
- }); |
- if (keyType == null) { |
- throw new Exception('$context: Key type not specified'); |
- } |
- if (valueType == null) { |
- throw new Exception('$context: Value type not specified'); |
+ if (valueType == null) { |
+ throw new Exception('$context: Value type not specified'); |
+ } |
+ types.add(new TypeMap(keyType, valueType, child)); |
+ }, |
+ 'enum': (dom.Element child) { |
+ types.add(typeEnumFromHtml(child, context)); |
+ }, |
+ 'ref': (dom.Element child) { |
+ checkAttributes(child, [], context); |
+ types.add(new TypeReference(innerText(child), child)); |
+ }, |
+ 'union': (dom.Element child) { |
+ checkAttributes(child, ['field'], context); |
+ String field = child.attributes['field']; |
+ types.add(new TypeUnion( |
+ processContentsAsTypes(child, context), field, child)); |
} |
- types.add(new TypeMap(keyType, valueType, child)); |
- }, |
- 'enum': (dom.Element child) { |
- types.add(typeEnumFromHtml(child, context)); |
- }, |
- 'ref': (dom.Element child) { |
- checkAttributes(child, [], context); |
- types.add(new TypeReference(innerText(child), child)); |
- }, |
- 'union': (dom.Element child) { |
- checkAttributes(child, ['field'], context); |
- String field = child.attributes['field']; |
- types.add( |
- new TypeUnion(processContentsAsTypes(child, context), field, child)); |
- } |
- }); |
- return types; |
-} |
+ }); |
+ return types; |
+ } |
-/** |
- * Read the API description from the file 'spec_input.html'. [pkgPath] is the |
- * path to the current package. |
- */ |
-Api readApi(String pkgPath) { |
- File htmlFile = new File(join(pkgPath, 'tool', 'spec', 'spec_input.html')); |
- String htmlContents = htmlFile.readAsStringSync(); |
- dom.Document document = parser.parse(htmlContents); |
- dom.Element htmlElement = document.children |
- .singleWhere((element) => element.localName.toLowerCase() == 'html'); |
- return apiFromHtml(htmlElement); |
-} |
+ /** |
+ * Read the API description from file with the given [filePath]. |
+ */ |
+ Api readApi() { |
+ String htmlContents = new File(filePath).readAsStringSync(); |
+ dom.Document document = parser.parse(htmlContents); |
+ dom.Element htmlElement = document.children |
+ .singleWhere((element) => element.localName.toLowerCase() == 'html'); |
+ return apiFromHtml(htmlElement); |
+ } |
-void recurse(dom.Element parent, String context, |
- Map<String, ElementProcessor> elementProcessors) { |
- for (String key in elementProcessors.keys) { |
- if (!specialElements.contains(key)) { |
- throw new Exception('$context: $key is not a special element'); |
+ void recurse(dom.Element parent, String context, |
+ Map<String, ElementProcessor> elementProcessors) { |
+ for (String key in elementProcessors.keys) { |
+ if (!specialElements.contains(key)) { |
+ throw new Exception('$context: $key is not a special element'); |
+ } |
} |
- } |
- for (dom.Node node in parent.nodes) { |
- if (node is dom.Element) { |
- if (elementProcessors.containsKey(node.localName)) { |
- elementProcessors[node.localName](node); |
- } else if (specialElements.contains(node.localName)) { |
- throw new Exception('$context: Unexpected use of <${node.localName}>'); |
- } else { |
- recurse(node, context, elementProcessors); |
+ for (dom.Node node in parent.nodes) { |
+ if (node is dom.Element) { |
+ if (elementProcessors.containsKey(node.localName)) { |
+ elementProcessors[node.localName](node); |
+ } else if (specialElements.contains(node.localName)) { |
+ throw new Exception( |
+ '$context: Unexpected use of <${node.localName}>'); |
+ } else { |
+ recurse(node, context, elementProcessors); |
+ } |
} |
} |
} |
-} |
-/** |
- * Create a [Refactoring] object from an HTML representation such as: |
- * |
- * <refactoring kind="refactoringKind"> |
- * <feedback>...</feedback> <!-- optional --> |
- * <options>...</options> <!-- optional --> |
- * </refactoring> |
- * |
- * <feedback> and <options> have the same form as <object>, as described in |
- * [typeDeclFromHtml]. |
- * |
- * Child elements can occur in any order. |
- */ |
-Refactoring refactoringFromHtml(dom.Element html) { |
- checkName(html, 'refactoring'); |
- String kind = html.attributes['kind']; |
- String context = kind != null ? kind : 'refactoring'; |
- checkAttributes(html, ['kind'], context); |
- TypeDecl feedback; |
- TypeDecl options; |
- recurse(html, context, { |
- 'feedback': (dom.Element child) { |
- feedback = typeObjectFromHtml(child, '$context.feedback'); |
- }, |
- 'options': (dom.Element child) { |
- options = typeObjectFromHtml(child, '$context.options'); |
- } |
- }); |
- return new Refactoring(kind, feedback, options, html); |
-} |
+ /** |
+ * Create a [Refactoring] object from an HTML representation such as: |
+ * |
+ * <refactoring kind="refactoringKind"> |
+ * <feedback>...</feedback> <!-- optional --> |
+ * <options>...</options> <!-- optional --> |
+ * </refactoring> |
+ * |
+ * <feedback> and <options> have the same form as <object>, as described in |
+ * [typeDeclFromHtml]. |
+ * |
+ * Child elements can occur in any order. |
+ */ |
+ Refactoring refactoringFromHtml(dom.Element html) { |
+ checkName(html, 'refactoring'); |
+ String kind = html.attributes['kind']; |
+ String context = kind != null ? kind : 'refactoring'; |
+ checkAttributes(html, ['kind'], context); |
+ TypeDecl feedback; |
+ TypeDecl options; |
+ recurse(html, context, { |
+ 'feedback': (dom.Element child) { |
+ feedback = typeObjectFromHtml(child, '$context.feedback'); |
+ }, |
+ 'options': (dom.Element child) { |
+ options = typeObjectFromHtml(child, '$context.options'); |
+ } |
+ }); |
+ return new Refactoring(kind, feedback, options, html); |
+ } |
-/** |
- * Create a [Refactorings] object from an HTML representation such as: |
- * |
- * <refactorings> |
- * <refactoring kind="...">...</refactoring> <!-- zero or more --> |
- * </refactorings> |
- */ |
-Refactorings refactoringsFromHtml(dom.Element html) { |
- checkName(html, 'refactorings'); |
- String context = 'refactorings'; |
- checkAttributes(html, [], context); |
- List<Refactoring> refactorings = <Refactoring>[]; |
- recurse(html, context, { |
- 'refactoring': (dom.Element child) { |
- refactorings.add(refactoringFromHtml(child)); |
- } |
- }); |
- return new Refactorings(refactorings, html); |
-} |
+ /** |
+ * Create a [Refactorings] object from an HTML representation such as: |
+ * |
+ * <refactorings> |
+ * <refactoring kind="...">...</refactoring> <!-- zero or more --> |
+ * </refactorings> |
+ */ |
+ Refactorings refactoringsFromHtml(dom.Element html) { |
+ checkName(html, 'refactorings'); |
+ String context = 'refactorings'; |
+ checkAttributes(html, [], context); |
+ List<Refactoring> refactorings = <Refactoring>[]; |
+ recurse(html, context, { |
+ 'refactoring': (dom.Element child) { |
+ refactorings.add(refactoringFromHtml(child)); |
+ } |
+ }); |
+ return new Refactorings(refactorings, html); |
+ } |
-/** |
- * Create a [Request] object from an HTML representation such as: |
- * |
- * <request method="methodName"> |
- * <params>...</params> <!-- optional --> |
- * <result>...</result> <!-- optional --> |
- * </request> |
- * |
- * Note that the method name should not include the domain name. |
- * |
- * <params> and <result> have the same form as <object>, as described in |
- * [typeDeclFromHtml]. |
- * |
- * Child elements can occur in any order. |
- */ |
-Request requestFromHtml(dom.Element html, String context) { |
- String domainName = getAncestor(html, 'domain', context).attributes['name']; |
- checkName(html, 'request', context); |
- String method = html.attributes['method']; |
- context = '$context.${method != null ? method : 'method'}'; |
- checkAttributes(html, ['method'], context, |
- optionalAttributes: ['experimental', 'deprecated']); |
- bool experimental = html.attributes['experimental'] == 'true'; |
- bool deprecated = html.attributes['deprecated'] == 'true'; |
- TypeDecl params; |
- TypeDecl result; |
- recurse(html, context, { |
- 'params': (dom.Element child) { |
- params = typeObjectFromHtml(child, '$context.params'); |
- }, |
- 'result': (dom.Element child) { |
- result = typeObjectFromHtml(child, '$context.result'); |
- } |
- }); |
- return new Request(domainName, method, params, result, html, |
- experimental: experimental, deprecated: deprecated); |
-} |
+ /** |
+ * Create a [Request] object from an HTML representation such as: |
+ * |
+ * <request method="methodName"> |
+ * <params>...</params> <!-- optional --> |
+ * <result>...</result> <!-- optional --> |
+ * </request> |
+ * |
+ * Note that the method name should not include the domain name. |
+ * |
+ * <params> and <result> have the same form as <object>, as described in |
+ * [typeDeclFromHtml]. |
+ * |
+ * Child elements can occur in any order. |
+ */ |
+ Request requestFromHtml(dom.Element html, String context) { |
+ String domainName = getAncestor(html, 'domain', context).attributes['name']; |
+ checkName(html, 'request', context); |
+ String method = html.attributes['method']; |
+ context = '$context.${method != null ? method : 'method'}'; |
+ checkAttributes(html, ['method'], context, |
+ optionalAttributes: ['experimental', 'deprecated']); |
+ bool experimental = html.attributes['experimental'] == 'true'; |
+ bool deprecated = html.attributes['deprecated'] == 'true'; |
+ TypeDecl params; |
+ TypeDecl result; |
+ recurse(html, context, { |
+ 'params': (dom.Element child) { |
+ params = typeObjectFromHtml(child, '$context.params'); |
+ }, |
+ 'result': (dom.Element child) { |
+ result = typeObjectFromHtml(child, '$context.result'); |
+ } |
+ }); |
+ return new Request(domainName, method, params, result, html, |
+ experimental: experimental, deprecated: deprecated); |
+ } |
-/** |
- * Create a [TypeDefinition] object from an HTML representation such as: |
- * |
- * <type name="typeName"> |
- * TYPE |
- * </type> |
- * |
- * Where TYPE is any HTML that can be parsed by [typeDeclFromHtml]. |
- * |
- * Child elements can occur in any order. |
- */ |
-TypeDefinition typeDefinitionFromHtml(dom.Element html) { |
- checkName(html, 'type'); |
- String name = html.attributes['name']; |
- String context = name != null ? name : 'type'; |
- checkAttributes(html, ['name'], context, |
- optionalAttributes: ['experimental', 'deprecated']); |
- TypeDecl type = processContentsAsType(html, context); |
- bool experimental = html.attributes['experimental'] == 'true'; |
- bool deprecated = html.attributes['deprecated'] == 'true'; |
- return new TypeDefinition(name, type, html, |
- experimental: experimental, deprecated: deprecated); |
-} |
+ /** |
+ * Create a [TypeDefinition] object from an HTML representation such as: |
+ * |
+ * <type name="typeName"> |
+ * TYPE |
+ * </type> |
+ * |
+ * Where TYPE is any HTML that can be parsed by [typeDeclFromHtml]. |
+ * |
+ * Child elements can occur in any order. |
+ */ |
+ TypeDefinition typeDefinitionFromHtml(dom.Element html) { |
+ checkName(html, 'type'); |
+ String name = html.attributes['name']; |
+ String context = name != null ? name : 'type'; |
+ checkAttributes(html, ['name'], context, |
+ optionalAttributes: ['experimental', 'deprecated']); |
+ TypeDecl type = processContentsAsType(html, context); |
+ bool experimental = html.attributes['experimental'] == 'true'; |
+ bool deprecated = html.attributes['deprecated'] == 'true'; |
+ return new TypeDefinition(name, type, html, |
+ experimental: experimental, deprecated: deprecated); |
+ } |
-/** |
- * Create a [TypeEnum] from an HTML description. |
- */ |
-TypeEnum typeEnumFromHtml(dom.Element html, String context) { |
- checkName(html, 'enum', context); |
- checkAttributes(html, [], context); |
- List<TypeEnumValue> values = <TypeEnumValue>[]; |
- recurse(html, context, { |
- 'value': (dom.Element child) { |
- values.add(typeEnumValueFromHtml(child, context)); |
- } |
- }); |
- return new TypeEnum(values, html); |
-} |
+ /** |
+ * Create a [TypeEnum] from an HTML description. |
+ */ |
+ TypeEnum typeEnumFromHtml(dom.Element html, String context) { |
+ checkName(html, 'enum', context); |
+ checkAttributes(html, [], context); |
+ List<TypeEnumValue> values = <TypeEnumValue>[]; |
+ recurse(html, context, { |
+ 'value': (dom.Element child) { |
+ values.add(typeEnumValueFromHtml(child, context)); |
+ } |
+ }); |
+ return new TypeEnum(values, html); |
+ } |
-/** |
- * Create a [TypeEnumValue] from an HTML description such as: |
- * |
- * <enum> |
- * <code>VALUE</code> |
- * </enum> |
- * |
- * Where VALUE is the text of the enumerated value. |
- * |
- * Child elements can occur in any order. |
- */ |
-TypeEnumValue typeEnumValueFromHtml(dom.Element html, String context) { |
- checkName(html, 'value', context); |
- checkAttributes(html, [], context, optionalAttributes: ['deprecated']); |
- bool deprecated = html.attributes['deprecated'] == 'true'; |
- List<String> values = <String>[]; |
- recurse(html, context, { |
- 'code': (dom.Element child) { |
- String text = innerText(child).trim(); |
- values.add(text); |
+ /** |
+ * Create a [TypeEnumValue] from an HTML description such as: |
+ * |
+ * <enum> |
+ * <code>VALUE</code> |
+ * </enum> |
+ * |
+ * Where VALUE is the text of the enumerated value. |
+ * |
+ * Child elements can occur in any order. |
+ */ |
+ TypeEnumValue typeEnumValueFromHtml(dom.Element html, String context) { |
+ checkName(html, 'value', context); |
+ checkAttributes(html, [], context, optionalAttributes: ['deprecated']); |
+ bool deprecated = html.attributes['deprecated'] == 'true'; |
+ List<String> values = <String>[]; |
+ recurse(html, context, { |
+ 'code': (dom.Element child) { |
+ String text = innerText(child).trim(); |
+ values.add(text); |
+ } |
+ }); |
+ if (values.length != 1) { |
+ throw new Exception('$context: Exactly one value must be specified'); |
} |
- }); |
- if (values.length != 1) { |
- throw new Exception('$context: Exactly one value must be specified'); |
+ return new TypeEnumValue(values[0], html, deprecated: deprecated); |
} |
- return new TypeEnumValue(values[0], html, deprecated: deprecated); |
-} |
-/** |
- * Create a [TypeObjectField] from an HTML description such as: |
- * |
- * <field name="fieldName"> |
- * TYPE |
- * </field> |
- * |
- * Where TYPE is any HTML that can be parsed by [typeDeclFromHtml]. |
- * |
- * In addition, the attribute optional="true" may be used to specify that the |
- * field is optional, and the attribute value="..." may be used to specify that |
- * the field is required to have a certain value. |
- * |
- * Child elements can occur in any order. |
- */ |
-TypeObjectField typeObjectFieldFromHtml(dom.Element html, String context) { |
- checkName(html, 'field', context); |
- String name = html.attributes['name']; |
- context = '$context.${name != null ? name : 'field'}'; |
- checkAttributes(html, ['name'], context, |
- optionalAttributes: ['optional', 'value', 'deprecated']); |
- bool deprecated = html.attributes['deprecated'] == 'true'; |
- bool optional = false; |
- String optionalString = html.attributes['optional']; |
- if (optionalString != null) { |
- switch (optionalString) { |
- case 'true': |
- optional = true; |
- break; |
- case 'false': |
- optional = false; |
- break; |
- default: |
- throw new Exception( |
- '$context: field contains invalid "optional" attribute: "$optionalString"'); |
+ /** |
+ * Create a [TypeObjectField] from an HTML description such as: |
+ * |
+ * <field name="fieldName"> |
+ * TYPE |
+ * </field> |
+ * |
+ * Where TYPE is any HTML that can be parsed by [typeDeclFromHtml]. |
+ * |
+ * In addition, the attribute optional="true" may be used to specify that the |
+ * field is optional, and the attribute value="..." may be used to specify that |
+ * the field is required to have a certain value. |
+ * |
+ * Child elements can occur in any order. |
+ */ |
+ TypeObjectField typeObjectFieldFromHtml(dom.Element html, String context) { |
+ checkName(html, 'field', context); |
+ String name = html.attributes['name']; |
+ context = '$context.${name != null ? name : 'field'}'; |
+ checkAttributes(html, ['name'], context, |
+ optionalAttributes: ['optional', 'value', 'deprecated']); |
+ bool deprecated = html.attributes['deprecated'] == 'true'; |
+ bool optional = false; |
+ String optionalString = html.attributes['optional']; |
+ if (optionalString != null) { |
+ switch (optionalString) { |
+ case 'true': |
+ optional = true; |
+ break; |
+ case 'false': |
+ optional = false; |
+ break; |
+ default: |
+ throw new Exception( |
+ '$context: field contains invalid "optional" attribute: "$optionalString"'); |
+ } |
} |
+ String value = html.attributes['value']; |
+ TypeDecl type = processContentsAsType(html, context); |
+ return new TypeObjectField(name, type, html, |
+ optional: optional, value: value, deprecated: deprecated); |
} |
- String value = html.attributes['value']; |
- TypeDecl type = processContentsAsType(html, context); |
- return new TypeObjectField(name, type, html, |
- optional: optional, value: value, deprecated: deprecated); |
-} |
-/** |
- * Create a [TypeObject] from an HTML description. |
- */ |
-TypeObject typeObjectFromHtml(dom.Element html, String context) { |
- checkAttributes(html, [], context, optionalAttributes: ['experimental']); |
- List<TypeObjectField> fields = <TypeObjectField>[]; |
- recurse(html, context, { |
- 'field': (dom.Element child) { |
- fields.add(typeObjectFieldFromHtml(child, context)); |
- } |
- }); |
- bool experimental = html.attributes['experimental'] == 'true'; |
- return new TypeObject(fields, html, experimental: experimental); |
-} |
+ /** |
+ * Create a [TypeObject] from an HTML description. |
+ */ |
+ TypeObject typeObjectFromHtml(dom.Element html, String context) { |
+ checkAttributes(html, [], context, optionalAttributes: ['experimental']); |
+ List<TypeObjectField> fields = <TypeObjectField>[]; |
+ recurse(html, context, { |
+ 'field': (dom.Element child) { |
+ fields.add(typeObjectFieldFromHtml(child, context)); |
+ } |
+ }); |
+ bool experimental = html.attributes['experimental'] == 'true'; |
+ return new TypeObject(fields, html, experimental: experimental); |
+ } |
-/** |
- * Create a [Types] object from an HTML representation such as: |
- * |
- * <types> |
- * <type name="...">...</type> <!-- zero or more --> |
- * </types> |
- */ |
-Types typesFromHtml(dom.Element html) { |
- checkName(html, 'types'); |
- String context = 'types'; |
- checkAttributes(html, [], context); |
- Map<String, TypeDefinition> types = <String, TypeDefinition>{}; |
- recurse(html, context, { |
- 'type': (dom.Element child) { |
- TypeDefinition typeDefinition = typeDefinitionFromHtml(child); |
- types[typeDefinition.name] = typeDefinition; |
+ /** |
+ * Create a [Types] object from an HTML representation such as: |
+ * |
+ * <types> |
+ * <type name="...">...</type> <!-- zero or more --> |
+ * </types> |
+ */ |
+ Types typesFromHtml(dom.Element html) { |
+ checkName(html, 'types'); |
+ String context = 'types'; |
+ checkAttributes(html, [], context); |
+ List<String> importUris = <String>[]; |
+ Map<String, TypeDefinition> typeMap = <String, TypeDefinition>{}; |
+ List<dom.Element> childElements = <dom.Element>[]; |
+ recurse(html, context, { |
+ 'include': (dom.Element child) { |
+ String importUri = child.attributes['import']; |
+ if (importUri != null) { |
+ importUris.add(importUri); |
+ } |
+ String relativePath = child.attributes['path']; |
+ String path = normalize(join(dirname(filePath), relativePath)); |
+ ApiReader reader = new ApiReader(path); |
+ Api api = reader.readApi(); |
+ for (TypeDefinition typeDefinition in api.types) { |
+ typeDefinition.isExternal = true; |
+ childElements.add(typeDefinition.html); |
+ typeMap[typeDefinition.name] = typeDefinition; |
+ } |
+ }, |
+ 'type': (dom.Element child) { |
+ TypeDefinition typeDefinition = typeDefinitionFromHtml(child); |
+ typeMap[typeDefinition.name] = typeDefinition; |
+ } |
+ }); |
+ for (dom.Element element in childElements) { |
+ html.append(element); |
} |
- }); |
- return new Types(types, html); |
+ Types types = new Types(typeMap, html); |
+ types.importUris.addAll(importUris); |
+ return types; |
+ } |
} |
- |
-typedef void ElementProcessor(dom.Element element); |
- |
-typedef void TextProcessor(dom.Text text); |