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); |