| Index: pkg/analyzer_plugin/tool/spec/from_html.dart
|
| diff --git a/pkg/analyzer_plugin/tool/spec/from_html.dart b/pkg/analyzer_plugin/tool/spec/from_html.dart
|
| index 4ac843dcdc4728bc960854ace075d1e586c5185a..d79242755e3d36f740cd03122f8d7c93232deffe 100644
|
| --- a/pkg/analyzer_plugin/tool/spec/from_html.dart
|
| +++ b/pkg/analyzer_plugin/tool/spec/from_html.dart
|
| @@ -14,548 +14,585 @@ 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', 'plugin_spec.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', 'plugin_spec.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);
|
| + Map<String, TypeDefinition> types = <String, TypeDefinition>{};
|
| + List<dom.Element> childElements = <dom.Element>[];
|
| + recurse(html, context, {
|
| + 'include': (dom.Element child) {
|
| + 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) {
|
| + childElements.add(typeDefinition.html);
|
| + types[typeDefinition.name] = typeDefinition;
|
| + }
|
| + },
|
| + 'type': (dom.Element child) {
|
| + TypeDefinition typeDefinition = typeDefinitionFromHtml(child);
|
| + types[typeDefinition.name] = typeDefinition;
|
| + }
|
| + });
|
| + for (dom.Element element in childElements) {
|
| + html.append(element);
|
| }
|
| - });
|
| - return new Types(types, html);
|
| + return new Types(types, html);
|
| + }
|
| }
|
| -
|
| -typedef void ElementProcessor(dom.Element element);
|
| -
|
| -typedef void TextProcessor(dom.Text text);
|
|
|