| 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 ed3c287a9fe6d6435d9e722e4967171d05dc616b..5b27e176cb2c29d02d81ef330778a3a9b7c98d24 100644
|
| --- a/pkg/analysis_server/tool/spec/from_html.dart
|
| +++ b/pkg/analysis_server/tool/spec/from_html.dart
|
| @@ -15,83 +15,29 @@ import 'package:html5lib/parser.dart' as parser;
|
| import 'api.dart';
|
| import 'html_tools.dart';
|
|
|
| -/**
|
| - * 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}');
|
| - }
|
| -}
|
| -
|
| -/**
|
| - * 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}');
|
| - }
|
| - }
|
| -}
|
| -
|
| -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'];
|
| -
|
| -typedef void ElementProcessor(dom.Element element);
|
| -typedef void TextProcessor(dom.Text text);
|
| -
|
| -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);
|
| - }
|
| - }
|
| - }
|
| -}
|
| -
|
| -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;
|
| - }
|
| - throw new Exception(
|
| - '$context: <${html.localName}> must be nested within <$name>');
|
| -}
|
| +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'];
|
|
|
| /**
|
| * Create an [Api] object from an HTML representation such as:
|
| @@ -136,97 +82,42 @@ Api apiFromHtml(dom.Element html) {
|
| }
|
|
|
| /**
|
| - * Create a [Refactorings] object from an HTML representation such as:
|
| - *
|
| - * <refactorings>
|
| - * <refactoring kind="...">...</refactoring> <!-- zero or more -->
|
| - * </refactorings>
|
| + * Check that the given [element] has all of the attributes in
|
| + * [requiredAttributes], possibly some of the attributes in
|
| + * [optionalAttributes], and no others.
|
| */
|
| -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));
|
| +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);
|
| });
|
| - return new Refactorings(refactorings, 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');
|
| + for (String expectedAttribute in requiredAttributes) {
|
| + if (!attributesFound.contains(expectedAttribute)) {
|
| + throw new Exception(
|
| + '$context: ${element.localName} must contain attribute ${expectedAttribute}');
|
| }
|
| - });
|
| - return new Refactoring(kind, feedback, options, html);
|
| + }
|
| }
|
|
|
| /**
|
| - * Create a [Types] object from an HTML representation such as:
|
| - *
|
| - * <types>
|
| - * <type name="...">...</type> <!-- zero or more -->
|
| - * </types>
|
| + * Check that the given [element] has the given [expectedName].
|
| */
|
| -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;
|
| +void checkName(dom.Element element, String expectedName, [String context]) {
|
| + if (element.localName != expectedName) {
|
| + if (context == null) {
|
| + context = element.localName;
|
| }
|
| - });
|
| - return new Types(types, html);
|
| -}
|
| -
|
| -/**
|
| - * 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);
|
| - TypeDecl type = processContentsAsType(html, context);
|
| - return new TypeDefinition(name, type, html);
|
| + throw new Exception(
|
| + '$context: Expected $expectedName, found ${element.localName}');
|
| + }
|
| }
|
| -
|
| /**
|
| * Create a [Domain] object from an HTML representation such as:
|
| *
|
| @@ -255,38 +146,16 @@ Domain domainFromHtml(dom.Element html) {
|
| return new Domain(name, requests, notifications, 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);
|
| - 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');
|
| +dom.Element getAncestor(dom.Element html, String name, String context) {
|
| + dom.Element ancestor = html.parent;
|
| + while (ancestor != null) {
|
| + if (ancestor.localName == name) {
|
| + return ancestor;
|
| }
|
| - });
|
| - return new Request(domainName, method, params, result, html);
|
| + ancestor = ancestor.parent;
|
| + }
|
| + throw new Exception(
|
| + '$context: <${html.localName}> must be nested within <$name>');
|
| }
|
|
|
| /**
|
| @@ -316,6 +185,7 @@ Notification notificationFromHtml(dom.Element html, String context) {
|
| });
|
| return new Notification(domainName, event, params, html);
|
| }
|
| +
|
| /**
|
| * Create a single of [TypeDecl] corresponding to the type defined inside the
|
| * given HTML element.
|
| @@ -403,14 +273,148 @@ List<TypeDecl> processContentsAsTypes(dom.Element html, String context) {
|
| '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 TypeUnion(processContentsAsTypes(child, context), field, child));
|
| }
|
| });
|
| return types;
|
| }
|
|
|
| /**
|
| + * Read the API description from the file 'spec_input.html'.
|
| + */
|
| +Api readApi() {
|
| + File htmlFile = new File('spec_input.html');
|
| + String htmlContents = htmlFile.readAsStringSync();
|
| + dom.Document document = parser.parse(htmlContents);
|
| + return apiFromHtml(document.firstChild);
|
| +}
|
| +
|
| +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);
|
| + }
|
| + }
|
| + }
|
| +}
|
| +
|
| +/**
|
| + * 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 [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);
|
| + 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);
|
| +}
|
| +
|
| +/**
|
| + * 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);
|
| + TypeDecl type = processContentsAsType(html, context);
|
| + return new TypeDefinition(name, type, html);
|
| +}
|
| +/**
|
| * Create a [TypeEnum] from an HTML description.
|
| */
|
| TypeEnum typeEnumFromHtml(dom.Element html, String context) {
|
| @@ -453,20 +457,6 @@ TypeEnumValue typeEnumValueFromHtml(dom.Element html, String context) {
|
| }
|
|
|
| /**
|
| - * Create a [TypeObject] from an HTML description.
|
| - */
|
| -TypeObject typeObjectFromHtml(dom.Element html, String context) {
|
| - checkAttributes(html, [], context);
|
| - List<TypeObjectField> fields = <TypeObjectField>[];
|
| - recurse(html, context, {
|
| - 'field': (dom.Element child) {
|
| - fields.add(typeObjectFieldFromHtml(child, context));
|
| - }
|
| - });
|
| - return new TypeObject(fields, html);
|
| -}
|
| -
|
| -/**
|
| * Create a [TypeObjectField] from an HTML description such as:
|
| *
|
| * <field name="fieldName">
|
| @@ -485,8 +475,11 @@ 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']);
|
| + checkAttributes(
|
| + html,
|
| + ['name'],
|
| + context,
|
| + optionalAttributes: ['optional', 'value']);
|
| bool optional = false;
|
| String optionalString = html.attributes['optional'];
|
| if (optionalString != null) {
|
| @@ -504,16 +497,49 @@ TypeObjectField typeObjectFieldFromHtml(dom.Element html, String context) {
|
| }
|
| String value = html.attributes['value'];
|
| TypeDecl type = processContentsAsType(html, context);
|
| - return new TypeObjectField(name, type, html, optional: optional, value: value
|
| - );
|
| + return new TypeObjectField(
|
| + name,
|
| + type,
|
| + html,
|
| + optional: optional,
|
| + value: value);
|
| }
|
|
|
| /**
|
| - * Read the API description from the file 'spec_input.html'.
|
| + * Create a [TypeObject] from an HTML description.
|
| */
|
| -Api readApi() {
|
| - File htmlFile = new File('spec_input.html');
|
| - String htmlContents = htmlFile.readAsStringSync();
|
| - dom.Document document = parser.parse(htmlContents);
|
| - return apiFromHtml(document.firstChild);
|
| +TypeObject typeObjectFromHtml(dom.Element html, String context) {
|
| + checkAttributes(html, [], context);
|
| + List<TypeObjectField> fields = <TypeObjectField>[];
|
| + recurse(html, context, {
|
| + 'field': (dom.Element child) {
|
| + fields.add(typeObjectFieldFromHtml(child, context));
|
| + }
|
| + });
|
| + return new TypeObject(fields, html);
|
| }
|
| +
|
| +/**
|
| + * 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;
|
| + }
|
| + });
|
| + return new Types(types, html);
|
| +}
|
| +
|
| +typedef void ElementProcessor(dom.Element element);
|
| +
|
| +typedef void TextProcessor(dom.Text text);
|
|
|