| OLD | NEW |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 /** | 5 /** |
| 6 * Code for reading an HTML API description. | 6 * Code for reading an HTML API description. |
| 7 */ | 7 */ |
| 8 library from.html; | 8 library from.html; |
| 9 | 9 |
| 10 import 'dart:io'; | 10 import 'dart:io'; |
| 11 | 11 |
| 12 import 'package:html5lib/dom.dart' as dom; | 12 import 'package:html5lib/dom.dart' as dom; |
| 13 import 'package:html5lib/parser.dart' as parser; | 13 import 'package:html5lib/parser.dart' as parser; |
| 14 | 14 |
| 15 import 'api.dart'; | 15 import 'api.dart'; |
| 16 import 'html_tools.dart'; | 16 import 'html_tools.dart'; |
| 17 | 17 |
| 18 const List<String> specialElements = const [ | 18 const List<String> specialElements = const [ |
| 19 'domain', | 19 'domain', |
| 20 'feedback', | 20 'feedback', |
| 21 'object', | 21 'object', |
| 22 'refactorings', | 22 'refactorings', |
| 23 'refactoring', | 23 'refactoring', |
| 24 'type', | 24 'type', |
| 25 'types', | 25 'types', |
| 26 'request', | 26 'request', |
| 27 'notification', | 27 'notification', |
| 28 'params', | 28 'params', |
| 29 'result', | 29 'result', |
| 30 'field', | 30 'field', |
| 31 'list', | 31 'list', |
| 32 'map', | 32 'map', |
| 33 'enum', | 33 'enum', |
| 34 'key', | 34 'key', |
| 35 'value', | 35 'value', |
| 36 'options', | 36 'options', |
| 37 'ref', | 37 'ref', |
| 38 'code', | 38 'code', |
| 39 'version', | 39 'version', |
| 40 'union']; | 40 'union' |
| 41 ]; |
| 41 | 42 |
| 42 /** | 43 /** |
| 43 * Create an [Api] object from an HTML representation such as: | 44 * Create an [Api] object from an HTML representation such as: |
| 44 * | 45 * |
| 45 * <html> | 46 * <html> |
| 46 * ... | 47 * ... |
| 47 * <body> | 48 * <body> |
| 48 * ... <version>1.0</version> ... | 49 * ... <version>1.0</version> ... |
| 49 * <domain name="...">...</domain> <!-- zero or more --> | 50 * <domain name="...">...</domain> <!-- zero or more --> |
| 50 * <types>...</types> | 51 * <types>...</types> |
| (...skipping 28 matching lines...) Expand all Loading... |
| 79 } | 80 } |
| 80 api = new Api(versions[0], domains, types, refactorings, html); | 81 api = new Api(versions[0], domains, types, refactorings, html); |
| 81 return api; | 82 return api; |
| 82 } | 83 } |
| 83 | 84 |
| 84 /** | 85 /** |
| 85 * Check that the given [element] has all of the attributes in | 86 * Check that the given [element] has all of the attributes in |
| 86 * [requiredAttributes], possibly some of the attributes in | 87 * [requiredAttributes], possibly some of the attributes in |
| 87 * [optionalAttributes], and no others. | 88 * [optionalAttributes], and no others. |
| 88 */ | 89 */ |
| 89 void checkAttributes(dom.Element element, List<String> requiredAttributes, | 90 void checkAttributes( |
| 90 String context, {List<String> optionalAttributes: const [ | 91 dom.Element element, List<String> requiredAttributes, String context, |
| 91 ]}) { | 92 {List<String> optionalAttributes: const []}) { |
| 92 Set<String> attributesFound = new Set<String>(); | 93 Set<String> attributesFound = new Set<String>(); |
| 93 element.attributes.forEach((String name, String value) { | 94 element.attributes.forEach((String name, String value) { |
| 94 if (!requiredAttributes.contains(name) && | 95 if (!requiredAttributes.contains(name) && |
| 95 !optionalAttributes.contains(name)) { | 96 !optionalAttributes.contains(name)) { |
| 96 throw new Exception( | 97 throw new Exception( |
| 97 '$context: Unexpected attribute in ${element.localName}: $name'); | 98 '$context: Unexpected attribute in ${element.localName}: $name'); |
| 98 } | 99 } |
| 99 attributesFound.add(name); | 100 attributesFound.add(name); |
| 100 }); | 101 }); |
| 101 for (String expectedAttribute in requiredAttributes) { | 102 for (String expectedAttribute in requiredAttributes) { |
| (...skipping 181 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 283 /** | 284 /** |
| 284 * Read the API description from the file 'spec_input.html'. | 285 * Read the API description from the file 'spec_input.html'. |
| 285 */ | 286 */ |
| 286 Api readApi() { | 287 Api readApi() { |
| 287 File htmlFile = new File('spec_input.html'); | 288 File htmlFile = new File('spec_input.html'); |
| 288 String htmlContents = htmlFile.readAsStringSync(); | 289 String htmlContents = htmlFile.readAsStringSync(); |
| 289 dom.Document document = parser.parse(htmlContents); | 290 dom.Document document = parser.parse(htmlContents); |
| 290 return apiFromHtml(document.firstChild); | 291 return apiFromHtml(document.firstChild); |
| 291 } | 292 } |
| 292 | 293 |
| 293 void recurse(dom.Element parent, String context, Map<String, | 294 void recurse(dom.Element parent, String context, |
| 294 ElementProcessor> elementProcessors) { | 295 Map<String, ElementProcessor> elementProcessors) { |
| 295 for (String key in elementProcessors.keys) { | 296 for (String key in elementProcessors.keys) { |
| 296 if (!specialElements.contains(key)) { | 297 if (!specialElements.contains(key)) { |
| 297 throw new Exception('$context: $key is not a special element'); | 298 throw new Exception('$context: $key is not a special element'); |
| 298 } | 299 } |
| 299 } | 300 } |
| 300 for (dom.Node node in parent.nodes) { | 301 for (dom.Node node in parent.nodes) { |
| 301 if (node is dom.Element) { | 302 if (node is dom.Element) { |
| 302 if (elementProcessors.containsKey(node.localName)) { | 303 if (elementProcessors.containsKey(node.localName)) { |
| 303 elementProcessors[node.localName](node); | 304 elementProcessors[node.localName](node); |
| 304 } else if (specialElements.contains(node.localName)) { | 305 } else if (specialElements.contains(node.localName)) { |
| (...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 469 * field is optional, and the attribute value="..." may be used to specify that | 470 * field is optional, and the attribute value="..." may be used to specify that |
| 470 * the field is required to have a certain value. | 471 * the field is required to have a certain value. |
| 471 * | 472 * |
| 472 * Child elements can occur in any order. | 473 * Child elements can occur in any order. |
| 473 */ | 474 */ |
| 474 TypeObjectField typeObjectFieldFromHtml(dom.Element html, String context) { | 475 TypeObjectField typeObjectFieldFromHtml(dom.Element html, String context) { |
| 475 checkName(html, 'field', context); | 476 checkName(html, 'field', context); |
| 476 String name = html.attributes['name']; | 477 String name = html.attributes['name']; |
| 477 context = '$context.${name != null ? name : 'field'}'; | 478 context = '$context.${name != null ? name : 'field'}'; |
| 478 checkAttributes( | 479 checkAttributes( |
| 479 html, | 480 html, ['name'], context, optionalAttributes: ['optional', 'value']); |
| 480 ['name'], | |
| 481 context, | |
| 482 optionalAttributes: ['optional', 'value']); | |
| 483 bool optional = false; | 481 bool optional = false; |
| 484 String optionalString = html.attributes['optional']; | 482 String optionalString = html.attributes['optional']; |
| 485 if (optionalString != null) { | 483 if (optionalString != null) { |
| 486 switch (optionalString) { | 484 switch (optionalString) { |
| 487 case 'true': | 485 case 'true': |
| 488 optional = true; | 486 optional = true; |
| 489 break; | 487 break; |
| 490 case 'false': | 488 case 'false': |
| 491 optional = false; | 489 optional = false; |
| 492 break; | 490 break; |
| 493 default: | 491 default: |
| 494 throw new Exception( | 492 throw new Exception( |
| 495 '$context: field contains invalid "optional" attribute: "$optionalSt
ring"'); | 493 '$context: field contains invalid "optional" attribute: "$optionalSt
ring"'); |
| 496 } | 494 } |
| 497 } | 495 } |
| 498 String value = html.attributes['value']; | 496 String value = html.attributes['value']; |
| 499 TypeDecl type = processContentsAsType(html, context); | 497 TypeDecl type = processContentsAsType(html, context); |
| 500 return new TypeObjectField( | 498 return new TypeObjectField(name, type, html, |
| 501 name, | 499 optional: optional, value: value); |
| 502 type, | |
| 503 html, | |
| 504 optional: optional, | |
| 505 value: value); | |
| 506 } | 500 } |
| 507 | 501 |
| 508 /** | 502 /** |
| 509 * Create a [TypeObject] from an HTML description. | 503 * Create a [TypeObject] from an HTML description. |
| 510 */ | 504 */ |
| 511 TypeObject typeObjectFromHtml(dom.Element html, String context) { | 505 TypeObject typeObjectFromHtml(dom.Element html, String context) { |
| 512 checkAttributes(html, [], context); | 506 checkAttributes(html, [], context); |
| 513 List<TypeObjectField> fields = <TypeObjectField>[]; | 507 List<TypeObjectField> fields = <TypeObjectField>[]; |
| 514 recurse(html, context, { | 508 recurse(html, context, { |
| 515 'field': (dom.Element child) { | 509 'field': (dom.Element child) { |
| (...skipping 20 matching lines...) Expand all Loading... |
| 536 TypeDefinition typeDefinition = typeDefinitionFromHtml(child); | 530 TypeDefinition typeDefinition = typeDefinitionFromHtml(child); |
| 537 types[typeDefinition.name] = typeDefinition; | 531 types[typeDefinition.name] = typeDefinition; |
| 538 } | 532 } |
| 539 }); | 533 }); |
| 540 return new Types(types, html); | 534 return new Types(types, html); |
| 541 } | 535 } |
| 542 | 536 |
| 543 typedef void ElementProcessor(dom.Element element); | 537 typedef void ElementProcessor(dom.Element element); |
| 544 | 538 |
| 545 typedef void TextProcessor(dom.Text text); | 539 typedef void TextProcessor(dom.Text text); |
| OLD | NEW |