Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1271)

Side by Side Diff: pkg/analyzer/lib/src/generated/html.dart

Issue 259773005: New analyzer snapshot. Sorted unit members. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « pkg/analyzer/lib/src/generated/error.dart ('k') | pkg/analyzer/lib/src/generated/index.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 // This code was auto-generated, is not intended to be edited, and is subject to 5 // This code was auto-generated, is not intended to be edited, and is subject to
6 // significant change. Please see the README file for more information. 6 // significant change. Please see the README file for more information.
7 7
8 library engine.html; 8 library engine.html;
9 9
10 import 'dart:collection'; 10 import 'dart:collection';
11 import 'java_core.dart'; 11 import 'java_core.dart';
12 import 'java_engine.dart'; 12 import 'java_engine.dart';
13 import 'source.dart'; 13 import 'source.dart';
14 import 'error.dart' show AnalysisErrorListener; 14 import 'error.dart' show AnalysisErrorListener;
15 import 'scanner.dart' as sc show Scanner, SubSequenceReader, Token; 15 import 'scanner.dart' as sc show Scanner, SubSequenceReader, Token;
16 import 'parser.dart' show Parser; 16 import 'parser.dart' show Parser;
17 import 'ast.dart'; 17 import 'ast.dart';
18 import 'element.dart'; 18 import 'element.dart';
19 import 'engine.dart' show AnalysisEngine, AngularHtmlUnitResolver, ExpressionVis itor; 19 import 'engine.dart' show AnalysisEngine, AngularHtmlUnitResolver, ExpressionVis itor;
20 20
21 /** 21 /**
22 * Instances of the class `XmlExpression` represent an abstract expression embed ded into
23 * [XmlNode].
24 */
25 abstract class XmlExpression {
26 /**
27 * An empty array of expressions.
28 */
29 static List<XmlExpression> EMPTY_ARRAY = new List<XmlExpression>(0);
30
31 /**
32 * Check if the given offset belongs to the expression's source range.
33 */
34 bool contains(int offset) => this.offset <= offset && offset < end;
35
36 /**
37 * Return the offset of the character immediately following the last character of this
38 * expression's source range. This is equivalent to `getOffset() + getLength() `.
39 *
40 * @return the offset of the character just past the expression's source range
41 */
42 int get end;
43
44 /**
45 * Return the number of characters in the expression's source range.
46 */
47 int get length;
48
49 /**
50 * Return the offset of the first character in the expression's source range.
51 */
52 int get offset;
53
54 /**
55 * Return the [Reference] at the given offset.
56 *
57 * @param offset the offset from the beginning of the file
58 * @return the [Reference] at the given offset, maybe `null`
59 */
60 XmlExpression_Reference getReference(int offset);
61 }
62
63 /**
64 * The reference to the [Element].
65 */
66 class XmlExpression_Reference {
67 Element element;
68
69 int offset = 0;
70
71 int length = 0;
72
73 XmlExpression_Reference(Element element, int offset, int length) {
74 this.element = element;
75 this.offset = offset;
76 this.length = length;
77 }
78 }
79
80 /**
81 * Instances of the class `SimpleXmlVisitor` implement an AST visitor that will do nothing
82 * when visiting an AST node. It is intended to be a superclass for classes that use the visitor
83 * pattern primarily as a dispatch mechanism (and hence don't need to recursivel y visit a whole
84 * structure) and that only need to visit a small number of node types.
85 */
86 class SimpleXmlVisitor<R> implements XmlVisitor<R> {
87 @override
88 R visitHtmlScriptTagNode(HtmlScriptTagNode node) => null;
89
90 @override
91 R visitHtmlUnit(HtmlUnit htmlUnit) => null;
92
93 @override
94 R visitXmlAttributeNode(XmlAttributeNode xmlAttributeNode) => null;
95
96 @override
97 R visitXmlTagNode(XmlTagNode xmlTagNode) => null;
98 }
99
100 /**
101 * The abstract class `AbstractScanner` implements a scanner for HTML code. Subc lasses are 22 * The abstract class `AbstractScanner` implements a scanner for HTML code. Subc lasses are
102 * required to implement the interface used to access the characters being scann ed. 23 * required to implement the interface used to access the characters being scann ed.
103 */ 24 */
104 abstract class AbstractScanner { 25 abstract class AbstractScanner {
105 static List<String> _NO_PASS_THROUGH_ELEMENTS = <String> []; 26 static List<String> _NO_PASS_THROUGH_ELEMENTS = <String> [];
106 27
107 /** 28 /**
108 * The source being scanned. 29 * The source being scanned.
109 */ 30 */
110 final Source source; 31 final Source source;
(...skipping 298 matching lines...) Expand 10 before | Expand all | Expand 10 after
409 _emitWithOffsetAndLength(TokenType.TAG, start, -1); 330 _emitWithOffsetAndLength(TokenType.TAG, start, -1);
410 } else { 331 } else {
411 // a non-char token 332 // a non-char token
412 _emitWithOffsetAndLength(TokenType.TEXT, start, 0); 333 _emitWithOffsetAndLength(TokenType.TEXT, start, 0);
413 c = advance(); 334 c = advance();
414 } 335 }
415 } 336 }
416 } 337 }
417 } 338 }
418 339
340 class ExpressionVisitor_HtmlUnitUtils_getExpression extends ExpressionVisitor {
341 int offset = 0;
342
343 List<Expression> result;
344
345 ExpressionVisitor_HtmlUnitUtils_getExpression(this.offset, this.result) : supe r();
346
347 @override
348 void visitExpression(Expression expression) {
349 Expression at = HtmlUnitUtils._getExpressionAt(expression, offset);
350 if (at != null) {
351 result[0] = at;
352 throw new HtmlUnitUtils_FoundExpressionError();
353 }
354 }
355 }
356
419 /** 357 /**
420 * Instances of the class `Token` represent a token that was scanned from the in put. Each 358 * Instances of the class `HtmlParser` are used to parse tokens into a AST struc ture comprised
421 * token knows which token follows it, acting as the head of a linked list of to kens. 359 * of [XmlNode]s.
422 */ 360 */
423 class Token { 361 class HtmlParser extends XmlParser {
424 /** 362 /**
425 * The offset from the beginning of the file to the first character in the tok en. 363 * The line information associated with the source being parsed.
426 */ 364 */
427 final int offset; 365 LineInfo _lineInfo;
428 366
429 /** 367 /**
430 * The previous token in the token stream. 368 * The error listener to which errors will be reported.
431 */ 369 */
432 Token previous; 370 final AnalysisErrorListener _errorListener;
433 371
434 /** 372 static String _APPLICATION_DART_IN_DOUBLE_QUOTES = "\"application/dart\"";
435 * The next token in the token stream. 373
436 */ 374 static String _APPLICATION_DART_IN_SINGLE_QUOTES = "'application/dart'";
437 Token _next; 375
438 376 static String _SCRIPT = "script";
439 /** 377
440 * The type of the token. 378 static String _TYPE = "type";
441 */ 379
442 final TokenType type; 380 /**
443 381 * A set containing the names of tags that do not have a closing tag.
444 /** 382 */
445 * The lexeme represented by this token. 383 static Set<String> SELF_CLOSING = new Set<String>.from(JavaArrays.asList(<Stri ng> [
446 */ 384 "area",
447 String _value; 385 "base",
448 386 "basefont",
449 /** 387 "br",
450 * Initialize a newly created token. 388 "col",
451 * 389 "frame",
452 * @param type the token type (not `null`) 390 "hr",
453 * @param offset the offset from the beginning of the file to the first charac ter in the token 391 "img",
454 */ 392 "input",
455 Token.con1(TokenType type, int offset) : this.con2(type, offset, type.lexeme); 393 "link",
456 394 "meta",
457 /** 395 "param",
458 * Initialize a newly created token. 396 "!"]));
459 * 397
460 * @param type the token type (not `null`) 398 /**
461 * @param offset the offset from the beginning of the file to the first charac ter in the token 399 * Given the contents of an embedded expression that occurs at the given offse t, parse it as a
462 * @param value the lexeme represented by this token (not `null`) 400 * Dart expression. The contents should not include the expression's delimiter s.
463 */ 401 *
464 Token.con2(this.type, this.offset, String value) { 402 * @param source the source that contains that given token
465 this._value = StringUtilities.intern(value); 403 * @param token the token to start parsing from
466 } 404 * @return the Dart expression that was parsed
467 405 */
468 /** 406 static Expression parseEmbeddedExpression(Source source, sc.Token token, Analy sisErrorListener errorListener) {
469 * Return the offset from the beginning of the file to the character after las t character of the 407 Parser parser = new Parser(source, errorListener);
470 * token. 408 return parser.parseExpression(token);
471 * 409 }
472 * @return the offset from the beginning of the file to the first character af ter last character 410
473 * of the token 411 /**
474 */ 412 * Given the contents of an embedded expression that occurs at the given offse t, scans it as a
475 int get end => offset + length; 413 * Dart code.
476 414 *
477 /** 415 * @param source the source of that contains the given contents
478 * Return the number of characters in the node's source range. 416 * @param contents the contents to scan
479 * 417 * @param contentOffset the offset of the contents in the larger file
480 * @return the number of characters in the node's source range 418 * @return the first Dart token
481 */ 419 */
482 int get length => lexeme.length; 420 static sc.Token scanDartSource(Source source, LineInfo lineInfo, String conten ts, int contentOffset, AnalysisErrorListener errorListener) {
483 421 LineInfo_Location location = lineInfo.getLocation(contentOffset);
484 /** 422 sc.Scanner scanner = new sc.Scanner(source, new sc.SubSequenceReader(content s, contentOffset), errorListener);
485 * Return the lexeme that represents this token. 423 scanner.setSourceStart(location.lineNumber, location.columnNumber);
486 * 424 return scanner.tokenize();
487 * @return the lexeme (not `null`) 425 }
488 */ 426
489 String get lexeme => _value; 427 /**
490 428 * Construct a parser for the specified source.
491 /** 429 *
492 * Return the next token in the token stream. 430 * @param source the source being parsed
493 * 431 * @param errorListener the error listener to which errors will be reported
494 * @return the next token in the token stream 432 */
495 */ 433 HtmlParser(Source source, this._errorListener) : super(source);
496 Token get next => _next; 434
497 435 /**
498 /** 436 * Parse the given tokens.
499 * Return `true` if this token is a synthetic token. A synthetic token is a to ken that was 437 *
500 * introduced by the parser in order to recover from an error in the code. Syn thetic tokens always 438 * @param token the first token in the stream of tokens to be parsed
501 * have a length of zero (`0`). 439 * @param lineInfo the line information created by the scanner
502 * 440 * @return the parse result (not `null`)
503 * @return `true` if this token is a synthetic token 441 */
504 */ 442 HtmlUnit parse(Token token, LineInfo lineInfo) {
505 bool get isSynthetic => length == 0; 443 this._lineInfo = lineInfo;
506 444 List<XmlTagNode> tagNodes = parseTopTagNodes(token);
507 /** 445 return new HtmlUnit(token, tagNodes, currentToken);
508 * Set the next token in the token stream to the given token. This has the sid e-effect of setting 446 }
509 * this token to be the previous token for the given token. 447
510 * 448 @override
511 * @param token the next token in the token stream 449 XmlAttributeNode createAttributeNode(Token name, Token equals, Token value) => new XmlAttributeNode(name, equals, value);
512 * @return the token that was passed in 450
513 */ 451 @override
514 Token setNext(Token token) { 452 XmlTagNode createTagNode(Token nodeStart, Token tag, List<XmlAttributeNode> at tributes, Token attributeEnd, List<XmlTagNode> tagNodes, Token contentEnd, Token closingTag, Token nodeEnd) {
515 _next = token; 453 if (_isScriptNode(tag, attributes, tagNodes)) {
516 token.previous = this; 454 HtmlScriptTagNode tagNode = new HtmlScriptTagNode(nodeStart, tag, attribut es, attributeEnd, tagNodes, contentEnd, closingTag, nodeEnd);
517 return token; 455 String contents = tagNode.content;
518 } 456 int contentOffset = attributeEnd.end;
519 457 LineInfo_Location location = _lineInfo.getLocation(contentOffset);
520 @override 458 sc.Scanner scanner = new sc.Scanner(source, new sc.SubSequenceReader(conte nts, contentOffset), _errorListener);
521 String toString() => lexeme; 459 scanner.setSourceStart(location.lineNumber, location.columnNumber);
460 sc.Token firstToken = scanner.tokenize();
461 Parser parser = new Parser(source, _errorListener);
462 CompilationUnit unit = parser.parseCompilationUnit(firstToken);
463 unit.lineInfo = _lineInfo;
464 tagNode.script = unit;
465 return tagNode;
466 }
467 return new XmlTagNode(nodeStart, tag, attributes, attributeEnd, tagNodes, co ntentEnd, closingTag, nodeEnd);
468 }
469
470 @override
471 bool isSelfClosing(Token tag) => SELF_CLOSING.contains(tag.lexeme);
472
473 /**
474 * Determine if the specified node is a Dart script.
475 *
476 * @param node the node to be tested (not `null`)
477 * @return `true` if the node is a Dart script
478 */
479 bool _isScriptNode(Token tag, List<XmlAttributeNode> attributes, List<XmlTagNo de> tagNodes) {
480 if (tagNodes.length != 0 || tag.lexeme != _SCRIPT) {
481 return false;
482 }
483 for (XmlAttributeNode attribute in attributes) {
484 if (attribute.name == _TYPE) {
485 Token valueToken = attribute.valueToken;
486 if (valueToken != null) {
487 String value = valueToken.lexeme;
488 if (value == _APPLICATION_DART_IN_DOUBLE_QUOTES || value == _APPLICATI ON_DART_IN_SINGLE_QUOTES) {
489 return true;
490 }
491 }
492 }
493 }
494 return false;
495 }
522 } 496 }
523 497
524 /** 498 /**
525 * Instances of `XmlTagNode` represent XML or HTML elements such as `` and 499 * Instances of the class `HtmlScriptTagNode` represent a script tag within an H TML file that
526 * `<body foo="bar"> ... </body>`. 500 * references a Dart script.
527 */ 501 */
528 class XmlTagNode extends XmlNode { 502 class HtmlScriptTagNode extends XmlTagNode {
529 /** 503 /**
530 * Constant representing empty list of attributes. 504 * The AST structure representing the Dart code within this tag.
531 */ 505 */
532 static List<XmlAttributeNode> NO_ATTRIBUTES = new UnmodifiableListView(new Lis t<XmlAttributeNode>()); 506 CompilationUnit _script;
533 507
534 /** 508 /**
535 * Constant representing empty list of tag nodes. 509 * The element representing this script.
536 */ 510 */
537 static List<XmlTagNode> NO_TAG_NODES = new UnmodifiableListView(new List<XmlTa gNode>()); 511 HtmlScriptElement scriptElement;
538 512
539 /** 513 /**
540 * The starting [TokenType#LT] token (not `null`). 514 * Initialize a newly created node to represent a script tag within an HTML fi le that references a
541 */ 515 * Dart script.
542 final Token nodeStart; 516 *
543 517 * @param nodeStart the token marking the beginning of the tag
544 /** 518 * @param tag the name of the tag
545 * The [TokenType#TAG] token after the starting '&lt;' (not `null`). 519 * @param attributes the attributes in the tag
546 */ 520 * @param attributeEnd the token terminating the region where attributes can b e
547 final Token _tag; 521 * @param tagNodes the children of the tag
548 522 * @param contentEnd the token that starts the closing tag
549 /** 523 * @param closingTag the name of the tag that occurs in the closing tag
550 * The attributes contained by the receiver (not `null`, contains no `null`s). 524 * @param nodeEnd the last token in the tag
551 */ 525 */
552 List<XmlAttributeNode> _attributes; 526 HtmlScriptTagNode(Token nodeStart, Token tag, List<XmlAttributeNode> attribute s, Token attributeEnd, List<XmlTagNode> tagNodes, Token contentEnd, Token closin gTag, Token nodeEnd) : super(nodeStart, tag, attributes, attributeEnd, tagNodes, contentEnd, closingTag, nodeEnd);
553 527
554 /** 528 @override
555 * The [TokenType#GT] or [TokenType#SLASH_GT] token after the attributes (not 529 accept(XmlVisitor visitor) => visitor.visitHtmlScriptTagNode(this);
556 * `null`). The token may be the same token as [nodeEnd] if there are no child 530
557 * [tagNodes]. 531 /**
558 */ 532 * Return the AST structure representing the Dart code within this tag, or `nu ll` if this
559 final Token attributeEnd; 533 * tag references an external script.
534 *
535 * @return the AST structure representing the Dart code within this tag
536 */
537 CompilationUnit get script => _script;
538
539 /**
540 * Set the AST structure representing the Dart code within this tag to the giv en compilation unit.
541 *
542 * @param unit the AST structure representing the Dart code within this tag
543 */
544 void set script(CompilationUnit unit) {
545 _script = unit;
546 }
547 }
548
549 /**
550 * Instances of the class `HtmlUnit` represent the contents of an HTML file.
551 */
552 class HtmlUnit extends XmlNode {
553 /**
554 * The first token in the token stream that was parsed to form this HTML unit.
555 */
556 final Token beginToken;
557
558 /**
559 * The last token in the token stream that was parsed to form this compilation unit. This token
560 * should always have a type of [TokenType.EOF].
561 */
562 final Token endToken;
560 563
561 /** 564 /**
562 * The tag nodes contained in the receiver (not `null`, contains no `null`s). 565 * The tag nodes contained in the receiver (not `null`, contains no `null`s).
563 */ 566 */
564 List<XmlTagNode> _tagNodes; 567 List<XmlTagNode> _tagNodes;
565 568
566 /** 569 /**
567 * The token (not `null`) after the content, which may be 570 * Construct a new instance representing the content of an HTML file.
568 * * (1) [TokenType#LT_SLASH] for nodes with open and close tags, or 571 *
569 * * (2) the [TokenType#LT] nodeStart of the next sibling node if this node is self 572 * @param beginToken the first token in the file (not `null`)
570 * closing or the attributeEnd is [TokenType#SLASH_GT], or 573 * @param tagNodes child tag nodes of the receiver (not `null`, contains no `n ull`s)
571 * * (3) [TokenType#EOF] if the node does not have a closing tag and is the la st node in 574 * @param endToken the last token in the token stream which should be of type
572 * the stream [TokenType#LT_SLASH] token after the content, or `null` if there is no 575 * [TokenType.EOF]
573 * content and the attributes ended with [TokenType#SLASH_GT]. 576 */
574 */ 577 HtmlUnit(this.beginToken, List<XmlTagNode> tagNodes, this.endToken) {
575 final Token contentEnd; 578 this._tagNodes = becomeParentOfAll(tagNodes);
576 579 }
577 /** 580
578 * The closing [TokenType#TAG] after the child elements or `null` if there is no 581 @override
579 * content and the attributes ended with [TokenType#SLASH_GT] 582 accept(XmlVisitor visitor) => visitor.visitHtmlUnit(this);
580 */ 583
581 final Token closingTag; 584 /**
582 585 * Return the element associated with this HTML unit.
583 /** 586 *
584 * The ending [TokenType#GT] or [TokenType#SLASH_GT] token (not `null`). 587 * @return the element or `null` if the receiver is not resolved
585 */ 588 */
586 final Token nodeEnd; 589 @override
587 590 HtmlElement get element => super.element as HtmlElement;
588 /**
589 * The expressions that are embedded in the tag's content.
590 */
591 List<XmlExpression> expressions = XmlExpression.EMPTY_ARRAY;
592
593 /**
594 * Construct a new instance representing an XML or HTML element
595 *
596 * @param nodeStart the starting [TokenType#LT] token (not `null`)
597 * @param tag the [TokenType#TAG] token after the starting '&lt;' (not `null`) .
598 * @param attributes the attributes associated with this element or [NO_ATTRIB UTES] (not
599 * `null`, contains no `null`s)
600 * @param attributeEnd The [TokenType#GT] or [TokenType#SLASH_GT] token after the
601 * attributes (not `null`). The token may be the same token as [nodeE nd] if
602 * there are no child [tagNodes].
603 * @param tagNodes child tag nodes of the receiver or [NO_TAG_NODES] (not `nul l`,
604 * contains no `null`s)
605 * @param contentEnd the token (not `null`) after the content, which may be
606 * * (1) [TokenType#LT_SLASH] for nodes with open and close tags, or
607 * * (2) the [TokenType#LT] nodeStart of the next sibling node if thi s node is
608 * self closing or the attributeEnd is [TokenType#SLASH_GT], or
609 * * (3) [TokenType#EOF] if the node does not have a closing tag and is the last
610 * node in the stream [TokenType#LT_SLASH] token after the content, o r `null`
611 * if there is no content and the attributes ended with [TokenType#SL ASH_GT].
612 * @param closingTag the closing [TokenType#TAG] after the child elements or ` null` if
613 * there is no content and the attributes ended with [TokenType#SLASH _GT]
614 * @param nodeEnd the ending [TokenType#GT] or [TokenType#SLASH_GT] token (not
615 * `null`)
616 */
617 XmlTagNode(this.nodeStart, this._tag, List<XmlAttributeNode> attributes, this. attributeEnd, List<XmlTagNode> tagNodes, this.contentEnd, this.closingTag, this. nodeEnd) {
618 this._attributes = becomeParentOfAll(attributes, ifEmpty: NO_ATTRIBUTES);
619 this._tagNodes = becomeParentOfAll(tagNodes, ifEmpty: NO_TAG_NODES);
620 }
621
622 @override
623 accept(XmlVisitor visitor) => visitor.visitXmlTagNode(this);
624
625 /**
626 * Answer the attribute with the specified name.
627 *
628 * @param name the attribute name
629 * @return the attribute or `null` if no matching attribute is found
630 */
631 XmlAttributeNode getAttribute(String name) {
632 for (XmlAttributeNode attribute in _attributes) {
633 if (attribute.name == name) {
634 return attribute;
635 }
636 }
637 return null;
638 }
639
640 /**
641 * Answer the receiver's attributes. Callers should not manipulate the returne d list to edit the
642 * AST structure.
643 *
644 * @return the attributes (not `null`, contains no `null`s)
645 */
646 List<XmlAttributeNode> get attributes => _attributes;
647
648 /**
649 * Find the attribute with the given name (see [getAttribute] and answer the l exeme
650 * for the attribute's value token without the leading and trailing quotes (se e
651 * [XmlAttributeNode#getText]).
652 *
653 * @param name the attribute name
654 * @return the attribute text or `null` if no matching attribute is found
655 */
656 String getAttributeText(String name) {
657 XmlAttributeNode attribute = getAttribute(name);
658 return attribute != null ? attribute.text : null;
659 }
660
661 @override
662 Token get beginToken => nodeStart;
663
664 /**
665 * Answer a string representing the content contained in the receiver. This in cludes the textual
666 * representation of any child tag nodes ([getTagNodes]). Whitespace between ' &lt;',
667 * '&lt;/', and '>', '/>' is discarded, but all other whitespace is preserved.
668 *
669 * @return the content (not `null`)
670 */
671 String get content {
672 Token token = attributeEnd.next;
673 if (identical(token, contentEnd)) {
674 return "";
675 }
676 //TODO (danrubel): handle CDATA and replace HTML character encodings with th e actual characters
677 String content = token.lexeme;
678 token = token.next;
679 if (identical(token, contentEnd)) {
680 return content;
681 }
682 JavaStringBuilder buffer = new JavaStringBuilder();
683 while (!identical(token, contentEnd)) {
684 buffer.append(token.lexeme);
685 token = token.next;
686 }
687 return buffer.toString();
688 }
689
690 @override
691 Token get endToken {
692 if (nodeEnd != null) {
693 return nodeEnd;
694 }
695 if (closingTag != null) {
696 return closingTag;
697 }
698 if (contentEnd != null) {
699 return contentEnd;
700 }
701 if (!_tagNodes.isEmpty) {
702 return _tagNodes[_tagNodes.length - 1].endToken;
703 }
704 if (attributeEnd != null) {
705 return attributeEnd;
706 }
707 if (!_attributes.isEmpty) {
708 return _attributes[_attributes.length - 1].endToken;
709 }
710 return _tag;
711 }
712
713 /**
714 * Answer the tag name after the starting '&lt;'.
715 *
716 * @return the tag name (not `null`)
717 */
718 String get tag => _tag.lexeme;
719 591
720 /** 592 /**
721 * Answer the tag nodes contained in the receiver. Callers should not manipula te the returned list 593 * Answer the tag nodes contained in the receiver. Callers should not manipula te the returned list
722 * to edit the AST structure. 594 * to edit the AST structure.
723 * 595 *
724 * @return the children (not `null`, contains no `null`s) 596 * @return the children (not `null`, contains no `null`s)
725 */ 597 */
726 List<XmlTagNode> get tagNodes => _tagNodes; 598 List<XmlTagNode> get tagNodes => _tagNodes;
727 599
728 /** 600 @override
729 * Answer the [TokenType#TAG] token after the starting '&lt;'. 601 void set element(Element element) {
730 * 602 if (element != null && element is! HtmlElement) {
731 * @return the token (not `null`) 603 throw new IllegalArgumentException("HtmlElement expected, but ${element.ru ntimeType} given");
732 */ 604 }
733 Token get tagToken => _tag; 605 super.element = element;
606 }
734 607
735 @override 608 @override
736 void visitChildren(XmlVisitor visitor) { 609 void visitChildren(XmlVisitor visitor) {
737 for (XmlAttributeNode node in _attributes) {
738 node.accept(visitor);
739 }
740 for (XmlTagNode node in _tagNodes) { 610 for (XmlTagNode node in _tagNodes) {
741 node.accept(visitor); 611 node.accept(visitor);
742 } 612 }
743 } 613 }
744 } 614 }
745 615
746 /** 616 /**
747 * Instances of the class `XmlParser` are used to parse tokens into a AST struct ure comprised 617 * Utilities locating [Expression]s and [Element]s in [HtmlUnit].
748 * of [XmlNode]s.
749 */ 618 */
750 class XmlParser { 619 class HtmlUnitUtils {
751 /** 620 /**
752 * The source being parsed. 621 * Returns the [XmlAttributeNode] that is part of the given [HtmlUnit] and enc loses
622 * the given offset.
753 */ 623 */
754 final Source source; 624 static XmlAttributeNode getAttributeNode(HtmlUnit htmlUnit, int offset) {
755 625 if (htmlUnit == null) {
756 /** 626 return null;
757 * The next token to be parsed.
758 */
759 Token _currentToken;
760
761 /**
762 * Construct a parser for the specified source.
763 *
764 * @param source the source being parsed
765 */
766 XmlParser(this.source);
767
768 /**
769 * Create a node representing an attribute.
770 *
771 * @param name the name of the attribute
772 * @param equals the equals sign, or `null` if there is no value
773 * @param value the value of the attribute
774 * @return the node that was created
775 */
776 XmlAttributeNode createAttributeNode(Token name, Token equals, Token value) => new XmlAttributeNode(name, equals, value);
777
778 /**
779 * Create a node representing a tag.
780 *
781 * @param nodeStart the token marking the beginning of the tag
782 * @param tag the name of the tag
783 * @param attributes the attributes in the tag
784 * @param attributeEnd the token terminating the region where attributes can b e
785 * @param tagNodes the children of the tag
786 * @param contentEnd the token that starts the closing tag
787 * @param closingTag the name of the tag that occurs in the closing tag
788 * @param nodeEnd the last token in the tag
789 * @return the node that was created
790 */
791 XmlTagNode createTagNode(Token nodeStart, Token tag, List<XmlAttributeNode> at tributes, Token attributeEnd, List<XmlTagNode> tagNodes, Token contentEnd, Token closingTag, Token nodeEnd) => new XmlTagNode(nodeStart, tag, attributes, attrib uteEnd, tagNodes, contentEnd, closingTag, nodeEnd);
792
793 /**
794 * Answer `true` if the specified tag is self closing and thus should never ha ve content or
795 * child tag nodes.
796 *
797 * @param tag the tag (not `null`)
798 * @return `true` if self closing
799 */
800 bool isSelfClosing(Token tag) => false;
801
802 /**
803 * Parse the entire token stream and in the process, advance the current token to the end of the
804 * token stream.
805 *
806 * @return the list of tag nodes found (not `null`, contains no `null`)
807 */
808 List<XmlTagNode> parseTopTagNodes(Token firstToken) {
809 _currentToken = firstToken;
810 List<XmlTagNode> tagNodes = new List<XmlTagNode>();
811 TokenType type = _currentToken.type;
812 while (type != TokenType.EOF) {
813 if (type == TokenType.LT) {
814 tagNodes.add(_parseTagNode());
815 } else if (type == TokenType.DECLARATION || type == TokenType.DIRECTIVE || type == TokenType.COMMENT) {
816 // ignored tokens
817 _currentToken = _currentToken.next;
818 } else {
819 _reportUnexpectedToken();
820 _currentToken = _currentToken.next;
821 }
822 type = _currentToken.type;
823 } 627 }
824 return tagNodes; 628 List<XmlAttributeNode> result = [null];
629 try {
630 htmlUnit.accept(new RecursiveXmlVisitor_HtmlUnitUtils_getAttributeNode(off set, result));
631 } on HtmlUnitUtils_FoundAttributeNodeError catch (e) {
632 return result[0];
633 }
634 return null;
825 } 635 }
826 636
827 /** 637 /**
828 * Answer the current token. 638 * Returns the best [Element] of the given [Expression].
829 *
830 * @return the current token
831 */ 639 */
832 Token get currentToken => _currentToken; 640 static Element getElement(Expression expression) {
833 641 if (expression == null) {
834 /** 642 return null;
835 * Insert a synthetic token of the specified type before the current token 643 }
836 * 644 return ElementLocator.locate(expression);
837 * @param type the type of token to be inserted (not `null`)
838 * @return the synthetic token that was inserted (not `null`)
839 */
840 Token _insertSyntheticToken(TokenType type) {
841 Token token = new Token.con2(type, _currentToken.offset, "");
842 _currentToken.previous.setNext(token);
843 token.setNext(_currentToken);
844 return token;
845 } 645 }
846 646
847 /** 647 /**
848 * Parse the token stream for an attribute. This method advances the current t oken over the 648 * Returns the [Element] of the [Expression] in the given [HtmlUnit], enclosin g
849 * attribute, but should not be called if the [currentToken] is not [TokenType #TAG]. 649 * the given offset.
850 *
851 * @return the attribute (not `null`)
852 */ 650 */
853 XmlAttributeNode _parseAttribute() { 651 static Element getElementAtOffset(HtmlUnit htmlUnit, int offset) {
854 // Assume the current token is a tag 652 Expression expression = getExpression(htmlUnit, offset);
855 Token name = _currentToken; 653 return getElement(expression);
856 _currentToken = _currentToken.next;
857 // Equals sign
858 Token equals;
859 if (_currentToken.type == TokenType.EQ) {
860 equals = _currentToken;
861 _currentToken = _currentToken.next;
862 } else {
863 _reportUnexpectedToken();
864 equals = _insertSyntheticToken(TokenType.EQ);
865 }
866 // String value
867 Token value;
868 if (_currentToken.type == TokenType.STRING) {
869 value = _currentToken;
870 _currentToken = _currentToken.next;
871 } else {
872 _reportUnexpectedToken();
873 value = _insertSyntheticToken(TokenType.STRING);
874 }
875 return createAttributeNode(name, equals, value);
876 } 654 }
877 655
878 /** 656 /**
879 * Parse the stream for a sequence of attributes. This method advances the cur rent token to the 657 * Returns the [Element] to open when requested at the given [Expression].
880 * next [TokenType#GT], [TokenType#SLASH_GT], or [TokenType#EOF].
881 *
882 * @return a collection of zero or more attributes (not `null`, contains no `n ull`s)
883 */ 658 */
884 List<XmlAttributeNode> _parseAttributes() { 659 static Element getElementToOpen(HtmlUnit htmlUnit, Expression expression) {
885 TokenType type = _currentToken.type; 660 Element element = getElement(expression);
886 if (type == TokenType.GT || type == TokenType.SLASH_GT || type == TokenType. EOF) { 661 {
887 return XmlTagNode.NO_ATTRIBUTES; 662 AngularElement angularElement = AngularHtmlUnitResolver.getAngularElement( element);
663 if (angularElement != null) {
664 return angularElement;
665 }
888 } 666 }
889 List<XmlAttributeNode> attributes = new List<XmlAttributeNode>(); 667 return element;
890 while (type != TokenType.GT && type != TokenType.SLASH_GT && type != TokenTy pe.EOF) {
891 if (type == TokenType.TAG) {
892 attributes.add(_parseAttribute());
893 } else {
894 _reportUnexpectedToken();
895 _currentToken = _currentToken.next;
896 }
897 type = _currentToken.type;
898 }
899 return attributes;
900 } 668 }
901 669
902 /** 670 /**
903 * Parse the stream for a sequence of tag nodes existing within a parent tag n ode. This method 671 * Returns the [XmlTagNode] that is part of the given [HtmlUnit] and encloses the
904 * advances the current token to the next [TokenType#LT_SLASH] or [TokenType#E OF]. 672 * given offset.
905 *
906 * @return a list of nodes (not `null`, contains no `null`s)
907 */ 673 */
908 List<XmlTagNode> _parseChildTagNodes() { 674 static XmlTagNode getEnclosingTagNode(HtmlUnit htmlUnit, int offset) {
909 TokenType type = _currentToken.type; 675 if (htmlUnit == null) {
910 if (type == TokenType.LT_SLASH || type == TokenType.EOF) { 676 return null;
911 return XmlTagNode.NO_TAG_NODES;
912 } 677 }
913 List<XmlTagNode> nodes = new List<XmlTagNode>(); 678 List<XmlTagNode> result = [null];
914 while (type != TokenType.LT_SLASH && type != TokenType.EOF) { 679 try {
915 if (type == TokenType.LT) { 680 htmlUnit.accept(new RecursiveXmlVisitor_HtmlUnitUtils_getEnclosingTagNode( offset, result));
916 nodes.add(_parseTagNode()); 681 } on HtmlUnitUtils_FoundTagNodeError catch (e) {
917 } else if (type == TokenType.COMMENT) { 682 return result[0];
918 // ignored token
919 _currentToken = _currentToken.next;
920 } else {
921 _reportUnexpectedToken();
922 _currentToken = _currentToken.next;
923 }
924 type = _currentToken.type;
925 } 683 }
926 return nodes; 684 return null;
927 } 685 }
928 686
929 /** 687 /**
930 * Parse the token stream for the next tag node. This method advances current token over the 688 * Returns the [Expression] that is part of the given [HtmlUnit] and encloses the
931 * parsed tag node, but should only be called if the current token is [TokenTy pe#LT] 689 * given offset.
932 *
933 * @return the tag node or `null` if none found
934 */ 690 */
935 XmlTagNode _parseTagNode() { 691 static Expression getExpression(HtmlUnit htmlUnit, int offset) {
936 // Assume that the current node is a tag node start TokenType#LT 692 if (htmlUnit == null) {
937 Token nodeStart = _currentToken; 693 return null;
938 _currentToken = _currentToken.next;
939 // Get the tag or create a synthetic tag and report an error
940 Token tag;
941 if (_currentToken.type == TokenType.TAG) {
942 tag = _currentToken;
943 _currentToken = _currentToken.next;
944 } else {
945 _reportUnexpectedToken();
946 tag = _insertSyntheticToken(TokenType.TAG);
947 } 694 }
948 // Parse the attributes 695 List<Expression> result = [null];
949 List<XmlAttributeNode> attributes = _parseAttributes(); 696 try {
950 // Token ending attribute list 697 // TODO(scheglov) this code is very Angular specific
951 Token attributeEnd; 698 htmlUnit.accept(new ExpressionVisitor_HtmlUnitUtils_getExpression(offset, result));
952 if (_currentToken.type == TokenType.GT || _currentToken.type == TokenType.SL ASH_GT) { 699 } on HtmlUnitUtils_FoundExpressionError catch (e) {
953 attributeEnd = _currentToken; 700 return result[0];
954 _currentToken = _currentToken.next;
955 } else {
956 _reportUnexpectedToken();
957 attributeEnd = _insertSyntheticToken(TokenType.SLASH_GT);
958 } 701 }
959 // If the node has no children, then return the node 702 return null;
960 if (attributeEnd.type == TokenType.SLASH_GT || isSelfClosing(tag)) {
961 return createTagNode(nodeStart, tag, attributes, attributeEnd, XmlTagNode. NO_TAG_NODES, _currentToken, null, attributeEnd);
962 }
963 // Parse the child tag nodes
964 List<XmlTagNode> tagNodes = _parseChildTagNodes();
965 // Token ending child tag nodes
966 Token contentEnd;
967 if (_currentToken.type == TokenType.LT_SLASH) {
968 contentEnd = _currentToken;
969 _currentToken = _currentToken.next;
970 } else {
971 // TODO (danrubel): handle self closing HTML elements by inserting synthet ic tokens
972 // but not reporting an error
973 _reportUnexpectedToken();
974 contentEnd = _insertSyntheticToken(TokenType.LT_SLASH);
975 }
976 // Closing tag
977 Token closingTag;
978 if (_currentToken.type == TokenType.TAG) {
979 closingTag = _currentToken;
980 _currentToken = _currentToken.next;
981 } else {
982 _reportUnexpectedToken();
983 closingTag = _insertSyntheticToken(TokenType.TAG);
984 }
985 // Token ending node
986 Token nodeEnd;
987 if (_currentToken.type == TokenType.GT) {
988 nodeEnd = _currentToken;
989 _currentToken = _currentToken.next;
990 } else {
991 _reportUnexpectedToken();
992 nodeEnd = _insertSyntheticToken(TokenType.GT);
993 }
994 return createTagNode(nodeStart, tag, attributes, attributeEnd, tagNodes, con tentEnd, closingTag, nodeEnd);
995 } 703 }
996 704
997 /** 705 /**
998 * Report the current token as unexpected 706 * Returns the [XmlTagNode] that is part of the given [HtmlUnit] and its open or
707 * closing tag name encloses the given offset.
999 */ 708 */
1000 void _reportUnexpectedToken() { 709 static XmlTagNode getTagNode(HtmlUnit htmlUnit, int offset) {
710 XmlTagNode node = getEnclosingTagNode(htmlUnit, offset);
711 // do we have an enclosing tag at all?
712 if (node == null) {
713 return null;
714 }
715 // is "offset" in the open tag?
716 Token openTag = node.tagToken;
717 if (openTag.offset <= offset && offset <= openTag.end) {
718 return node;
719 }
720 // is "offset" in the open tag?
721 Token closeTag = node.closingTag;
722 if (closeTag != null && closeTag.offset <= offset && offset <= closeTag.end) {
723 return node;
724 }
725 // not on a tag name
726 return null;
727 }
728
729 /**
730 * Returns the [Expression] that is part of the given root [AstNode] and enclo ses the
731 * given offset.
732 */
733 static Expression _getExpressionAt(AstNode root, int offset) {
734 if (root.offset <= offset && offset <= root.end) {
735 AstNode dartNode = new NodeLocator.con1(offset).searchWithin(root);
736 if (dartNode is Expression) {
737 return dartNode;
738 }
739 }
740 return null;
1001 } 741 }
1002 } 742 }
1003 743
1004 /** 744 class HtmlUnitUtils_FoundAttributeNodeError extends Error {
1005 * The abstract class `XmlNode` defines behavior common to all XML/HTML nodes. 745 }
1006 */
1007 abstract class XmlNode {
1008 /**
1009 * The parent of the node, or `null` if the node is the root of an AST structu re.
1010 */
1011 XmlNode _parent;
1012 746
1013 /** 747 class HtmlUnitUtils_FoundExpressionError extends Error {
1014 * The element associated with this node or `null` if the receiver is not reso lved. 748 }
1015 */
1016 Element _element;
1017 749
1018 /** 750 class HtmlUnitUtils_FoundTagNodeError extends Error {
1019 * Use the given visitor to visit this node.
1020 *
1021 * @param visitor the visitor that will visit this node
1022 * @return the value returned by the visitor as a result of visiting this node
1023 */
1024 accept(XmlVisitor visitor);
1025
1026 /**
1027 * Return the first token included in this node's source range.
1028 *
1029 * @return the first token or `null` if none
1030 */
1031 Token get beginToken;
1032
1033 /**
1034 * Return the element associated with this node.
1035 *
1036 * @return the element or `null` if the receiver is not resolved
1037 */
1038 Element get element => _element;
1039
1040 /**
1041 * Return the offset of the character immediately following the last character of this node's
1042 * source range. This is equivalent to `node.getOffset() + node.getLength()`. For an html
1043 * unit this will be equal to the length of the unit's source.
1044 *
1045 * @return the offset of the character just past the node's source range
1046 */
1047 int get end => offset + length;
1048
1049 /**
1050 * Return the last token included in this node's source range.
1051 *
1052 * @return the last token or `null` if none
1053 */
1054 Token get endToken;
1055
1056 /**
1057 * Return the number of characters in the node's source range.
1058 *
1059 * @return the number of characters in the node's source range
1060 */
1061 int get length {
1062 Token beginToken = this.beginToken;
1063 Token endToken = this.endToken;
1064 if (beginToken == null || endToken == null) {
1065 return -1;
1066 }
1067 return endToken.offset + endToken.length - beginToken.offset;
1068 }
1069
1070 /**
1071 * Return the offset from the beginning of the file to the first character in the node's source
1072 * range.
1073 *
1074 * @return the offset from the beginning of the file to the first character in the node's source
1075 * range
1076 */
1077 int get offset {
1078 Token beginToken = this.beginToken;
1079 if (beginToken == null) {
1080 return -1;
1081 }
1082 return this.beginToken.offset;
1083 }
1084
1085 /**
1086 * Return this node's parent node, or `null` if this node is the root of an AS T structure.
1087 *
1088 * Note that the relationship between an AST node and its parent node may chan ge over the lifetime
1089 * of a node.
1090 *
1091 * @return the parent of this node, or `null` if none
1092 */
1093 XmlNode get parent => _parent;
1094
1095 /**
1096 * Set the element associated with this node.
1097 *
1098 * @param element the element
1099 */
1100 void set element(Element element) {
1101 this._element = element;
1102 }
1103
1104 @override
1105 String toString() {
1106 PrintStringWriter writer = new PrintStringWriter();
1107 accept(new ToSourceVisitor(writer));
1108 return writer.toString();
1109 }
1110
1111 /**
1112 * Use the given visitor to visit all of the children of this node. The childr en will be visited
1113 * in source order.
1114 *
1115 * @param visitor the visitor that will be used to visit the children of this node
1116 */
1117 void visitChildren(XmlVisitor visitor);
1118
1119 /**
1120 * Make this node the parent of the given child node.
1121 *
1122 * @param child the node that will become a child of this node
1123 * @return the node that was made a child of this node
1124 */
1125 XmlNode becomeParentOf(XmlNode child) {
1126 if (child != null) {
1127 XmlNode node = child;
1128 node.parent = this;
1129 }
1130 return child;
1131 }
1132
1133 /**
1134 * Make this node the parent of the given child nodes.
1135 *
1136 * @param children the nodes that will become the children of this node
1137 * @param ifEmpty the (empty) nodes to return if "children" is empty
1138 * @return the nodes that were made children of this node
1139 */
1140 List becomeParentOfAll(List children, {List ifEmpty}) {
1141 if (children == null || children.isEmpty) {
1142 if (ifEmpty != null) {
1143 return ifEmpty;
1144 }
1145 }
1146 if (children != null) {
1147 for (JavaIterator iter = new JavaIterator(children); iter.hasNext;) {
1148 XmlNode node = iter.next();
1149 node.parent = this;
1150 }
1151 // This will create ArrayList for exactly given number of elements.
1152 return new List.from(children);
1153 }
1154 return children;
1155 }
1156
1157 /**
1158 * This method exists for debugging purposes only.
1159 */
1160 void _appendIdentifier(JavaStringBuilder builder, XmlNode node) {
1161 if (node is XmlTagNode) {
1162 builder.append(node.tag);
1163 } else if (node is XmlAttributeNode) {
1164 builder.append(node.name);
1165 } else {
1166 builder.append("htmlUnit");
1167 }
1168 }
1169
1170 /**
1171 * This method exists for debugging purposes only.
1172 */
1173 String _buildRecursiveStructureMessage(XmlNode newParent) {
1174 JavaStringBuilder builder = new JavaStringBuilder();
1175 builder.append("Attempt to create recursive structure: ");
1176 XmlNode current = newParent;
1177 while (current != null) {
1178 if (!identical(current, newParent)) {
1179 builder.append(" -> ");
1180 }
1181 if (identical(current, this)) {
1182 builder.appendChar(0x2A);
1183 _appendIdentifier(builder, current);
1184 builder.appendChar(0x2A);
1185 } else {
1186 _appendIdentifier(builder, current);
1187 }
1188 current = current.parent;
1189 }
1190 return builder.toString();
1191 }
1192
1193 /**
1194 * Set the parent of this node to the given node.
1195 *
1196 * @param newParent the node that is to be made the parent of this node
1197 */
1198 void set parent(XmlNode newParent) {
1199 XmlNode current = newParent;
1200 while (current != null) {
1201 if (identical(current, this)) {
1202 AnalysisEngine.instance.logger.logError2("Circular structure while setti ng an XML node's parent", new IllegalArgumentException(_buildRecursiveStructureM essage(newParent)));
1203 return;
1204 }
1205 current = current.parent;
1206 }
1207 _parent = newParent;
1208 }
1209 } 751 }
1210 752
1211 /** 753 /**
1212 * Implementation of [XmlExpression] for an [Expression] embedded without any wr apping 754 * Implementation of [XmlExpression] for an [Expression] embedded without any wr apping
1213 * characters. 755 * characters.
1214 */ 756 */
1215 class RawXmlExpression extends XmlExpression { 757 class RawXmlExpression extends XmlExpression {
1216 final Expression expression; 758 final Expression expression;
1217 759
1218 RawXmlExpression(this.expression); 760 RawXmlExpression(this.expression);
(...skipping 12 matching lines...) Expand all
1231 AstNode node = new NodeLocator.con1(offset).searchWithin(expression); 773 AstNode node = new NodeLocator.con1(offset).searchWithin(expression);
1232 if (node != null) { 774 if (node != null) {
1233 Element element = ElementLocator.locate(node); 775 Element element = ElementLocator.locate(node);
1234 return new XmlExpression_Reference(element, node.offset, node.length); 776 return new XmlExpression_Reference(element, node.offset, node.length);
1235 } 777 }
1236 return null; 778 return null;
1237 } 779 }
1238 } 780 }
1239 781
1240 /** 782 /**
1241 * Utilities locating [Expression]s and [Element]s in [HtmlUnit]. 783 * Instances of the class `RecursiveXmlVisitor` implement an XML visitor that wi ll recursively
784 * visit all of the nodes in an XML structure. For example, using an instance of this class to visit
785 * a [XmlTagNode] will also cause all of the contained [XmlAttributeNode]s and
786 * [XmlTagNode]s to be visited.
787 *
788 * Subclasses that override a visit method must either invoke the overridden vis it method or must
789 * explicitly ask the visited node to visit its children. Failure to do so will cause the children
790 * of the visited node to not be visited.
1242 */ 791 */
1243 class HtmlUnitUtils { 792 class RecursiveXmlVisitor<R> implements XmlVisitor<R> {
1244 /** 793 @override
1245 * Returns the [XmlAttributeNode] that is part of the given [HtmlUnit] and enc loses 794 R visitHtmlScriptTagNode(HtmlScriptTagNode node) {
1246 * the given offset. 795 node.visitChildren(this);
1247 */
1248 static XmlAttributeNode getAttributeNode(HtmlUnit htmlUnit, int offset) {
1249 if (htmlUnit == null) {
1250 return null;
1251 }
1252 List<XmlAttributeNode> result = [null];
1253 try {
1254 htmlUnit.accept(new RecursiveXmlVisitor_HtmlUnitUtils_getAttributeNode(off set, result));
1255 } on HtmlUnitUtils_FoundAttributeNodeError catch (e) {
1256 return result[0];
1257 }
1258 return null; 796 return null;
1259 } 797 }
1260 798
1261 /** 799 @override
1262 * Returns the best [Element] of the given [Expression]. 800 R visitHtmlUnit(HtmlUnit node) {
1263 */ 801 node.visitChildren(this);
1264 static Element getElement(Expression expression) {
1265 if (expression == null) {
1266 return null;
1267 }
1268 return ElementLocator.locate(expression);
1269 }
1270
1271 /**
1272 * Returns the [Element] of the [Expression] in the given [HtmlUnit], enclosin g
1273 * the given offset.
1274 */
1275 static Element getElementAtOffset(HtmlUnit htmlUnit, int offset) {
1276 Expression expression = getExpression(htmlUnit, offset);
1277 return getElement(expression);
1278 }
1279
1280 /**
1281 * Returns the [Element] to open when requested at the given [Expression].
1282 */
1283 static Element getElementToOpen(HtmlUnit htmlUnit, Expression expression) {
1284 Element element = getElement(expression);
1285 {
1286 AngularElement angularElement = AngularHtmlUnitResolver.getAngularElement( element);
1287 if (angularElement != null) {
1288 return angularElement;
1289 }
1290 }
1291 return element;
1292 }
1293
1294 /**
1295 * Returns the [XmlTagNode] that is part of the given [HtmlUnit] and encloses the
1296 * given offset.
1297 */
1298 static XmlTagNode getEnclosingTagNode(HtmlUnit htmlUnit, int offset) {
1299 if (htmlUnit == null) {
1300 return null;
1301 }
1302 List<XmlTagNode> result = [null];
1303 try {
1304 htmlUnit.accept(new RecursiveXmlVisitor_HtmlUnitUtils_getEnclosingTagNode( offset, result));
1305 } on HtmlUnitUtils_FoundTagNodeError catch (e) {
1306 return result[0];
1307 }
1308 return null; 802 return null;
1309 } 803 }
1310 804
1311 /** 805 @override
1312 * Returns the [Expression] that is part of the given [HtmlUnit] and encloses the 806 R visitXmlAttributeNode(XmlAttributeNode node) {
1313 * given offset. 807 node.visitChildren(this);
1314 */
1315 static Expression getExpression(HtmlUnit htmlUnit, int offset) {
1316 if (htmlUnit == null) {
1317 return null;
1318 }
1319 List<Expression> result = [null];
1320 try {
1321 // TODO(scheglov) this code is very Angular specific
1322 htmlUnit.accept(new ExpressionVisitor_HtmlUnitUtils_getExpression(offset, result));
1323 } on HtmlUnitUtils_FoundExpressionError catch (e) {
1324 return result[0];
1325 }
1326 return null; 808 return null;
1327 } 809 }
1328 810
1329 /** 811 @override
1330 * Returns the [XmlTagNode] that is part of the given [HtmlUnit] and its open or 812 R visitXmlTagNode(XmlTagNode node) {
1331 * closing tag name encloses the given offset. 813 node.visitChildren(this);
1332 */
1333 static XmlTagNode getTagNode(HtmlUnit htmlUnit, int offset) {
1334 XmlTagNode node = getEnclosingTagNode(htmlUnit, offset);
1335 // do we have an enclosing tag at all?
1336 if (node == null) {
1337 return null;
1338 }
1339 // is "offset" in the open tag?
1340 Token openTag = node.tagToken;
1341 if (openTag.offset <= offset && offset <= openTag.end) {
1342 return node;
1343 }
1344 // is "offset" in the open tag?
1345 Token closeTag = node.closingTag;
1346 if (closeTag != null && closeTag.offset <= offset && offset <= closeTag.end) {
1347 return node;
1348 }
1349 // not on a tag name
1350 return null; 814 return null;
1351 } 815 }
1352
1353 /**
1354 * Returns the [Expression] that is part of the given root [AstNode] and enclo ses the
1355 * given offset.
1356 */
1357 static Expression _getExpressionAt(AstNode root, int offset) {
1358 if (root.offset <= offset && offset <= root.end) {
1359 AstNode dartNode = new NodeLocator.con1(offset).searchWithin(root);
1360 if (dartNode is Expression) {
1361 return dartNode;
1362 }
1363 }
1364 return null;
1365 }
1366 }
1367
1368 class HtmlUnitUtils_FoundAttributeNodeError extends Error {
1369 }
1370
1371 class HtmlUnitUtils_FoundExpressionError extends Error {
1372 }
1373
1374 class HtmlUnitUtils_FoundTagNodeError extends Error {
1375 } 816 }
1376 817
1377 class RecursiveXmlVisitor_HtmlUnitUtils_getAttributeNode extends RecursiveXmlVis itor<Object> { 818 class RecursiveXmlVisitor_HtmlUnitUtils_getAttributeNode extends RecursiveXmlVis itor<Object> {
1378 int offset = 0; 819 int offset = 0;
1379 820
1380 List<XmlAttributeNode> result; 821 List<XmlAttributeNode> result;
1381 822
1382 RecursiveXmlVisitor_HtmlUnitUtils_getAttributeNode(this.offset, this.result) : super(); 823 RecursiveXmlVisitor_HtmlUnitUtils_getAttributeNode(this.offset, this.result) : super();
1383 824
1384 @override 825 @override
(...skipping 18 matching lines...) Expand all
1403 Object visitXmlTagNode(XmlTagNode node) { 844 Object visitXmlTagNode(XmlTagNode node) {
1404 if (node.offset <= offset && offset < node.end) { 845 if (node.offset <= offset && offset < node.end) {
1405 result[0] = node; 846 result[0] = node;
1406 super.visitXmlTagNode(node); 847 super.visitXmlTagNode(node);
1407 throw new HtmlUnitUtils_FoundTagNodeError(); 848 throw new HtmlUnitUtils_FoundTagNodeError();
1408 } 849 }
1409 return null; 850 return null;
1410 } 851 }
1411 } 852 }
1412 853
1413 class ExpressionVisitor_HtmlUnitUtils_getExpression extends ExpressionVisitor {
1414 int offset = 0;
1415
1416 List<Expression> result;
1417
1418 ExpressionVisitor_HtmlUnitUtils_getExpression(this.offset, this.result) : supe r();
1419
1420 @override
1421 void visitExpression(Expression expression) {
1422 Expression at = HtmlUnitUtils._getExpressionAt(expression, offset);
1423 if (at != null) {
1424 result[0] = at;
1425 throw new HtmlUnitUtils_FoundExpressionError();
1426 }
1427 }
1428 }
1429
1430 /** 854 /**
1431 * The interface `XmlVisitor` defines the behavior of objects that can be used t o visit an 855 * Instances of the class `SimpleXmlVisitor` implement an AST visitor that will do nothing
1432 * [XmlNode] structure. 856 * when visiting an AST node. It is intended to be a superclass for classes that use the visitor
857 * pattern primarily as a dispatch mechanism (and hence don't need to recursivel y visit a whole
858 * structure) and that only need to visit a small number of node types.
1433 */ 859 */
1434 abstract class XmlVisitor<R> { 860 class SimpleXmlVisitor<R> implements XmlVisitor<R> {
1435 R visitHtmlScriptTagNode(HtmlScriptTagNode node); 861 @override
862 R visitHtmlScriptTagNode(HtmlScriptTagNode node) => null;
1436 863
1437 R visitHtmlUnit(HtmlUnit htmlUnit); 864 @override
865 R visitHtmlUnit(HtmlUnit htmlUnit) => null;
1438 866
1439 R visitXmlAttributeNode(XmlAttributeNode xmlAttributeNode); 867 @override
868 R visitXmlAttributeNode(XmlAttributeNode xmlAttributeNode) => null;
1440 869
1441 R visitXmlTagNode(XmlTagNode xmlTagNode); 870 @override
871 R visitXmlTagNode(XmlTagNode xmlTagNode) => null;
1442 } 872 }
1443 873
1444 /** 874 /**
875 * Instances of the class `StringScanner` implement a scanner that reads from a string. The
876 * scanning logic is in the superclass.
877 */
878 class StringScanner extends AbstractScanner {
879 /**
880 * The string from which characters will be read.
881 */
882 final String _string;
883
884 /**
885 * The number of characters in the string.
886 */
887 int _stringLength = 0;
888
889 /**
890 * The index, relative to the string, of the last character that was read.
891 */
892 int _charOffset = 0;
893
894 /**
895 * Initialize a newly created scanner to scan the characters in the given stri ng.
896 *
897 * @param source the source being scanned
898 * @param string the string from which characters will be read
899 */
900 StringScanner(Source source, this._string) : super(source) {
901 this._stringLength = _string.length;
902 this._charOffset = -1;
903 }
904
905 @override
906 int get offset => _charOffset;
907
908 void set offset(int offset) {
909 _charOffset = offset;
910 }
911
912 @override
913 int advance() {
914 if (++_charOffset < _stringLength) {
915 return _string.codeUnitAt(_charOffset);
916 }
917 _charOffset = _stringLength;
918 return -1;
919 }
920
921 @override
922 String getString(int start, int endDelta) => _string.substring(start, _charOff set + 1 + endDelta).toString();
923
924 @override
925 int peek() {
926 if (_charOffset + 1 < _stringLength) {
927 return _string.codeUnitAt(_charOffset + 1);
928 }
929 return -1;
930 }
931 }
932
933 /**
1445 * Instances of the class `ToSourceVisitor` write a source representation of a v isited XML 934 * Instances of the class `ToSourceVisitor` write a source representation of a v isited XML
1446 * node (and all of it's children) to a writer. 935 * node (and all of it's children) to a writer.
1447 */ 936 */
1448 class ToSourceVisitor implements XmlVisitor<Object> { 937 class ToSourceVisitor implements XmlVisitor<Object> {
1449 /** 938 /**
1450 * The writer to which the source is to be written. 939 * The writer to which the source is to be written.
1451 */ 940 */
1452 final PrintWriter _writer; 941 final PrintWriter _writer;
1453 942
1454 /** 943 /**
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
1515 * @param node the node to be visited 1004 * @param node the node to be visited
1516 */ 1005 */
1517 void _visit(XmlNode node) { 1006 void _visit(XmlNode node) {
1518 if (node != null) { 1007 if (node != null) {
1519 node.accept(this); 1008 node.accept(this);
1520 } 1009 }
1521 } 1010 }
1522 } 1011 }
1523 1012
1524 /** 1013 /**
1525 * Instances of the class `HtmlUnit` represent the contents of an HTML file. 1014 * Instances of the class `Token` represent a token that was scanned from the in put. Each
1015 * token knows which token follows it, acting as the head of a linked list of to kens.
1526 */ 1016 */
1527 class HtmlUnit extends XmlNode { 1017 class Token {
1528 /** 1018 /**
1529 * The first token in the token stream that was parsed to form this HTML unit. 1019 * The offset from the beginning of the file to the first character in the tok en.
1530 */ 1020 */
1531 final Token beginToken; 1021 final int offset;
1532 1022
1533 /** 1023 /**
1534 * The last token in the token stream that was parsed to form this compilation unit. This token 1024 * The previous token in the token stream.
1535 * should always have a type of [TokenType.EOF]. 1025 */
1536 */ 1026 Token previous;
1537 final Token endToken; 1027
1538 1028 /**
1539 /** 1029 * The next token in the token stream.
1540 * The tag nodes contained in the receiver (not `null`, contains no `null`s). 1030 */
1541 */ 1031 Token _next;
1542 List<XmlTagNode> _tagNodes; 1032
1543 1033 /**
1544 /** 1034 * The type of the token.
1545 * Construct a new instance representing the content of an HTML file. 1035 */
1546 * 1036 final TokenType type;
1547 * @param beginToken the first token in the file (not `null`) 1037
1548 * @param tagNodes child tag nodes of the receiver (not `null`, contains no `n ull`s) 1038 /**
1549 * @param endToken the last token in the token stream which should be of type 1039 * The lexeme represented by this token.
1550 * [TokenType.EOF] 1040 */
1551 */ 1041 String _value;
1552 HtmlUnit(this.beginToken, List<XmlTagNode> tagNodes, this.endToken) { 1042
1553 this._tagNodes = becomeParentOfAll(tagNodes); 1043 /**
1044 * Initialize a newly created token.
1045 *
1046 * @param type the token type (not `null`)
1047 * @param offset the offset from the beginning of the file to the first charac ter in the token
1048 */
1049 Token.con1(TokenType type, int offset) : this.con2(type, offset, type.lexeme);
1050
1051 /**
1052 * Initialize a newly created token.
1053 *
1054 * @param type the token type (not `null`)
1055 * @param offset the offset from the beginning of the file to the first charac ter in the token
1056 * @param value the lexeme represented by this token (not `null`)
1057 */
1058 Token.con2(this.type, this.offset, String value) {
1059 this._value = StringUtilities.intern(value);
1060 }
1061
1062 /**
1063 * Return the offset from the beginning of the file to the character after las t character of the
1064 * token.
1065 *
1066 * @return the offset from the beginning of the file to the first character af ter last character
1067 * of the token
1068 */
1069 int get end => offset + length;
1070
1071 /**
1072 * Return the number of characters in the node's source range.
1073 *
1074 * @return the number of characters in the node's source range
1075 */
1076 int get length => lexeme.length;
1077
1078 /**
1079 * Return the lexeme that represents this token.
1080 *
1081 * @return the lexeme (not `null`)
1082 */
1083 String get lexeme => _value;
1084
1085 /**
1086 * Return the next token in the token stream.
1087 *
1088 * @return the next token in the token stream
1089 */
1090 Token get next => _next;
1091
1092 /**
1093 * Return `true` if this token is a synthetic token. A synthetic token is a to ken that was
1094 * introduced by the parser in order to recover from an error in the code. Syn thetic tokens always
1095 * have a length of zero (`0`).
1096 *
1097 * @return `true` if this token is a synthetic token
1098 */
1099 bool get isSynthetic => length == 0;
1100
1101 /**
1102 * Set the next token in the token stream to the given token. This has the sid e-effect of setting
1103 * this token to be the previous token for the given token.
1104 *
1105 * @param token the next token in the token stream
1106 * @return the token that was passed in
1107 */
1108 Token setNext(Token token) {
1109 _next = token;
1110 token.previous = this;
1111 return token;
1554 } 1112 }
1555 1113
1556 @override 1114 @override
1557 accept(XmlVisitor visitor) => visitor.visitHtmlUnit(this); 1115 String toString() => lexeme;
1558
1559 /**
1560 * Return the element associated with this HTML unit.
1561 *
1562 * @return the element or `null` if the receiver is not resolved
1563 */
1564 @override
1565 HtmlElement get element => super.element as HtmlElement;
1566
1567 /**
1568 * Answer the tag nodes contained in the receiver. Callers should not manipula te the returned list
1569 * to edit the AST structure.
1570 *
1571 * @return the children (not `null`, contains no `null`s)
1572 */
1573 List<XmlTagNode> get tagNodes => _tagNodes;
1574
1575 @override
1576 void set element(Element element) {
1577 if (element != null && element is! HtmlElement) {
1578 throw new IllegalArgumentException("HtmlElement expected, but ${element.ru ntimeType} given");
1579 }
1580 super.element = element;
1581 }
1582
1583 @override
1584 void visitChildren(XmlVisitor visitor) {
1585 for (XmlTagNode node in _tagNodes) {
1586 node.accept(visitor);
1587 }
1588 }
1589 }
1590
1591 /**
1592 * Instances of the class `StringScanner` implement a scanner that reads from a string. The
1593 * scanning logic is in the superclass.
1594 */
1595 class StringScanner extends AbstractScanner {
1596 /**
1597 * The string from which characters will be read.
1598 */
1599 final String _string;
1600
1601 /**
1602 * The number of characters in the string.
1603 */
1604 int _stringLength = 0;
1605
1606 /**
1607 * The index, relative to the string, of the last character that was read.
1608 */
1609 int _charOffset = 0;
1610
1611 /**
1612 * Initialize a newly created scanner to scan the characters in the given stri ng.
1613 *
1614 * @param source the source being scanned
1615 * @param string the string from which characters will be read
1616 */
1617 StringScanner(Source source, this._string) : super(source) {
1618 this._stringLength = _string.length;
1619 this._charOffset = -1;
1620 }
1621
1622 @override
1623 int get offset => _charOffset;
1624
1625 void set offset(int offset) {
1626 _charOffset = offset;
1627 }
1628
1629 @override
1630 int advance() {
1631 if (++_charOffset < _stringLength) {
1632 return _string.codeUnitAt(_charOffset);
1633 }
1634 _charOffset = _stringLength;
1635 return -1;
1636 }
1637
1638 @override
1639 String getString(int start, int endDelta) => _string.substring(start, _charOff set + 1 + endDelta).toString();
1640
1641 @override
1642 int peek() {
1643 if (_charOffset + 1 < _stringLength) {
1644 return _string.codeUnitAt(_charOffset + 1);
1645 }
1646 return -1;
1647 }
1648 }
1649
1650 /**
1651 * Instances of the class `HtmlParser` are used to parse tokens into a AST struc ture comprised
1652 * of [XmlNode]s.
1653 */
1654 class HtmlParser extends XmlParser {
1655 /**
1656 * The line information associated with the source being parsed.
1657 */
1658 LineInfo _lineInfo;
1659
1660 /**
1661 * The error listener to which errors will be reported.
1662 */
1663 final AnalysisErrorListener _errorListener;
1664
1665 static String _APPLICATION_DART_IN_DOUBLE_QUOTES = "\"application/dart\"";
1666
1667 static String _APPLICATION_DART_IN_SINGLE_QUOTES = "'application/dart'";
1668
1669 static String _SCRIPT = "script";
1670
1671 static String _TYPE = "type";
1672
1673 /**
1674 * A set containing the names of tags that do not have a closing tag.
1675 */
1676 static Set<String> SELF_CLOSING = new Set<String>.from(JavaArrays.asList(<Stri ng> [
1677 "area",
1678 "base",
1679 "basefont",
1680 "br",
1681 "col",
1682 "frame",
1683 "hr",
1684 "img",
1685 "input",
1686 "link",
1687 "meta",
1688 "param",
1689 "!"]));
1690
1691 /**
1692 * Given the contents of an embedded expression that occurs at the given offse t, parse it as a
1693 * Dart expression. The contents should not include the expression's delimiter s.
1694 *
1695 * @param source the source that contains that given token
1696 * @param token the token to start parsing from
1697 * @return the Dart expression that was parsed
1698 */
1699 static Expression parseEmbeddedExpression(Source source, sc.Token token, Analy sisErrorListener errorListener) {
1700 Parser parser = new Parser(source, errorListener);
1701 return parser.parseExpression(token);
1702 }
1703
1704 /**
1705 * Given the contents of an embedded expression that occurs at the given offse t, scans it as a
1706 * Dart code.
1707 *
1708 * @param source the source of that contains the given contents
1709 * @param contents the contents to scan
1710 * @param contentOffset the offset of the contents in the larger file
1711 * @return the first Dart token
1712 */
1713 static sc.Token scanDartSource(Source source, LineInfo lineInfo, String conten ts, int contentOffset, AnalysisErrorListener errorListener) {
1714 LineInfo_Location location = lineInfo.getLocation(contentOffset);
1715 sc.Scanner scanner = new sc.Scanner(source, new sc.SubSequenceReader(content s, contentOffset), errorListener);
1716 scanner.setSourceStart(location.lineNumber, location.columnNumber);
1717 return scanner.tokenize();
1718 }
1719
1720 /**
1721 * Construct a parser for the specified source.
1722 *
1723 * @param source the source being parsed
1724 * @param errorListener the error listener to which errors will be reported
1725 */
1726 HtmlParser(Source source, this._errorListener) : super(source);
1727
1728 /**
1729 * Parse the given tokens.
1730 *
1731 * @param token the first token in the stream of tokens to be parsed
1732 * @param lineInfo the line information created by the scanner
1733 * @return the parse result (not `null`)
1734 */
1735 HtmlUnit parse(Token token, LineInfo lineInfo) {
1736 this._lineInfo = lineInfo;
1737 List<XmlTagNode> tagNodes = parseTopTagNodes(token);
1738 return new HtmlUnit(token, tagNodes, currentToken);
1739 }
1740
1741 @override
1742 XmlAttributeNode createAttributeNode(Token name, Token equals, Token value) => new XmlAttributeNode(name, equals, value);
1743
1744 @override
1745 XmlTagNode createTagNode(Token nodeStart, Token tag, List<XmlAttributeNode> at tributes, Token attributeEnd, List<XmlTagNode> tagNodes, Token contentEnd, Token closingTag, Token nodeEnd) {
1746 if (_isScriptNode(tag, attributes, tagNodes)) {
1747 HtmlScriptTagNode tagNode = new HtmlScriptTagNode(nodeStart, tag, attribut es, attributeEnd, tagNodes, contentEnd, closingTag, nodeEnd);
1748 String contents = tagNode.content;
1749 int contentOffset = attributeEnd.end;
1750 LineInfo_Location location = _lineInfo.getLocation(contentOffset);
1751 sc.Scanner scanner = new sc.Scanner(source, new sc.SubSequenceReader(conte nts, contentOffset), _errorListener);
1752 scanner.setSourceStart(location.lineNumber, location.columnNumber);
1753 sc.Token firstToken = scanner.tokenize();
1754 Parser parser = new Parser(source, _errorListener);
1755 CompilationUnit unit = parser.parseCompilationUnit(firstToken);
1756 unit.lineInfo = _lineInfo;
1757 tagNode.script = unit;
1758 return tagNode;
1759 }
1760 return new XmlTagNode(nodeStart, tag, attributes, attributeEnd, tagNodes, co ntentEnd, closingTag, nodeEnd);
1761 }
1762
1763 @override
1764 bool isSelfClosing(Token tag) => SELF_CLOSING.contains(tag.lexeme);
1765
1766 /**
1767 * Determine if the specified node is a Dart script.
1768 *
1769 * @param node the node to be tested (not `null`)
1770 * @return `true` if the node is a Dart script
1771 */
1772 bool _isScriptNode(Token tag, List<XmlAttributeNode> attributes, List<XmlTagNo de> tagNodes) {
1773 if (tagNodes.length != 0 || tag.lexeme != _SCRIPT) {
1774 return false;
1775 }
1776 for (XmlAttributeNode attribute in attributes) {
1777 if (attribute.name == _TYPE) {
1778 Token valueToken = attribute.valueToken;
1779 if (valueToken != null) {
1780 String value = valueToken.lexeme;
1781 if (value == _APPLICATION_DART_IN_DOUBLE_QUOTES || value == _APPLICATI ON_DART_IN_SINGLE_QUOTES) {
1782 return true;
1783 }
1784 }
1785 }
1786 }
1787 return false;
1788 }
1789 }
1790
1791 /**
1792 * Instances of the class `RecursiveXmlVisitor` implement an XML visitor that wi ll recursively
1793 * visit all of the nodes in an XML structure. For example, using an instance of this class to visit
1794 * a [XmlTagNode] will also cause all of the contained [XmlAttributeNode]s and
1795 * [XmlTagNode]s to be visited.
1796 *
1797 * Subclasses that override a visit method must either invoke the overridden vis it method or must
1798 * explicitly ask the visited node to visit its children. Failure to do so will cause the children
1799 * of the visited node to not be visited.
1800 */
1801 class RecursiveXmlVisitor<R> implements XmlVisitor<R> {
1802 @override
1803 R visitHtmlScriptTagNode(HtmlScriptTagNode node) {
1804 node.visitChildren(this);
1805 return null;
1806 }
1807
1808 @override
1809 R visitHtmlUnit(HtmlUnit node) {
1810 node.visitChildren(this);
1811 return null;
1812 }
1813
1814 @override
1815 R visitXmlAttributeNode(XmlAttributeNode node) {
1816 node.visitChildren(this);
1817 return null;
1818 }
1819
1820 @override
1821 R visitXmlTagNode(XmlTagNode node) {
1822 node.visitChildren(this);
1823 return null;
1824 }
1825 }
1826
1827 /**
1828 * Instances of the class `HtmlScriptTagNode` represent a script tag within an H TML file that
1829 * references a Dart script.
1830 */
1831 class HtmlScriptTagNode extends XmlTagNode {
1832 /**
1833 * The AST structure representing the Dart code within this tag.
1834 */
1835 CompilationUnit _script;
1836
1837 /**
1838 * The element representing this script.
1839 */
1840 HtmlScriptElement scriptElement;
1841
1842 /**
1843 * Initialize a newly created node to represent a script tag within an HTML fi le that references a
1844 * Dart script.
1845 *
1846 * @param nodeStart the token marking the beginning of the tag
1847 * @param tag the name of the tag
1848 * @param attributes the attributes in the tag
1849 * @param attributeEnd the token terminating the region where attributes can b e
1850 * @param tagNodes the children of the tag
1851 * @param contentEnd the token that starts the closing tag
1852 * @param closingTag the name of the tag that occurs in the closing tag
1853 * @param nodeEnd the last token in the tag
1854 */
1855 HtmlScriptTagNode(Token nodeStart, Token tag, List<XmlAttributeNode> attribute s, Token attributeEnd, List<XmlTagNode> tagNodes, Token contentEnd, Token closin gTag, Token nodeEnd) : super(nodeStart, tag, attributes, attributeEnd, tagNodes, contentEnd, closingTag, nodeEnd);
1856
1857 @override
1858 accept(XmlVisitor visitor) => visitor.visitHtmlScriptTagNode(this);
1859
1860 /**
1861 * Return the AST structure representing the Dart code within this tag, or `nu ll` if this
1862 * tag references an external script.
1863 *
1864 * @return the AST structure representing the Dart code within this tag
1865 */
1866 CompilationUnit get script => _script;
1867
1868 /**
1869 * Set the AST structure representing the Dart code within this tag to the giv en compilation unit.
1870 *
1871 * @param unit the AST structure representing the Dart code within this tag
1872 */
1873 void set script(CompilationUnit unit) {
1874 _script = unit;
1875 }
1876 } 1116 }
1877 1117
1878 /** 1118 /**
1879 * The enumeration `TokenType` defines the types of tokens that can be returned by the 1119 * The enumeration `TokenType` defines the types of tokens that can be returned by the
1880 * scanner. 1120 * scanner.
1881 */ 1121 */
1882 class TokenType extends Enum<TokenType> { 1122 class TokenType extends Enum<TokenType> {
1883 /** 1123 /**
1884 * The type of the token that marks the end of the input. 1124 * The type of the token that marks the end of the input.
1885 */ 1125 */
(...skipping 147 matching lines...) Expand 10 before | Expand all | Expand 10 after
2033 * Answer the attribute value token. A properly formed value will start and en d with matching 1273 * Answer the attribute value token. A properly formed value will start and en d with matching
2034 * quote characters, but the value returned may not be properly formed. 1274 * quote characters, but the value returned may not be properly formed.
2035 * 1275 *
2036 * @return the value token or `null` if this represents a badly formed attribu te 1276 * @return the value token or `null` if this represents a badly formed attribu te
2037 */ 1277 */
2038 Token get valueToken => _value; 1278 Token get valueToken => _value;
2039 1279
2040 @override 1280 @override
2041 void visitChildren(XmlVisitor visitor) { 1281 void visitChildren(XmlVisitor visitor) {
2042 } 1282 }
1283 }
1284
1285 /**
1286 * Instances of the class `XmlExpression` represent an abstract expression embed ded into
1287 * [XmlNode].
1288 */
1289 abstract class XmlExpression {
1290 /**
1291 * An empty array of expressions.
1292 */
1293 static List<XmlExpression> EMPTY_ARRAY = new List<XmlExpression>(0);
1294
1295 /**
1296 * Check if the given offset belongs to the expression's source range.
1297 */
1298 bool contains(int offset) => this.offset <= offset && offset < end;
1299
1300 /**
1301 * Return the offset of the character immediately following the last character of this
1302 * expression's source range. This is equivalent to `getOffset() + getLength() `.
1303 *
1304 * @return the offset of the character just past the expression's source range
1305 */
1306 int get end;
1307
1308 /**
1309 * Return the number of characters in the expression's source range.
1310 */
1311 int get length;
1312
1313 /**
1314 * Return the offset of the first character in the expression's source range.
1315 */
1316 int get offset;
1317
1318 /**
1319 * Return the [Reference] at the given offset.
1320 *
1321 * @param offset the offset from the beginning of the file
1322 * @return the [Reference] at the given offset, maybe `null`
1323 */
1324 XmlExpression_Reference getReference(int offset);
1325 }
1326
1327 /**
1328 * The reference to the [Element].
1329 */
1330 class XmlExpression_Reference {
1331 Element element;
1332
1333 int offset = 0;
1334
1335 int length = 0;
1336
1337 XmlExpression_Reference(Element element, int offset, int length) {
1338 this.element = element;
1339 this.offset = offset;
1340 this.length = length;
1341 }
1342 }
1343
1344 /**
1345 * The abstract class `XmlNode` defines behavior common to all XML/HTML nodes.
1346 */
1347 abstract class XmlNode {
1348 /**
1349 * The parent of the node, or `null` if the node is the root of an AST structu re.
1350 */
1351 XmlNode _parent;
1352
1353 /**
1354 * The element associated with this node or `null` if the receiver is not reso lved.
1355 */
1356 Element _element;
1357
1358 /**
1359 * Use the given visitor to visit this node.
1360 *
1361 * @param visitor the visitor that will visit this node
1362 * @return the value returned by the visitor as a result of visiting this node
1363 */
1364 accept(XmlVisitor visitor);
1365
1366 /**
1367 * Return the first token included in this node's source range.
1368 *
1369 * @return the first token or `null` if none
1370 */
1371 Token get beginToken;
1372
1373 /**
1374 * Return the element associated with this node.
1375 *
1376 * @return the element or `null` if the receiver is not resolved
1377 */
1378 Element get element => _element;
1379
1380 /**
1381 * Return the offset of the character immediately following the last character of this node's
1382 * source range. This is equivalent to `node.getOffset() + node.getLength()`. For an html
1383 * unit this will be equal to the length of the unit's source.
1384 *
1385 * @return the offset of the character just past the node's source range
1386 */
1387 int get end => offset + length;
1388
1389 /**
1390 * Return the last token included in this node's source range.
1391 *
1392 * @return the last token or `null` if none
1393 */
1394 Token get endToken;
1395
1396 /**
1397 * Return the number of characters in the node's source range.
1398 *
1399 * @return the number of characters in the node's source range
1400 */
1401 int get length {
1402 Token beginToken = this.beginToken;
1403 Token endToken = this.endToken;
1404 if (beginToken == null || endToken == null) {
1405 return -1;
1406 }
1407 return endToken.offset + endToken.length - beginToken.offset;
1408 }
1409
1410 /**
1411 * Return the offset from the beginning of the file to the first character in the node's source
1412 * range.
1413 *
1414 * @return the offset from the beginning of the file to the first character in the node's source
1415 * range
1416 */
1417 int get offset {
1418 Token beginToken = this.beginToken;
1419 if (beginToken == null) {
1420 return -1;
1421 }
1422 return this.beginToken.offset;
1423 }
1424
1425 /**
1426 * Return this node's parent node, or `null` if this node is the root of an AS T structure.
1427 *
1428 * Note that the relationship between an AST node and its parent node may chan ge over the lifetime
1429 * of a node.
1430 *
1431 * @return the parent of this node, or `null` if none
1432 */
1433 XmlNode get parent => _parent;
1434
1435 /**
1436 * Set the element associated with this node.
1437 *
1438 * @param element the element
1439 */
1440 void set element(Element element) {
1441 this._element = element;
1442 }
1443
1444 @override
1445 String toString() {
1446 PrintStringWriter writer = new PrintStringWriter();
1447 accept(new ToSourceVisitor(writer));
1448 return writer.toString();
1449 }
1450
1451 /**
1452 * Use the given visitor to visit all of the children of this node. The childr en will be visited
1453 * in source order.
1454 *
1455 * @param visitor the visitor that will be used to visit the children of this node
1456 */
1457 void visitChildren(XmlVisitor visitor);
1458
1459 /**
1460 * Make this node the parent of the given child node.
1461 *
1462 * @param child the node that will become a child of this node
1463 * @return the node that was made a child of this node
1464 */
1465 XmlNode becomeParentOf(XmlNode child) {
1466 if (child != null) {
1467 XmlNode node = child;
1468 node.parent = this;
1469 }
1470 return child;
1471 }
1472
1473 /**
1474 * Make this node the parent of the given child nodes.
1475 *
1476 * @param children the nodes that will become the children of this node
1477 * @param ifEmpty the (empty) nodes to return if "children" is empty
1478 * @return the nodes that were made children of this node
1479 */
1480 List becomeParentOfAll(List children, {List ifEmpty}) {
1481 if (children == null || children.isEmpty) {
1482 if (ifEmpty != null) {
1483 return ifEmpty;
1484 }
1485 }
1486 if (children != null) {
1487 for (JavaIterator iter = new JavaIterator(children); iter.hasNext;) {
1488 XmlNode node = iter.next();
1489 node.parent = this;
1490 }
1491 // This will create ArrayList for exactly given number of elements.
1492 return new List.from(children);
1493 }
1494 return children;
1495 }
1496
1497 /**
1498 * This method exists for debugging purposes only.
1499 */
1500 void _appendIdentifier(JavaStringBuilder builder, XmlNode node) {
1501 if (node is XmlTagNode) {
1502 builder.append(node.tag);
1503 } else if (node is XmlAttributeNode) {
1504 builder.append(node.name);
1505 } else {
1506 builder.append("htmlUnit");
1507 }
1508 }
1509
1510 /**
1511 * This method exists for debugging purposes only.
1512 */
1513 String _buildRecursiveStructureMessage(XmlNode newParent) {
1514 JavaStringBuilder builder = new JavaStringBuilder();
1515 builder.append("Attempt to create recursive structure: ");
1516 XmlNode current = newParent;
1517 while (current != null) {
1518 if (!identical(current, newParent)) {
1519 builder.append(" -> ");
1520 }
1521 if (identical(current, this)) {
1522 builder.appendChar(0x2A);
1523 _appendIdentifier(builder, current);
1524 builder.appendChar(0x2A);
1525 } else {
1526 _appendIdentifier(builder, current);
1527 }
1528 current = current.parent;
1529 }
1530 return builder.toString();
1531 }
1532
1533 /**
1534 * Set the parent of this node to the given node.
1535 *
1536 * @param newParent the node that is to be made the parent of this node
1537 */
1538 void set parent(XmlNode newParent) {
1539 XmlNode current = newParent;
1540 while (current != null) {
1541 if (identical(current, this)) {
1542 AnalysisEngine.instance.logger.logError2("Circular structure while setti ng an XML node's parent", new IllegalArgumentException(_buildRecursiveStructureM essage(newParent)));
1543 return;
1544 }
1545 current = current.parent;
1546 }
1547 _parent = newParent;
1548 }
1549 }
1550
1551 /**
1552 * Instances of the class `XmlParser` are used to parse tokens into a AST struct ure comprised
1553 * of [XmlNode]s.
1554 */
1555 class XmlParser {
1556 /**
1557 * The source being parsed.
1558 */
1559 final Source source;
1560
1561 /**
1562 * The next token to be parsed.
1563 */
1564 Token _currentToken;
1565
1566 /**
1567 * Construct a parser for the specified source.
1568 *
1569 * @param source the source being parsed
1570 */
1571 XmlParser(this.source);
1572
1573 /**
1574 * Create a node representing an attribute.
1575 *
1576 * @param name the name of the attribute
1577 * @param equals the equals sign, or `null` if there is no value
1578 * @param value the value of the attribute
1579 * @return the node that was created
1580 */
1581 XmlAttributeNode createAttributeNode(Token name, Token equals, Token value) => new XmlAttributeNode(name, equals, value);
1582
1583 /**
1584 * Create a node representing a tag.
1585 *
1586 * @param nodeStart the token marking the beginning of the tag
1587 * @param tag the name of the tag
1588 * @param attributes the attributes in the tag
1589 * @param attributeEnd the token terminating the region where attributes can b e
1590 * @param tagNodes the children of the tag
1591 * @param contentEnd the token that starts the closing tag
1592 * @param closingTag the name of the tag that occurs in the closing tag
1593 * @param nodeEnd the last token in the tag
1594 * @return the node that was created
1595 */
1596 XmlTagNode createTagNode(Token nodeStart, Token tag, List<XmlAttributeNode> at tributes, Token attributeEnd, List<XmlTagNode> tagNodes, Token contentEnd, Token closingTag, Token nodeEnd) => new XmlTagNode(nodeStart, tag, attributes, attrib uteEnd, tagNodes, contentEnd, closingTag, nodeEnd);
1597
1598 /**
1599 * Answer `true` if the specified tag is self closing and thus should never ha ve content or
1600 * child tag nodes.
1601 *
1602 * @param tag the tag (not `null`)
1603 * @return `true` if self closing
1604 */
1605 bool isSelfClosing(Token tag) => false;
1606
1607 /**
1608 * Parse the entire token stream and in the process, advance the current token to the end of the
1609 * token stream.
1610 *
1611 * @return the list of tag nodes found (not `null`, contains no `null`)
1612 */
1613 List<XmlTagNode> parseTopTagNodes(Token firstToken) {
1614 _currentToken = firstToken;
1615 List<XmlTagNode> tagNodes = new List<XmlTagNode>();
1616 TokenType type = _currentToken.type;
1617 while (type != TokenType.EOF) {
1618 if (type == TokenType.LT) {
1619 tagNodes.add(_parseTagNode());
1620 } else if (type == TokenType.DECLARATION || type == TokenType.DIRECTIVE || type == TokenType.COMMENT) {
1621 // ignored tokens
1622 _currentToken = _currentToken.next;
1623 } else {
1624 _reportUnexpectedToken();
1625 _currentToken = _currentToken.next;
1626 }
1627 type = _currentToken.type;
1628 }
1629 return tagNodes;
1630 }
1631
1632 /**
1633 * Answer the current token.
1634 *
1635 * @return the current token
1636 */
1637 Token get currentToken => _currentToken;
1638
1639 /**
1640 * Insert a synthetic token of the specified type before the current token
1641 *
1642 * @param type the type of token to be inserted (not `null`)
1643 * @return the synthetic token that was inserted (not `null`)
1644 */
1645 Token _insertSyntheticToken(TokenType type) {
1646 Token token = new Token.con2(type, _currentToken.offset, "");
1647 _currentToken.previous.setNext(token);
1648 token.setNext(_currentToken);
1649 return token;
1650 }
1651
1652 /**
1653 * Parse the token stream for an attribute. This method advances the current t oken over the
1654 * attribute, but should not be called if the [currentToken] is not [TokenType #TAG].
1655 *
1656 * @return the attribute (not `null`)
1657 */
1658 XmlAttributeNode _parseAttribute() {
1659 // Assume the current token is a tag
1660 Token name = _currentToken;
1661 _currentToken = _currentToken.next;
1662 // Equals sign
1663 Token equals;
1664 if (_currentToken.type == TokenType.EQ) {
1665 equals = _currentToken;
1666 _currentToken = _currentToken.next;
1667 } else {
1668 _reportUnexpectedToken();
1669 equals = _insertSyntheticToken(TokenType.EQ);
1670 }
1671 // String value
1672 Token value;
1673 if (_currentToken.type == TokenType.STRING) {
1674 value = _currentToken;
1675 _currentToken = _currentToken.next;
1676 } else {
1677 _reportUnexpectedToken();
1678 value = _insertSyntheticToken(TokenType.STRING);
1679 }
1680 return createAttributeNode(name, equals, value);
1681 }
1682
1683 /**
1684 * Parse the stream for a sequence of attributes. This method advances the cur rent token to the
1685 * next [TokenType#GT], [TokenType#SLASH_GT], or [TokenType#EOF].
1686 *
1687 * @return a collection of zero or more attributes (not `null`, contains no `n ull`s)
1688 */
1689 List<XmlAttributeNode> _parseAttributes() {
1690 TokenType type = _currentToken.type;
1691 if (type == TokenType.GT || type == TokenType.SLASH_GT || type == TokenType. EOF) {
1692 return XmlTagNode.NO_ATTRIBUTES;
1693 }
1694 List<XmlAttributeNode> attributes = new List<XmlAttributeNode>();
1695 while (type != TokenType.GT && type != TokenType.SLASH_GT && type != TokenTy pe.EOF) {
1696 if (type == TokenType.TAG) {
1697 attributes.add(_parseAttribute());
1698 } else {
1699 _reportUnexpectedToken();
1700 _currentToken = _currentToken.next;
1701 }
1702 type = _currentToken.type;
1703 }
1704 return attributes;
1705 }
1706
1707 /**
1708 * Parse the stream for a sequence of tag nodes existing within a parent tag n ode. This method
1709 * advances the current token to the next [TokenType#LT_SLASH] or [TokenType#E OF].
1710 *
1711 * @return a list of nodes (not `null`, contains no `null`s)
1712 */
1713 List<XmlTagNode> _parseChildTagNodes() {
1714 TokenType type = _currentToken.type;
1715 if (type == TokenType.LT_SLASH || type == TokenType.EOF) {
1716 return XmlTagNode.NO_TAG_NODES;
1717 }
1718 List<XmlTagNode> nodes = new List<XmlTagNode>();
1719 while (type != TokenType.LT_SLASH && type != TokenType.EOF) {
1720 if (type == TokenType.LT) {
1721 nodes.add(_parseTagNode());
1722 } else if (type == TokenType.COMMENT) {
1723 // ignored token
1724 _currentToken = _currentToken.next;
1725 } else {
1726 _reportUnexpectedToken();
1727 _currentToken = _currentToken.next;
1728 }
1729 type = _currentToken.type;
1730 }
1731 return nodes;
1732 }
1733
1734 /**
1735 * Parse the token stream for the next tag node. This method advances current token over the
1736 * parsed tag node, but should only be called if the current token is [TokenTy pe#LT]
1737 *
1738 * @return the tag node or `null` if none found
1739 */
1740 XmlTagNode _parseTagNode() {
1741 // Assume that the current node is a tag node start TokenType#LT
1742 Token nodeStart = _currentToken;
1743 _currentToken = _currentToken.next;
1744 // Get the tag or create a synthetic tag and report an error
1745 Token tag;
1746 if (_currentToken.type == TokenType.TAG) {
1747 tag = _currentToken;
1748 _currentToken = _currentToken.next;
1749 } else {
1750 _reportUnexpectedToken();
1751 tag = _insertSyntheticToken(TokenType.TAG);
1752 }
1753 // Parse the attributes
1754 List<XmlAttributeNode> attributes = _parseAttributes();
1755 // Token ending attribute list
1756 Token attributeEnd;
1757 if (_currentToken.type == TokenType.GT || _currentToken.type == TokenType.SL ASH_GT) {
1758 attributeEnd = _currentToken;
1759 _currentToken = _currentToken.next;
1760 } else {
1761 _reportUnexpectedToken();
1762 attributeEnd = _insertSyntheticToken(TokenType.SLASH_GT);
1763 }
1764 // If the node has no children, then return the node
1765 if (attributeEnd.type == TokenType.SLASH_GT || isSelfClosing(tag)) {
1766 return createTagNode(nodeStart, tag, attributes, attributeEnd, XmlTagNode. NO_TAG_NODES, _currentToken, null, attributeEnd);
1767 }
1768 // Parse the child tag nodes
1769 List<XmlTagNode> tagNodes = _parseChildTagNodes();
1770 // Token ending child tag nodes
1771 Token contentEnd;
1772 if (_currentToken.type == TokenType.LT_SLASH) {
1773 contentEnd = _currentToken;
1774 _currentToken = _currentToken.next;
1775 } else {
1776 // TODO (danrubel): handle self closing HTML elements by inserting synthet ic tokens
1777 // but not reporting an error
1778 _reportUnexpectedToken();
1779 contentEnd = _insertSyntheticToken(TokenType.LT_SLASH);
1780 }
1781 // Closing tag
1782 Token closingTag;
1783 if (_currentToken.type == TokenType.TAG) {
1784 closingTag = _currentToken;
1785 _currentToken = _currentToken.next;
1786 } else {
1787 _reportUnexpectedToken();
1788 closingTag = _insertSyntheticToken(TokenType.TAG);
1789 }
1790 // Token ending node
1791 Token nodeEnd;
1792 if (_currentToken.type == TokenType.GT) {
1793 nodeEnd = _currentToken;
1794 _currentToken = _currentToken.next;
1795 } else {
1796 _reportUnexpectedToken();
1797 nodeEnd = _insertSyntheticToken(TokenType.GT);
1798 }
1799 return createTagNode(nodeStart, tag, attributes, attributeEnd, tagNodes, con tentEnd, closingTag, nodeEnd);
1800 }
1801
1802 /**
1803 * Report the current token as unexpected
1804 */
1805 void _reportUnexpectedToken() {
1806 }
1807 }
1808
1809 /**
1810 * Instances of `XmlTagNode` represent XML or HTML elements such as `` and
1811 * `<body foo="bar"> ... </body>`.
1812 */
1813 class XmlTagNode extends XmlNode {
1814 /**
1815 * Constant representing empty list of attributes.
1816 */
1817 static List<XmlAttributeNode> NO_ATTRIBUTES = new UnmodifiableListView(new Lis t<XmlAttributeNode>());
1818
1819 /**
1820 * Constant representing empty list of tag nodes.
1821 */
1822 static List<XmlTagNode> NO_TAG_NODES = new UnmodifiableListView(new List<XmlTa gNode>());
1823
1824 /**
1825 * The starting [TokenType#LT] token (not `null`).
1826 */
1827 final Token nodeStart;
1828
1829 /**
1830 * The [TokenType#TAG] token after the starting '&lt;' (not `null`).
1831 */
1832 final Token _tag;
1833
1834 /**
1835 * The attributes contained by the receiver (not `null`, contains no `null`s).
1836 */
1837 List<XmlAttributeNode> _attributes;
1838
1839 /**
1840 * The [TokenType#GT] or [TokenType#SLASH_GT] token after the attributes (not
1841 * `null`). The token may be the same token as [nodeEnd] if there are no child
1842 * [tagNodes].
1843 */
1844 final Token attributeEnd;
1845
1846 /**
1847 * The tag nodes contained in the receiver (not `null`, contains no `null`s).
1848 */
1849 List<XmlTagNode> _tagNodes;
1850
1851 /**
1852 * The token (not `null`) after the content, which may be
1853 * * (1) [TokenType#LT_SLASH] for nodes with open and close tags, or
1854 * * (2) the [TokenType#LT] nodeStart of the next sibling node if this node is self
1855 * closing or the attributeEnd is [TokenType#SLASH_GT], or
1856 * * (3) [TokenType#EOF] if the node does not have a closing tag and is the la st node in
1857 * the stream [TokenType#LT_SLASH] token after the content, or `null` if there is no
1858 * content and the attributes ended with [TokenType#SLASH_GT].
1859 */
1860 final Token contentEnd;
1861
1862 /**
1863 * The closing [TokenType#TAG] after the child elements or `null` if there is no
1864 * content and the attributes ended with [TokenType#SLASH_GT]
1865 */
1866 final Token closingTag;
1867
1868 /**
1869 * The ending [TokenType#GT] or [TokenType#SLASH_GT] token (not `null`).
1870 */
1871 final Token nodeEnd;
1872
1873 /**
1874 * The expressions that are embedded in the tag's content.
1875 */
1876 List<XmlExpression> expressions = XmlExpression.EMPTY_ARRAY;
1877
1878 /**
1879 * Construct a new instance representing an XML or HTML element
1880 *
1881 * @param nodeStart the starting [TokenType#LT] token (not `null`)
1882 * @param tag the [TokenType#TAG] token after the starting '&lt;' (not `null`) .
1883 * @param attributes the attributes associated with this element or [NO_ATTRIB UTES] (not
1884 * `null`, contains no `null`s)
1885 * @param attributeEnd The [TokenType#GT] or [TokenType#SLASH_GT] token after the
1886 * attributes (not `null`). The token may be the same token as [nodeE nd] if
1887 * there are no child [tagNodes].
1888 * @param tagNodes child tag nodes of the receiver or [NO_TAG_NODES] (not `nul l`,
1889 * contains no `null`s)
1890 * @param contentEnd the token (not `null`) after the content, which may be
1891 * * (1) [TokenType#LT_SLASH] for nodes with open and close tags, or
1892 * * (2) the [TokenType#LT] nodeStart of the next sibling node if thi s node is
1893 * self closing or the attributeEnd is [TokenType#SLASH_GT], or
1894 * * (3) [TokenType#EOF] if the node does not have a closing tag and is the last
1895 * node in the stream [TokenType#LT_SLASH] token after the content, o r `null`
1896 * if there is no content and the attributes ended with [TokenType#SL ASH_GT].
1897 * @param closingTag the closing [TokenType#TAG] after the child elements or ` null` if
1898 * there is no content and the attributes ended with [TokenType#SLASH _GT]
1899 * @param nodeEnd the ending [TokenType#GT] or [TokenType#SLASH_GT] token (not
1900 * `null`)
1901 */
1902 XmlTagNode(this.nodeStart, this._tag, List<XmlAttributeNode> attributes, this. attributeEnd, List<XmlTagNode> tagNodes, this.contentEnd, this.closingTag, this. nodeEnd) {
1903 this._attributes = becomeParentOfAll(attributes, ifEmpty: NO_ATTRIBUTES);
1904 this._tagNodes = becomeParentOfAll(tagNodes, ifEmpty: NO_TAG_NODES);
1905 }
1906
1907 @override
1908 accept(XmlVisitor visitor) => visitor.visitXmlTagNode(this);
1909
1910 /**
1911 * Answer the attribute with the specified name.
1912 *
1913 * @param name the attribute name
1914 * @return the attribute or `null` if no matching attribute is found
1915 */
1916 XmlAttributeNode getAttribute(String name) {
1917 for (XmlAttributeNode attribute in _attributes) {
1918 if (attribute.name == name) {
1919 return attribute;
1920 }
1921 }
1922 return null;
1923 }
1924
1925 /**
1926 * Answer the receiver's attributes. Callers should not manipulate the returne d list to edit the
1927 * AST structure.
1928 *
1929 * @return the attributes (not `null`, contains no `null`s)
1930 */
1931 List<XmlAttributeNode> get attributes => _attributes;
1932
1933 /**
1934 * Find the attribute with the given name (see [getAttribute] and answer the l exeme
1935 * for the attribute's value token without the leading and trailing quotes (se e
1936 * [XmlAttributeNode#getText]).
1937 *
1938 * @param name the attribute name
1939 * @return the attribute text or `null` if no matching attribute is found
1940 */
1941 String getAttributeText(String name) {
1942 XmlAttributeNode attribute = getAttribute(name);
1943 return attribute != null ? attribute.text : null;
1944 }
1945
1946 @override
1947 Token get beginToken => nodeStart;
1948
1949 /**
1950 * Answer a string representing the content contained in the receiver. This in cludes the textual
1951 * representation of any child tag nodes ([getTagNodes]). Whitespace between ' &lt;',
1952 * '&lt;/', and '>', '/>' is discarded, but all other whitespace is preserved.
1953 *
1954 * @return the content (not `null`)
1955 */
1956 String get content {
1957 Token token = attributeEnd.next;
1958 if (identical(token, contentEnd)) {
1959 return "";
1960 }
1961 //TODO (danrubel): handle CDATA and replace HTML character encodings with th e actual characters
1962 String content = token.lexeme;
1963 token = token.next;
1964 if (identical(token, contentEnd)) {
1965 return content;
1966 }
1967 JavaStringBuilder buffer = new JavaStringBuilder();
1968 while (!identical(token, contentEnd)) {
1969 buffer.append(token.lexeme);
1970 token = token.next;
1971 }
1972 return buffer.toString();
1973 }
1974
1975 @override
1976 Token get endToken {
1977 if (nodeEnd != null) {
1978 return nodeEnd;
1979 }
1980 if (closingTag != null) {
1981 return closingTag;
1982 }
1983 if (contentEnd != null) {
1984 return contentEnd;
1985 }
1986 if (!_tagNodes.isEmpty) {
1987 return _tagNodes[_tagNodes.length - 1].endToken;
1988 }
1989 if (attributeEnd != null) {
1990 return attributeEnd;
1991 }
1992 if (!_attributes.isEmpty) {
1993 return _attributes[_attributes.length - 1].endToken;
1994 }
1995 return _tag;
1996 }
1997
1998 /**
1999 * Answer the tag name after the starting '&lt;'.
2000 *
2001 * @return the tag name (not `null`)
2002 */
2003 String get tag => _tag.lexeme;
2004
2005 /**
2006 * Answer the tag nodes contained in the receiver. Callers should not manipula te the returned list
2007 * to edit the AST structure.
2008 *
2009 * @return the children (not `null`, contains no `null`s)
2010 */
2011 List<XmlTagNode> get tagNodes => _tagNodes;
2012
2013 /**
2014 * Answer the [TokenType#TAG] token after the starting '&lt;'.
2015 *
2016 * @return the token (not `null`)
2017 */
2018 Token get tagToken => _tag;
2019
2020 @override
2021 void visitChildren(XmlVisitor visitor) {
2022 for (XmlAttributeNode node in _attributes) {
2023 node.accept(visitor);
2024 }
2025 for (XmlTagNode node in _tagNodes) {
2026 node.accept(visitor);
2027 }
2028 }
2029 }
2030
2031 /**
2032 * The interface `XmlVisitor` defines the behavior of objects that can be used t o visit an
2033 * [XmlNode] structure.
2034 */
2035 abstract class XmlVisitor<R> {
2036 R visitHtmlScriptTagNode(HtmlScriptTagNode node);
2037
2038 R visitHtmlUnit(HtmlUnit htmlUnit);
2039
2040 R visitXmlAttributeNode(XmlAttributeNode xmlAttributeNode);
2041
2042 R visitXmlTagNode(XmlTagNode xmlTagNode);
2043 } 2043 }
OLDNEW
« no previous file with comments | « pkg/analyzer/lib/src/generated/error.dart ('k') | pkg/analyzer/lib/src/generated/index.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698