OLD | NEW |
| (Empty) |
1 // This code was auto-generated, is not intended to be edited, and is subject to | |
2 // significant change. Please see the README file for more information. | |
3 | |
4 library engine.html; | |
5 | |
6 import 'dart:collection'; | |
7 import 'java_core.dart'; | |
8 import 'source.dart'; | |
9 import 'error.dart'; | |
10 import 'instrumentation.dart'; | |
11 import 'element.dart' show HtmlElementImpl; | |
12 | |
13 /** | |
14 * Instances of the class {@code Token} represent a token that was scanned from
the input. Each | |
15 * token knows which token follows it, acting as the head of a linked list of to
kens. | |
16 * @coverage dart.engine.html | |
17 */ | |
18 class Token { | |
19 /** | |
20 * The offset from the beginning of the file to the first character in the tok
en. | |
21 */ | |
22 int _offset = 0; | |
23 /** | |
24 * The previous token in the token stream. | |
25 */ | |
26 Token _previous; | |
27 /** | |
28 * The next token in the token stream. | |
29 */ | |
30 Token _next; | |
31 /** | |
32 * The type of the token. | |
33 */ | |
34 TokenType _type; | |
35 /** | |
36 * The lexeme represented by this token. | |
37 */ | |
38 String _value; | |
39 /** | |
40 * Initialize a newly created token. | |
41 * @param type the token type (not {@code null}) | |
42 * @param offset the offset from the beginning of the file to the first charac
ter in the token | |
43 */ | |
44 Token.con1(TokenType type, int offset) { | |
45 _jtd_constructor_145_impl(type, offset); | |
46 } | |
47 _jtd_constructor_145_impl(TokenType type, int offset) { | |
48 _jtd_constructor_146_impl(type, offset, type.lexeme); | |
49 } | |
50 /** | |
51 * Initialize a newly created token. | |
52 * @param type the token type (not {@code null}) | |
53 * @param offset the offset from the beginning of the file to the first charac
ter in the token | |
54 * @param value the lexeme represented by this token (not {@code null}) | |
55 */ | |
56 Token.con2(TokenType type4, int offset3, String value7) { | |
57 _jtd_constructor_146_impl(type4, offset3, value7); | |
58 } | |
59 _jtd_constructor_146_impl(TokenType type4, int offset3, String value7) { | |
60 this._type = type4; | |
61 this._value = value7; | |
62 this._offset = offset3; | |
63 } | |
64 /** | |
65 * Return the offset from the beginning of the file to the character after las
t character of the | |
66 * token. | |
67 * @return the offset from the beginning of the file to the first character af
ter last character | |
68 * of the token | |
69 */ | |
70 int get end => _offset + length; | |
71 /** | |
72 * Return the number of characters in the node's source range. | |
73 * @return the number of characters in the node's source range | |
74 */ | |
75 int get length => lexeme.length; | |
76 /** | |
77 * Return the lexeme that represents this token. | |
78 * @return the lexeme (not {@code null}) | |
79 */ | |
80 String get lexeme => _value; | |
81 /** | |
82 * Return the next token in the token stream. | |
83 * @return the next token in the token stream | |
84 */ | |
85 Token get next => _next; | |
86 /** | |
87 * Return the offset from the beginning of the file to the first character in
the token. | |
88 * @return the offset from the beginning of the file to the first character in
the token | |
89 */ | |
90 int get offset => _offset; | |
91 /** | |
92 * Return the previous token in the token stream. | |
93 * @return the previous token in the token stream | |
94 */ | |
95 Token get previous => _previous; | |
96 /** | |
97 * Answer the token type for the receiver. | |
98 * @return the token type (not {@code null}) | |
99 */ | |
100 TokenType get type => _type; | |
101 /** | |
102 * Return {@code true} if this token is a synthetic token. A synthetic token i
s a token that was | |
103 * introduced by the parser in order to recover from an error in the code. Syn
thetic tokens always | |
104 * have a length of zero ({@code 0}). | |
105 * @return {@code true} if this token is a synthetic token | |
106 */ | |
107 bool isSynthetic() => length == 0; | |
108 /** | |
109 * Set the next token in the token stream to the given token. This has the sid
e-effect of setting | |
110 * this token to be the previous token for the given token. | |
111 * @param token the next token in the token stream | |
112 * @return the token that was passed in | |
113 */ | |
114 Token setNext(Token token) { | |
115 _next = token; | |
116 token.previous = this; | |
117 return token; | |
118 } | |
119 String toString() => lexeme; | |
120 /** | |
121 * Set the previous token in the token stream to the given token. | |
122 * @param previous the previous token in the token stream | |
123 */ | |
124 void set previous(Token previous2) { | |
125 this._previous = previous2; | |
126 } | |
127 } | |
128 /** | |
129 * Instances of {@code HtmlParseResult} hold the result of parsing an HTML file. | |
130 * @coverage dart.engine.html | |
131 */ | |
132 class HtmlParseResult extends HtmlScanResult { | |
133 /** | |
134 * The unit containing the parsed information (not {@code null}). | |
135 */ | |
136 HtmlUnit _unit; | |
137 HtmlParseResult(Token token, List<int> lineStarts, HtmlUnit unit) : super(toke
n, lineStarts) { | |
138 this._unit = unit; | |
139 } | |
140 /** | |
141 * Answer the unit generated by parsing the source | |
142 * @return the unit (not {@code null}) | |
143 */ | |
144 HtmlUnit get htmlUnit => _unit; | |
145 } | |
146 /** | |
147 * Instances of the class {@code RecursiveXmlVisitor} implement an XML visitor t
hat will recursively | |
148 * visit all of the nodes in an XML structure. For example, using an instance of
this class to visit | |
149 * a {@link XmlTagNode} will also cause all of the contained {@link XmlAttribute
Node}s and{@link XmlTagNode}s to be visited. | |
150 * <p> | |
151 * Subclasses that override a visit method must either invoke the overridden vis
it method or must | |
152 * explicitly ask the visited node to visit its children. Failure to do so will
cause the children | |
153 * of the visited node to not be visited. | |
154 * @coverage dart.engine.html | |
155 */ | |
156 class RecursiveXmlVisitor<R> implements XmlVisitor<R> { | |
157 R visitHtmlUnit(HtmlUnit node) { | |
158 node.visitChildren(this); | |
159 return null; | |
160 } | |
161 R visitXmlAttributeNode(XmlAttributeNode node) { | |
162 node.visitChildren(this); | |
163 return null; | |
164 } | |
165 R visitXmlTagNode(XmlTagNode node) { | |
166 node.visitChildren(this); | |
167 return null; | |
168 } | |
169 } | |
170 /** | |
171 * The abstract class {@code XmlNode} defines behavior common to all XML/HTML no
des. | |
172 * @coverage dart.engine.html | |
173 */ | |
174 abstract class XmlNode { | |
175 /** | |
176 * The parent of the node, or {@code null} if the node is the root of an AST s
tructure. | |
177 */ | |
178 XmlNode _parent; | |
179 /** | |
180 * Use the given visitor to visit this node. | |
181 * @param visitor the visitor that will visit this node | |
182 * @return the value returned by the visitor as a result of visiting this node | |
183 */ | |
184 accept(XmlVisitor visitor); | |
185 /** | |
186 * Return the first token included in this node's source range. | |
187 * @return the first token or {@code null} if none | |
188 */ | |
189 Token get beginToken; | |
190 /** | |
191 * Return the offset of the character immediately following the last character
of this node's | |
192 * source range. This is equivalent to {@code node.getOffset() + node.getLengt
h()}. For an html | |
193 * unit this will be equal to the length of the unit's source. | |
194 * @return the offset of the character just past the node's source range | |
195 */ | |
196 int get end => offset + length; | |
197 /** | |
198 * Return the last token included in this node's source range. | |
199 * @return the last token or {@code null} if none | |
200 */ | |
201 Token get endToken; | |
202 /** | |
203 * Return the number of characters in the node's source range. | |
204 * @return the number of characters in the node's source range | |
205 */ | |
206 int get length { | |
207 Token beginToken4 = beginToken; | |
208 Token endToken4 = endToken; | |
209 if (beginToken4 == null || endToken4 == null) { | |
210 return -1; | |
211 } | |
212 return endToken4.offset + endToken4.length - beginToken4.offset; | |
213 } | |
214 /** | |
215 * Return the offset from the beginning of the file to the first character in
the node's source | |
216 * range. | |
217 * @return the offset from the beginning of the file to the first character in
the node's source | |
218 * range | |
219 */ | |
220 int get offset { | |
221 Token beginToken5 = beginToken; | |
222 if (beginToken5 == null) { | |
223 return -1; | |
224 } | |
225 return beginToken.offset; | |
226 } | |
227 /** | |
228 * Return this node's parent node, or {@code null} if this node is the root of
an AST structure. | |
229 * <p> | |
230 * Note that the relationship between an AST node and its parent node may chan
ge over the lifetime | |
231 * of a node. | |
232 * @return the parent of this node, or {@code null} if none | |
233 */ | |
234 XmlNode get parent => _parent; | |
235 /** | |
236 * Use the given visitor to visit all of the children of this node. The childr
en will be visited | |
237 * in source order. | |
238 * @param visitor the visitor that will be used to visit the children of this
node | |
239 */ | |
240 void visitChildren(XmlVisitor<Object> visitor); | |
241 /** | |
242 * Make this node the parent of the given child nodes. | |
243 * @param children the nodes that will become the children of this node | |
244 * @return the nodes that were made children of this node | |
245 */ | |
246 List<XmlNode> becomeParentOf(List<XmlNode> children) { | |
247 if (children != null) { | |
248 for (JavaIterator<XmlNode> iter = new JavaIterator(children); iter.hasNext
;) { | |
249 XmlNode node = iter.next(); | |
250 node.parent = this; | |
251 } | |
252 } | |
253 return children; | |
254 } | |
255 /** | |
256 * Make this node the parent of the given child node. | |
257 * @param child the node that will become a child of this node | |
258 * @return the node that was made a child of this node | |
259 */ | |
260 XmlNode becomeParentOf2(XmlNode child) { | |
261 if (child != null) { | |
262 XmlNode node = child; | |
263 node.parent = this; | |
264 } | |
265 return child; | |
266 } | |
267 /** | |
268 * Set the parent of this node to the given node. | |
269 * @param newParent the node that is to be made the parent of this node | |
270 */ | |
271 void set parent(XmlNode newParent) { | |
272 _parent = newParent; | |
273 } | |
274 } | |
275 /** | |
276 * Instances of the class {@code SimpleXmlVisitor} implement an AST visitor that
will do nothing | |
277 * when visiting an AST node. It is intended to be a superclass for classes that
use the visitor | |
278 * pattern primarily as a dispatch mechanism (and hence don't need to recursivel
y visit a whole | |
279 * structure) and that only need to visit a small number of node types. | |
280 */ | |
281 class SimpleXmlVisitor<R> implements XmlVisitor<R> { | |
282 R visitHtmlUnit(HtmlUnit htmlUnit) => null; | |
283 R visitXmlAttributeNode(XmlAttributeNode xmlAttributeNode) => null; | |
284 R visitXmlTagNode(XmlTagNode xmlTagNode) => null; | |
285 } | |
286 /** | |
287 * The abstract class {@code AbstractScanner} implements a scanner for HTML code
. Subclasses are | |
288 * required to implement the interface used to access the characters being scann
ed. | |
289 * @coverage dart.engine.html | |
290 */ | |
291 abstract class AbstractScanner { | |
292 static List<String> _NO_PASS_THROUGH_ELEMENTS = <String> []; | |
293 /** | |
294 * The source being scanned. | |
295 */ | |
296 Source _source; | |
297 /** | |
298 * The token pointing to the head of the linked list of tokens. | |
299 */ | |
300 Token _tokens; | |
301 /** | |
302 * The last token that was scanned. | |
303 */ | |
304 Token _tail; | |
305 /** | |
306 * A list containing the offsets of the first character of each line in the so
urce code. | |
307 */ | |
308 List<int> _lineStarts = new List<int>(); | |
309 /** | |
310 * An array of element tags for which the content between tags should be consi
der a single token. | |
311 */ | |
312 List<String> _passThroughElements = _NO_PASS_THROUGH_ELEMENTS; | |
313 /** | |
314 * Initialize a newly created scanner. | |
315 * @param source the source being scanned | |
316 */ | |
317 AbstractScanner(Source source) { | |
318 this._source = source; | |
319 _tokens = new Token.con1(TokenType.EOF, -1); | |
320 _tokens.setNext(_tokens); | |
321 _tail = _tokens; | |
322 recordStartOfLine(); | |
323 } | |
324 /** | |
325 * Return an array containing the offsets of the first character of each line
in the source code. | |
326 * @return an array containing the offsets of the first character of each line
in the source code | |
327 */ | |
328 List<int> get lineStarts => _lineStarts; | |
329 /** | |
330 * Return the current offset relative to the beginning of the file. Return the
initial offset if | |
331 * the scanner has not yet scanned the source code, and one (1) past the end o
f the source code if | |
332 * the source code has been scanned. | |
333 * @return the current offset of the scanner in the source | |
334 */ | |
335 int get offset; | |
336 /** | |
337 * Answer the source being scanned. | |
338 * @return the source or {@code null} if undefined | |
339 */ | |
340 Source get source => _source; | |
341 /** | |
342 * Set array of element tags for which the content between tags should be cons
ider a single token. | |
343 */ | |
344 void set passThroughElements(List<String> passThroughElements2) { | |
345 this._passThroughElements = passThroughElements2 != null ? passThroughElemen
ts2 : _NO_PASS_THROUGH_ELEMENTS; | |
346 } | |
347 /** | |
348 * Scan the source code to produce a list of tokens representing the source. | |
349 * @return the first token in the list of tokens that were produced | |
350 */ | |
351 Token tokenize() { | |
352 scan(); | |
353 appendEofToken(); | |
354 return firstToken(); | |
355 } | |
356 /** | |
357 * Advance the current position and return the character at the new current po
sition. | |
358 * @return the character at the new current position | |
359 */ | |
360 int advance(); | |
361 /** | |
362 * Return the substring of the source code between the start offset and the mo
dified current | |
363 * position. The current position is modified by adding the end delta. | |
364 * @param start the offset to the beginning of the string, relative to the sta
rt of the file | |
365 * @param endDelta the number of character after the current location to be in
cluded in the | |
366 * string, or the number of characters before the current location to be exclu
ded if the | |
367 * offset is negative | |
368 * @return the specified substring of the source code | |
369 */ | |
370 String getString(int start, int endDelta); | |
371 /** | |
372 * Return the character at the current position without changing the current p
osition. | |
373 * @return the character at the current position | |
374 */ | |
375 int peek(); | |
376 /** | |
377 * Record the fact that we are at the beginning of a new line in the source. | |
378 */ | |
379 void recordStartOfLine() { | |
380 _lineStarts.add(offset); | |
381 } | |
382 void appendEofToken() { | |
383 Token eofToken = new Token.con1(TokenType.EOF, offset); | |
384 eofToken.setNext(eofToken); | |
385 _tail = _tail.setNext(eofToken); | |
386 } | |
387 Token emit(Token token) { | |
388 _tail.setNext(token); | |
389 _tail = token; | |
390 return token; | |
391 } | |
392 Token emit2(TokenType type, int start) => emit(new Token.con1(type, start)); | |
393 Token emit3(TokenType type, int start, int count) => emit(new Token.con2(type,
start, getString(start, count))); | |
394 Token firstToken() => _tokens.next; | |
395 int recordStartOfLineAndAdvance(int c) { | |
396 if (c == 0xD) { | |
397 c = advance(); | |
398 if (c == 0xA) { | |
399 c = advance(); | |
400 } | |
401 recordStartOfLine(); | |
402 } else if (c == 0xA) { | |
403 c = advance(); | |
404 recordStartOfLine(); | |
405 } else { | |
406 c = advance(); | |
407 } | |
408 return c; | |
409 } | |
410 void scan() { | |
411 bool inBrackets = false; | |
412 bool passThrough = false; | |
413 int c = advance(); | |
414 while (c >= 0) { | |
415 int start = offset; | |
416 if (c == 0x3C) { | |
417 c = advance(); | |
418 if (c == 0x21) { | |
419 c = advance(); | |
420 if (c == 0x2D && peek() == 0x2D) { | |
421 c = advance(); | |
422 int dashCount = 1; | |
423 while (c >= 0) { | |
424 if (c == 0x2D) { | |
425 dashCount++; | |
426 } else if (c == 0x3E && dashCount >= 2) { | |
427 c = advance(); | |
428 break; | |
429 } else { | |
430 dashCount = 0; | |
431 } | |
432 c = recordStartOfLineAndAdvance(c); | |
433 } | |
434 emit3(TokenType.COMMENT, start, -1); | |
435 if (_tail.length < 7) { | |
436 } | |
437 } else { | |
438 while (c >= 0) { | |
439 if (c == 0x3E) { | |
440 c = advance(); | |
441 break; | |
442 } | |
443 c = recordStartOfLineAndAdvance(c); | |
444 } | |
445 emit3(TokenType.DECLARATION, start, -1); | |
446 if (!_tail.lexeme.endsWith(">")) { | |
447 } | |
448 } | |
449 } else if (c == 0x3F) { | |
450 while (c >= 0) { | |
451 if (c == 0x3F) { | |
452 c = advance(); | |
453 if (c == 0x3E) { | |
454 c = advance(); | |
455 break; | |
456 } | |
457 } else { | |
458 c = recordStartOfLineAndAdvance(c); | |
459 } | |
460 } | |
461 emit3(TokenType.DIRECTIVE, start, -1); | |
462 if (_tail.length < 4) { | |
463 } | |
464 } else if (c == 0x2F) { | |
465 emit2(TokenType.LT_SLASH, start); | |
466 inBrackets = true; | |
467 c = advance(); | |
468 } else { | |
469 inBrackets = true; | |
470 emit2(TokenType.LT, start); | |
471 while (Character.isWhitespace(c)) { | |
472 c = recordStartOfLineAndAdvance(c); | |
473 } | |
474 if (Character.isLetterOrDigit(c)) { | |
475 int tagStart = offset; | |
476 c = advance(); | |
477 while (Character.isLetterOrDigit(c) || c == 0x2D || c == 0x5F) { | |
478 c = advance(); | |
479 } | |
480 emit3(TokenType.TAG, tagStart, -1); | |
481 String tag = _tail.lexeme; | |
482 for (String str in _passThroughElements) { | |
483 if (str == tag) { | |
484 passThrough = true; | |
485 break; | |
486 } | |
487 } | |
488 } | |
489 } | |
490 } else if (c == 0x3E) { | |
491 emit2(TokenType.GT, start); | |
492 inBrackets = false; | |
493 c = advance(); | |
494 if (passThrough) { | |
495 while (c >= 0 && (c != 0x3C || peek() != 0x2F)) { | |
496 c = recordStartOfLineAndAdvance(c); | |
497 } | |
498 if (start + 1 < offset) { | |
499 emit3(TokenType.TEXT, start + 1, -1); | |
500 } | |
501 passThrough = false; | |
502 } | |
503 } else if (c == 0x2F && peek() == 0x3E) { | |
504 advance(); | |
505 emit2(TokenType.SLASH_GT, start); | |
506 inBrackets = false; | |
507 c = advance(); | |
508 } else if (!inBrackets) { | |
509 c = recordStartOfLineAndAdvance(c); | |
510 while (c != 0x3C && c >= 0) { | |
511 c = recordStartOfLineAndAdvance(c); | |
512 } | |
513 emit3(TokenType.TEXT, start, -1); | |
514 } else if (c == 0x22 || c == 0x27) { | |
515 int endQuote = c; | |
516 c = advance(); | |
517 while (c >= 0) { | |
518 if (c == endQuote) { | |
519 c = advance(); | |
520 break; | |
521 } | |
522 c = recordStartOfLineAndAdvance(c); | |
523 } | |
524 emit3(TokenType.STRING, start, -1); | |
525 } else if (c == 0x3D) { | |
526 emit2(TokenType.EQ, start); | |
527 c = advance(); | |
528 } else if (Character.isWhitespace(c)) { | |
529 do { | |
530 c = recordStartOfLineAndAdvance(c); | |
531 } while (Character.isWhitespace(c)); | |
532 } else if (Character.isLetterOrDigit(c)) { | |
533 c = advance(); | |
534 while (Character.isLetterOrDigit(c) || c == 0x2D || c == 0x5F) { | |
535 c = advance(); | |
536 } | |
537 emit3(TokenType.TAG, start, -1); | |
538 } else { | |
539 emit3(TokenType.TEXT, start, 0); | |
540 c = advance(); | |
541 } | |
542 } | |
543 } | |
544 } | |
545 /** | |
546 * Instances of {@code HtmlScanResult} hold the result of scanning an HTML file. | |
547 * @coverage dart.engine.html | |
548 */ | |
549 class HtmlScanResult { | |
550 /** | |
551 * The first token in the token stream (not {@code null}). | |
552 */ | |
553 Token _token; | |
554 /** | |
555 * The line start information that was produced. | |
556 */ | |
557 List<int> _lineStarts; | |
558 HtmlScanResult(Token token, List<int> lineStarts) { | |
559 this._token = token; | |
560 this._lineStarts = lineStarts; | |
561 } | |
562 /** | |
563 * Answer the line start information that was produced. | |
564 * @return an array of line starts (not {@code null}) | |
565 */ | |
566 List<int> get lineStarts => _lineStarts; | |
567 /** | |
568 * Answer the first token in the token stream. | |
569 * @return the token (not {@code null}) | |
570 */ | |
571 Token get token => _token; | |
572 } | |
573 /** | |
574 * Instances of the class {@code StringScanner} implement a scanner that reads f
rom a string. The | |
575 * scanning logic is in the superclass. | |
576 * @coverage dart.engine.html | |
577 */ | |
578 class StringScanner extends AbstractScanner { | |
579 /** | |
580 * The string from which characters will be read. | |
581 */ | |
582 String _string; | |
583 /** | |
584 * The number of characters in the string. | |
585 */ | |
586 int _stringLength = 0; | |
587 /** | |
588 * The index, relative to the string, of the last character that was read. | |
589 */ | |
590 int _charOffset = 0; | |
591 /** | |
592 * Initialize a newly created scanner to scan the characters in the given stri
ng. | |
593 * @param source the source being scanned | |
594 * @param string the string from which characters will be read | |
595 */ | |
596 StringScanner(Source source, String string) : super(source) { | |
597 this._string = string; | |
598 this._stringLength = string.length; | |
599 this._charOffset = -1; | |
600 } | |
601 int get offset => _charOffset; | |
602 void set offset(int offset12) { | |
603 _charOffset = offset12; | |
604 } | |
605 int advance() { | |
606 if (++_charOffset < _stringLength) { | |
607 return _string.codeUnitAt(_charOffset); | |
608 } | |
609 _charOffset = _stringLength; | |
610 return -1; | |
611 } | |
612 String getString(int start, int endDelta) => _string.substring(start, _charOff
set + 1 + endDelta); | |
613 int peek() { | |
614 if (_charOffset + 1 < _stringLength) { | |
615 return _string.codeUnitAt(_charOffset + 1); | |
616 } | |
617 return -1; | |
618 } | |
619 } | |
620 /** | |
621 * Instances of the class {@code CharBufferScanner} implement a scanner that rea
ds from a character | |
622 * buffer. The scanning logic is in the superclass. | |
623 * @coverage dart.engine.html | |
624 */ | |
625 class CharBufferScanner extends AbstractScanner { | |
626 /** | |
627 * The buffer from which characters will be read. | |
628 */ | |
629 CharBuffer _buffer; | |
630 /** | |
631 * The number of characters in the buffer. | |
632 */ | |
633 int _bufferLength = 0; | |
634 /** | |
635 * The index of the last character that was read. | |
636 */ | |
637 int _charOffset = 0; | |
638 /** | |
639 * Initialize a newly created scanner to scan the characters in the given char
acter buffer. | |
640 * @param source the source being scanned | |
641 * @param buffer the buffer from which characters will be read | |
642 */ | |
643 CharBufferScanner(Source source, CharBuffer buffer) : super(source) { | |
644 this._buffer = buffer; | |
645 this._bufferLength = buffer.length(); | |
646 this._charOffset = -1; | |
647 } | |
648 int get offset => _charOffset; | |
649 int advance() { | |
650 if (++_charOffset < _bufferLength) { | |
651 return _buffer.charAt(_charOffset); | |
652 } | |
653 _charOffset = _bufferLength; | |
654 return -1; | |
655 } | |
656 String getString(int start, int endDelta) => _buffer.subSequence(start, _charO
ffset + 1 + endDelta).toString(); | |
657 int peek() { | |
658 if (_charOffset + 1 < _bufferLength) { | |
659 return _buffer.charAt(_charOffset + 1); | |
660 } | |
661 return -1; | |
662 } | |
663 } | |
664 /** | |
665 * The enumeration {@code TokenType} defines the types of tokens that can be ret
urned by the | |
666 * scanner. | |
667 * @coverage dart.engine.html | |
668 */ | |
669 class TokenType { | |
670 /** | |
671 * The type of the token that marks the end of the input. | |
672 */ | |
673 static final TokenType EOF = new TokenType_EOF('EOF', 0, ""); | |
674 static final TokenType EQ = new TokenType('EQ', 1, "="); | |
675 static final TokenType GT = new TokenType('GT', 2, ">"); | |
676 static final TokenType LT_SLASH = new TokenType('LT_SLASH', 3, "</"); | |
677 static final TokenType LT = new TokenType('LT', 4, "<"); | |
678 static final TokenType SLASH_GT = new TokenType('SLASH_GT', 5, "/>"); | |
679 static final TokenType COMMENT = new TokenType('COMMENT', 6, null); | |
680 static final TokenType DECLARATION = new TokenType('DECLARATION', 7, null); | |
681 static final TokenType DIRECTIVE = new TokenType('DIRECTIVE', 8, null); | |
682 static final TokenType STRING = new TokenType('STRING', 9, null); | |
683 static final TokenType TAG = new TokenType('TAG', 10, null); | |
684 static final TokenType TEXT = new TokenType('TEXT', 11, null); | |
685 static final List<TokenType> values = [EOF, EQ, GT, LT_SLASH, LT, SLASH_GT, CO
MMENT, DECLARATION, DIRECTIVE, STRING, TAG, TEXT]; | |
686 final String __name; | |
687 final int __ordinal; | |
688 int get ordinal => __ordinal; | |
689 /** | |
690 * The lexeme that defines this type of token, or {@code null} if there is mor
e than one possible | |
691 * lexeme for this type of token. | |
692 */ | |
693 String _lexeme; | |
694 TokenType(this.__name, this.__ordinal, String lexeme) { | |
695 this._lexeme = lexeme; | |
696 } | |
697 /** | |
698 * Return the lexeme that defines this type of token, or {@code null} if there
is more than one | |
699 * possible lexeme for this type of token. | |
700 * @return the lexeme that defines this type of token | |
701 */ | |
702 String get lexeme => _lexeme; | |
703 String toString() => __name; | |
704 } | |
705 class TokenType_EOF extends TokenType { | |
706 TokenType_EOF(String ___name, int ___ordinal, String arg0) : super(___name, __
_ordinal, arg0); | |
707 String toString() => "-eof-"; | |
708 } | |
709 /** | |
710 * Instances of {@code XmlAttributeNode} represent name/value pairs owned by an
{@link XmlTagNode}. | |
711 * @coverage dart.engine.html | |
712 */ | |
713 class XmlAttributeNode extends XmlNode { | |
714 Token _name; | |
715 Token _equals; | |
716 Token _value; | |
717 /** | |
718 * Construct a new instance representing an XML attribute. | |
719 * @param name the name token (not {@code null}). This may be a zero length to
ken if the attribute | |
720 * is badly formed. | |
721 * @param equals the equals sign or {@code null} if none | |
722 * @param value the value token (not {@code null}) | |
723 */ | |
724 XmlAttributeNode(Token name, Token equals, Token value) { | |
725 this._name = name; | |
726 this._equals = equals; | |
727 this._value = value; | |
728 } | |
729 accept(XmlVisitor visitor) => visitor.visitXmlAttributeNode(this); | |
730 Token get beginToken => _name; | |
731 Token get endToken => _value; | |
732 /** | |
733 * Answer the equals sign token that appears between the name and value tokens
. This may be{@code null} if the attribute is badly formed. | |
734 * @return the token or {@code null} if there is no equals sign between the na
me and value | |
735 */ | |
736 Token get equals => _equals; | |
737 /** | |
738 * Answer the attribute name. This may be a zero length token if the attribute
is badly formed. | |
739 * @return the name (not {@code null}) | |
740 */ | |
741 Token get name => _name; | |
742 /** | |
743 * Answer the lexeme for the value token without the leading and trailing quot
es. | |
744 * @return the text or {@code null} if the value is not specified | |
745 */ | |
746 String get text { | |
747 if (_value == null) { | |
748 return null; | |
749 } | |
750 String text = _value.lexeme; | |
751 int len = text.length; | |
752 if (len > 0) { | |
753 if (text.codeUnitAt(0) == 0x22) { | |
754 if (len > 1 && text.codeUnitAt(len - 1) == 0x22) { | |
755 return text.substring(1, len - 1); | |
756 } else { | |
757 return text.substring(1); | |
758 } | |
759 } else if (text.codeUnitAt(0) == 0x27) { | |
760 if (len > 1 && text.codeUnitAt(len - 1) == 0x27) { | |
761 return text.substring(1, len - 1); | |
762 } else { | |
763 return text.substring(1); | |
764 } | |
765 } | |
766 } | |
767 return text; | |
768 } | |
769 /** | |
770 * Answer the attribute value. A properly formed value will start and end with
matching quote | |
771 * characters, but the value returned may not be properly formed. | |
772 * @return the value or {@code null} if this represents a badly formed attribu
te | |
773 */ | |
774 Token get value => _value; | |
775 void visitChildren(XmlVisitor<Object> visitor) { | |
776 } | |
777 } | |
778 /** | |
779 * The interface {@code XmlVisitor} defines the behavior of objects that can be
used to visit an{@link XmlNode} structure. | |
780 * @coverage dart.engine.html | |
781 */ | |
782 abstract class XmlVisitor<R> { | |
783 R visitHtmlUnit(HtmlUnit htmlUnit); | |
784 R visitXmlAttributeNode(XmlAttributeNode xmlAttributeNode); | |
785 R visitXmlTagNode(XmlTagNode xmlTagNode); | |
786 } | |
787 /** | |
788 * Instances of {@code HtmlScanner} receive and scan HTML content from a {@link
Source}.<br/> | |
789 * For example, the following code scans HTML source and returns the result: | |
790 * <pre> | |
791 * HtmlScanner scanner = new HtmlScanner(source); | |
792 * source.getContents(scanner); | |
793 * return scanner.getResult(); | |
794 * </pre> | |
795 * @coverage dart.engine.html | |
796 */ | |
797 class HtmlScanner implements Source_ContentReceiver { | |
798 List<String> _SCRIPT_TAG = <String> ["script"]; | |
799 /** | |
800 * The source being scanned (not {@code null}) | |
801 */ | |
802 Source _source; | |
803 /** | |
804 * The scanner used to scan the source | |
805 */ | |
806 AbstractScanner _scanner; | |
807 /** | |
808 * The first token in the token stream. | |
809 */ | |
810 Token _token; | |
811 /** | |
812 * Construct a new instance to scan the specified source. | |
813 * @param source the source to be scanned (not {@code null}) | |
814 */ | |
815 HtmlScanner(Source source) { | |
816 this._source = source; | |
817 } | |
818 accept(CharBuffer contents) { | |
819 _scanner = new CharBufferScanner(_source, contents); | |
820 _scanner.passThroughElements = _SCRIPT_TAG; | |
821 _token = _scanner.tokenize(); | |
822 } | |
823 void accept2(String contents) { | |
824 _scanner = new StringScanner(_source, contents); | |
825 _scanner.passThroughElements = _SCRIPT_TAG; | |
826 _token = _scanner.tokenize(); | |
827 } | |
828 /** | |
829 * Answer the result of scanning the source | |
830 * @return the result (not {@code null}) | |
831 */ | |
832 HtmlScanResult get result => new HtmlScanResult(_token, _scanner.lineStarts); | |
833 } | |
834 /** | |
835 * Instances of the class {@code XmlParser} are used to parse tokens into a AST
structure comprised | |
836 * of {@link XmlNode}s. | |
837 * @coverage dart.engine.html | |
838 */ | |
839 class XmlParser { | |
840 /** | |
841 * The source being parsed. | |
842 */ | |
843 Source _source; | |
844 /** | |
845 * The next token to be parsed. | |
846 */ | |
847 Token _currentToken; | |
848 /** | |
849 * Construct a parser for the specified source. | |
850 * @param source the source being parsed | |
851 */ | |
852 XmlParser(Source source) { | |
853 this._source = source; | |
854 } | |
855 /** | |
856 * Answer the source being parsed. | |
857 * @return the source | |
858 */ | |
859 Source get source => _source; | |
860 /** | |
861 * Answer {@code true} if the specified tag is self closing and thus should ne
ver have content or | |
862 * child tag nodes. | |
863 * @param tag the tag (not {@code null}) | |
864 * @return {@code true} if self closing | |
865 */ | |
866 bool isSelfClosing(Token tag) => false; | |
867 /** | |
868 * Parse the entire token stream and in the process, advance the current token
to the end of the | |
869 * token stream. | |
870 * @return the list of tag nodes found (not {@code null}, contains no {@code n
ull}) | |
871 */ | |
872 List<XmlTagNode> parseTopTagNodes(Token firstToken) { | |
873 _currentToken = firstToken; | |
874 List<XmlTagNode> tagNodes = new List<XmlTagNode>(); | |
875 while (true) { | |
876 while (true) { | |
877 if (_currentToken.type == TokenType.LT) { | |
878 tagNodes.add(parseTagNode()); | |
879 } else if (_currentToken.type == TokenType.DECLARATION || _currentToken.
type == TokenType.DIRECTIVE || _currentToken.type == TokenType.COMMENT) { | |
880 _currentToken = _currentToken.next; | |
881 } else if (_currentToken.type == TokenType.EOF) { | |
882 return tagNodes; | |
883 } else { | |
884 reportUnexpectedToken(); | |
885 _currentToken = _currentToken.next; | |
886 } | |
887 break; | |
888 } | |
889 } | |
890 } | |
891 /** | |
892 * Answer the current token. | |
893 * @return the current token | |
894 */ | |
895 Token get currentToken => _currentToken; | |
896 /** | |
897 * Insert a synthetic token of the specified type before the current token | |
898 * @param type the type of token to be inserted (not {@code null}) | |
899 * @return the synthetic token that was inserted (not {@code null}) | |
900 */ | |
901 Token insertSyntheticToken(TokenType type) { | |
902 Token token = new Token.con2(type, _currentToken.offset, ""); | |
903 _currentToken.previous.setNext(token); | |
904 token.setNext(_currentToken); | |
905 return token; | |
906 } | |
907 /** | |
908 * Parse the token stream for an attribute. This method advances the current t
oken over the | |
909 * attribute, but should not be called if the {@link #currentToken} is not {@l
ink TokenType#TAG}. | |
910 * @return the attribute (not {@code null}) | |
911 */ | |
912 XmlAttributeNode parseAttribute() { | |
913 Token name = _currentToken; | |
914 _currentToken = _currentToken.next; | |
915 Token equals; | |
916 if (identical(_currentToken.type, TokenType.EQ)) { | |
917 equals = _currentToken; | |
918 _currentToken = _currentToken.next; | |
919 } else { | |
920 reportUnexpectedToken(); | |
921 equals = insertSyntheticToken(TokenType.EQ); | |
922 } | |
923 Token value; | |
924 if (identical(_currentToken.type, TokenType.STRING)) { | |
925 value = _currentToken; | |
926 _currentToken = _currentToken.next; | |
927 } else { | |
928 reportUnexpectedToken(); | |
929 value = insertSyntheticToken(TokenType.STRING); | |
930 } | |
931 return new XmlAttributeNode(name, equals, value); | |
932 } | |
933 /** | |
934 * Parse the stream for a sequence of attributes. This method advances the cur
rent token to the | |
935 * next {@link TokenType#GT}, {@link TokenType#SLASH_GT}, or {@link TokenType#
EOF}. | |
936 * @return a collection of zero or more attributes (not {@code null}, contains
no {@code null}s) | |
937 */ | |
938 List<XmlAttributeNode> parseAttributes() { | |
939 TokenType type11 = _currentToken.type; | |
940 if (identical(type11, TokenType.GT) || identical(type11, TokenType.SLASH_GT)
|| identical(type11, TokenType.EOF)) { | |
941 return XmlTagNode.NO_ATTRIBUTES; | |
942 } | |
943 List<XmlAttributeNode> attributes = new List<XmlAttributeNode>(); | |
944 while (true) { | |
945 while (true) { | |
946 if (_currentToken.type == TokenType.GT || _currentToken.type == TokenTyp
e.SLASH_GT || _currentToken.type == TokenType.EOF) { | |
947 return attributes; | |
948 } else if (_currentToken.type == TokenType.TAG) { | |
949 attributes.add(parseAttribute()); | |
950 } else { | |
951 reportUnexpectedToken(); | |
952 _currentToken = _currentToken.next; | |
953 } | |
954 break; | |
955 } | |
956 } | |
957 } | |
958 /** | |
959 * Parse the stream for a sequence of tag nodes existing within a parent tag n
ode. This method | |
960 * advances the current token to the next {@link TokenType#LT_SLASH} or {@link
TokenType#EOF}. | |
961 * @return a list of nodes (not {@code null}, contains no {@code null}s) | |
962 */ | |
963 List<XmlTagNode> parseChildTagNodes() { | |
964 TokenType type12 = _currentToken.type; | |
965 if (identical(type12, TokenType.LT_SLASH) || identical(type12, TokenType.EOF
)) { | |
966 return XmlTagNode.NO_TAG_NODES; | |
967 } | |
968 List<XmlTagNode> nodes = new List<XmlTagNode>(); | |
969 while (true) { | |
970 while (true) { | |
971 if (_currentToken.type == TokenType.LT) { | |
972 nodes.add(parseTagNode()); | |
973 } else if (_currentToken.type == TokenType.LT_SLASH || _currentToken.typ
e == TokenType.EOF) { | |
974 return nodes; | |
975 } else if (_currentToken.type == TokenType.COMMENT) { | |
976 _currentToken = _currentToken.next; | |
977 } else { | |
978 reportUnexpectedToken(); | |
979 _currentToken = _currentToken.next; | |
980 } | |
981 break; | |
982 } | |
983 } | |
984 } | |
985 /** | |
986 * Parse the token stream for the next tag node. This method advances current
token over the | |
987 * parsed tag node, but should only be called if the current token is {@link T
okenType#LT} | |
988 * @return the tag node or {@code null} if none found | |
989 */ | |
990 XmlTagNode parseTagNode() { | |
991 Token nodeStart = _currentToken; | |
992 _currentToken = _currentToken.next; | |
993 Token tag; | |
994 if (identical(_currentToken.type, TokenType.TAG)) { | |
995 tag = _currentToken; | |
996 _currentToken = _currentToken.next; | |
997 } else { | |
998 reportUnexpectedToken(); | |
999 tag = insertSyntheticToken(TokenType.TAG); | |
1000 } | |
1001 List<XmlAttributeNode> attributes = parseAttributes(); | |
1002 Token attributeEnd; | |
1003 if (identical(_currentToken.type, TokenType.GT) || identical(_currentToken.t
ype, TokenType.SLASH_GT)) { | |
1004 attributeEnd = _currentToken; | |
1005 _currentToken = _currentToken.next; | |
1006 } else { | |
1007 reportUnexpectedToken(); | |
1008 attributeEnd = insertSyntheticToken(TokenType.SLASH_GT); | |
1009 } | |
1010 if (identical(attributeEnd.type, TokenType.SLASH_GT) || isSelfClosing(tag))
{ | |
1011 return new XmlTagNode(nodeStart, tag, attributes, attributeEnd, XmlTagNode
.NO_TAG_NODES, _currentToken, null, attributeEnd); | |
1012 } | |
1013 List<XmlTagNode> tagNodes = parseChildTagNodes(); | |
1014 Token contentEnd; | |
1015 if (identical(_currentToken.type, TokenType.LT_SLASH)) { | |
1016 contentEnd = _currentToken; | |
1017 _currentToken = _currentToken.next; | |
1018 } else { | |
1019 reportUnexpectedToken(); | |
1020 contentEnd = insertSyntheticToken(TokenType.LT_SLASH); | |
1021 } | |
1022 Token closingTag; | |
1023 if (identical(_currentToken.type, TokenType.TAG)) { | |
1024 closingTag = _currentToken; | |
1025 _currentToken = _currentToken.next; | |
1026 } else { | |
1027 reportUnexpectedToken(); | |
1028 closingTag = insertSyntheticToken(TokenType.TAG); | |
1029 } | |
1030 Token nodeEnd; | |
1031 if (identical(_currentToken.type, TokenType.GT)) { | |
1032 nodeEnd = _currentToken; | |
1033 _currentToken = _currentToken.next; | |
1034 } else { | |
1035 reportUnexpectedToken(); | |
1036 nodeEnd = insertSyntheticToken(TokenType.GT); | |
1037 } | |
1038 return new XmlTagNode(nodeStart, tag, attributes, attributeEnd, tagNodes, co
ntentEnd, closingTag, nodeEnd); | |
1039 } | |
1040 /** | |
1041 * Report the current token as unexpected | |
1042 */ | |
1043 void reportUnexpectedToken() { | |
1044 } | |
1045 } | |
1046 /** | |
1047 * Instances of {@code XmlTagNode} represent XML or HTML elements such as {@code
<p>} and{@code <body foo="bar"> ... </body>}. | |
1048 * @coverage dart.engine.html | |
1049 */ | |
1050 class XmlTagNode extends XmlNode { | |
1051 /** | |
1052 * Constant representing empty list of attributes. | |
1053 */ | |
1054 static List<XmlAttributeNode> NO_ATTRIBUTES = new UnmodifiableListView(new Lis
t<XmlAttributeNode>()); | |
1055 /** | |
1056 * Constant representing empty list of tag nodes. | |
1057 */ | |
1058 static List<XmlTagNode> NO_TAG_NODES = new UnmodifiableListView(new List<XmlTa
gNode>()); | |
1059 /** | |
1060 * The starting {@link TokenType#LT} token (not {@code null}). | |
1061 */ | |
1062 Token _nodeStart; | |
1063 /** | |
1064 * The {@link TokenType#TAG} token after the starting '<' (not {@code null}
). | |
1065 */ | |
1066 Token _tag; | |
1067 /** | |
1068 * The attributes contained by the receiver (not {@code null}, contains no {@c
ode null}s). | |
1069 */ | |
1070 List<XmlAttributeNode> _attributes; | |
1071 /** | |
1072 * The {@link TokenType#GT} or {@link TokenType#SLASH_GT} token after the attr
ibutes (not{@code null}). The token may be the same token as {@link #nodeEnd} if
there are no child{@link #tagNodes}. | |
1073 */ | |
1074 Token _attributeEnd; | |
1075 /** | |
1076 * The tag nodes contained in the receiver (not {@code null}, contains no {@co
de null}s). | |
1077 */ | |
1078 List<XmlTagNode> _tagNodes; | |
1079 /** | |
1080 * The token (not {@code null}) after the content, which may be | |
1081 * <ul> | |
1082 * <li>(1) {@link TokenType#LT_SLASH} for nodes with open and close tags, or</
li> | |
1083 * <li>(2) the {@link TokenType#LT} nodeStart of the next sibling node if this
node is self | |
1084 * closing or the attributeEnd is {@link TokenType#SLASH_GT}, or</li> | |
1085 * <li>(3) {@link TokenType#EOF} if the node does not have a closing tag and i
s the last node in | |
1086 * the stream {@link TokenType#LT_SLASH} token after the content, or {@code nu
ll} if there is no | |
1087 * content and the attributes ended with {@link TokenType#SLASH_GT}.</li> | |
1088 * </ul> | |
1089 */ | |
1090 Token _contentEnd; | |
1091 /** | |
1092 * The closing {@link TokenType#TAG} after the child elements or {@code null}
if there is no | |
1093 * content and the attributes ended with {@link TokenType#SLASH_GT} | |
1094 */ | |
1095 Token _closingTag; | |
1096 /** | |
1097 * The ending {@link TokenType#GT} or {@link TokenType#SLASH_GT} token (not {@
code null}). | |
1098 */ | |
1099 Token _nodeEnd; | |
1100 /** | |
1101 * Construct a new instance representing an XML or HTML element | |
1102 * @param nodeStart the starting {@link TokenType#LT} token (not {@code null}) | |
1103 * @param tag the {@link TokenType#TAG} token after the starting '<' (not {
@code null}). | |
1104 * @param attributes the attributes associated with this element or {@link #NO
_ATTRIBUTES} (not{@code null}, contains no {@code null}s) | |
1105 * @param attributeEnd The {@link TokenType#GT} or {@link TokenType#SLASH_GT}
token after the | |
1106 * attributes (not {@code null}). The token may be the same token as {@link #n
odeEnd} if | |
1107 * there are no child {@link #tagNodes}. | |
1108 * @param tagNodes child tag nodes of the receiver or {@link #NO_TAG_NODES} (n
ot {@code null}, | |
1109 * contains no {@code null}s) | |
1110 * @param contentEnd the token (not {@code null}) after the content, which may
be | |
1111 * <ul> | |
1112 * <li>(1) {@link TokenType#LT_SLASH} for nodes with open and close tags, or</
li> | |
1113 * <li>(2) the {@link TokenType#LT} nodeStart of the next sibling node if this
node is | |
1114 * self closing or the attributeEnd is {@link TokenType#SLASH_GT}, or</li> | |
1115 * <li>(3) {@link TokenType#EOF} if the node does not have a closing tag and i
s the last | |
1116 * node in the stream {@link TokenType#LT_SLASH} token after the content, or {
@code null}if there is no content and the attributes ended with {@link TokenType
#SLASH_GT}.</li> | |
1117 * </ul> | |
1118 * @param closingTag the closing {@link TokenType#TAG} after the child element
s or {@code null} if | |
1119 * there is no content and the attributes ended with {@link TokenType#SLASH_GT
} | |
1120 * @param nodeEnd the ending {@link TokenType#GT} or {@link TokenType#SLASH_GT
} token (not{@code null}) | |
1121 */ | |
1122 XmlTagNode(Token nodeStart, Token tag, List<XmlAttributeNode> attributes, Toke
n attributeEnd, List<XmlTagNode> tagNodes, Token contentEnd, Token closingTag, T
oken nodeEnd) { | |
1123 this._nodeStart = nodeStart; | |
1124 this._tag = tag; | |
1125 this._attributes = becomeParentOf(attributes); | |
1126 this._attributeEnd = attributeEnd; | |
1127 this._tagNodes = becomeParentOf(tagNodes); | |
1128 this._contentEnd = contentEnd; | |
1129 this._closingTag = closingTag; | |
1130 this._nodeEnd = nodeEnd; | |
1131 } | |
1132 accept(XmlVisitor visitor) => visitor.visitXmlTagNode(this); | |
1133 /** | |
1134 * The {@link TokenType#GT} or {@link TokenType#SLASH_GT} token after the attr
ibutes (not{@code null}). The token may be the same token as {@link #nodeEnd} if
there are no child{@link #tagNodes}. | |
1135 * @return the token (not {@code null}) | |
1136 */ | |
1137 Token get attributeEnd => _attributeEnd; | |
1138 /** | |
1139 * Answer the receiver's attributes. Callers should not manipulate the returne
d list to edit the | |
1140 * AST structure. | |
1141 * @return the attributes (not {@code null}, contains no {@code null}s) | |
1142 */ | |
1143 List<XmlAttributeNode> get attributes => _attributes; | |
1144 Token get beginToken => _nodeStart; | |
1145 /** | |
1146 * The the closing {@link TokenType#TAG} after the child elements or {@code nu
ll} if there is no | |
1147 * content and the attributes ended with {@link TokenType#SLASH_GT} | |
1148 * @return the closing tag or {@code null} | |
1149 */ | |
1150 Token get closingTag => _closingTag; | |
1151 /** | |
1152 * Answer a string representing the content contained in the receiver. This in
cludes the textual | |
1153 * representation of any child tag nodes ({@link #getTagNodes()}). Whitespace
between '<', | |
1154 * '</', and '>', '/>' is discarded, but all other whitespace is preserved. | |
1155 * @return the content (not {@code null}) | |
1156 */ | |
1157 String get content { | |
1158 Token token = _attributeEnd.next; | |
1159 if (identical(token, _contentEnd)) { | |
1160 return ""; | |
1161 } | |
1162 String content = token.lexeme; | |
1163 token = token.next; | |
1164 if (identical(token, _contentEnd)) { | |
1165 return content; | |
1166 } | |
1167 JavaStringBuilder buffer = new JavaStringBuilder(); | |
1168 while (token != _contentEnd) { | |
1169 buffer.append(token.lexeme); | |
1170 token = token.next; | |
1171 } | |
1172 return buffer.toString(); | |
1173 } | |
1174 /** | |
1175 * Answer the token (not {@code null}) after the content, which may be | |
1176 * <ul> | |
1177 * <li>(1) {@link TokenType#LT_SLASH} for nodes with open and close tags, or</
li> | |
1178 * <li>(2) the {@link TokenType#LT} nodeStart of the next sibling node if this
node is self | |
1179 * closing or the attributeEnd is {@link TokenType#SLASH_GT}, or</li> | |
1180 * <li>(3) {@link TokenType#EOF} if the node does not have a closing tag and i
s the last node in | |
1181 * the stream {@link TokenType#LT_SLASH} token after the content, or {@code nu
ll} if there is no | |
1182 * content and the attributes ended with {@link TokenType#SLASH_GT}.</li> | |
1183 * </ul> | |
1184 * @return the token (not {@code null}) | |
1185 */ | |
1186 Token get contentEnd => _contentEnd; | |
1187 Token get endToken { | |
1188 if (_nodeEnd != null) { | |
1189 return _nodeEnd; | |
1190 } | |
1191 if (_closingTag != null) { | |
1192 return _closingTag; | |
1193 } | |
1194 if (_contentEnd != null) { | |
1195 return _contentEnd; | |
1196 } | |
1197 if (!_tagNodes.isEmpty) { | |
1198 return _tagNodes[_tagNodes.length - 1].endToken; | |
1199 } | |
1200 if (_attributeEnd != null) { | |
1201 return _attributeEnd; | |
1202 } | |
1203 if (!_attributes.isEmpty) { | |
1204 return _attributes[_attributes.length - 1].endToken; | |
1205 } | |
1206 return _tag; | |
1207 } | |
1208 /** | |
1209 * Answer the ending {@link TokenType#GT} or {@link TokenType#SLASH_GT} token. | |
1210 * @return the token (not {@code null}) | |
1211 */ | |
1212 Token get nodeEnd => _nodeEnd; | |
1213 /** | |
1214 * Answer the starting {@link TokenType#LT} token. | |
1215 * @return the token (not {@code null}) | |
1216 */ | |
1217 Token get nodeStart => _nodeStart; | |
1218 /** | |
1219 * Answer the {@link TokenType#TAG} token after the starting '<'. | |
1220 * @return the token (not {@code null}) | |
1221 */ | |
1222 Token get tag => _tag; | |
1223 /** | |
1224 * Answer the tag nodes contained in the receiver. Callers should not manipula
te the returned list | |
1225 * to edit the AST structure. | |
1226 * @return the children (not {@code null}, contains no {@code null}s) | |
1227 */ | |
1228 List<XmlTagNode> get tagNodes => _tagNodes; | |
1229 void visitChildren(XmlVisitor<Object> visitor) { | |
1230 for (XmlAttributeNode node in _attributes) { | |
1231 node.accept(visitor); | |
1232 } | |
1233 for (XmlTagNode node in _tagNodes) { | |
1234 node.accept(visitor); | |
1235 } | |
1236 } | |
1237 } | |
1238 /** | |
1239 * Instances of the class {@code HtmlParser} are used to parse tokens into a AST
structure comprised | |
1240 * of {@link XmlNode}s. | |
1241 * @coverage dart.engine.html | |
1242 */ | |
1243 class HtmlParser extends XmlParser { | |
1244 static Set<String> SELF_CLOSING = new Set<String>(); | |
1245 /** | |
1246 * Construct a parser for the specified source. | |
1247 * @param source the source being parsed | |
1248 */ | |
1249 HtmlParser(Source source) : super(source) { | |
1250 } | |
1251 /** | |
1252 * Parse the tokens specified by the given scan result. | |
1253 * @param scanResult the result of scanning an HTML source (not {@code null}) | |
1254 * @return the parse result (not {@code null}) | |
1255 */ | |
1256 HtmlParseResult parse(HtmlScanResult scanResult) { | |
1257 Token firstToken = scanResult.token; | |
1258 List<XmlTagNode> tagNodes = parseTopTagNodes(firstToken); | |
1259 HtmlUnit unit = new HtmlUnit(firstToken, tagNodes, currentToken); | |
1260 return new HtmlParseResult(firstToken, scanResult.lineStarts, unit); | |
1261 } | |
1262 /** | |
1263 * Scan then parse the specified source. | |
1264 * @param source the source to be scanned and parsed (not {@code null}) | |
1265 * @return the parse result (not {@code null}) | |
1266 */ | |
1267 HtmlParseResult parse2(Source source) { | |
1268 HtmlScanner scanner = new HtmlScanner(source); | |
1269 source.getContents(scanner); | |
1270 return parse(scanner.result); | |
1271 } | |
1272 bool isSelfClosing(Token tag) => SELF_CLOSING.contains(tag.lexeme); | |
1273 } | |
1274 /** | |
1275 * Instances of the class {@code HtmlUnit} represent the contents of an HTML fil
e. | |
1276 * @coverage dart.engine.html | |
1277 */ | |
1278 class HtmlUnit extends XmlNode { | |
1279 /** | |
1280 * The first token in the token stream that was parsed to form this HTML unit. | |
1281 */ | |
1282 Token _beginToken; | |
1283 /** | |
1284 * The last token in the token stream that was parsed to form this compilation
unit. This token | |
1285 * should always have a type of {@link TokenType.EOF}. | |
1286 */ | |
1287 Token _endToken; | |
1288 /** | |
1289 * The tag nodes contained in the receiver (not {@code null}, contains no {@co
de null}s). | |
1290 */ | |
1291 List<XmlTagNode> _tagNodes; | |
1292 /** | |
1293 * The element associated with this HTML unit or {@code null} if the receiver
is not resolved. | |
1294 */ | |
1295 HtmlElementImpl _element; | |
1296 /** | |
1297 * Construct a new instance representing the content of an HTML file. | |
1298 * @param beginToken the first token in the file (not {@code null}) | |
1299 * @param tagNodes child tag nodes of the receiver (not {@code null}, contains
no {@code null}s) | |
1300 * @param endToken the last token in the token stream which should be of type{
@link TokenType.EOF} | |
1301 */ | |
1302 HtmlUnit(Token beginToken, List<XmlTagNode> tagNodes, Token endToken) { | |
1303 this._beginToken = beginToken; | |
1304 this._tagNodes = becomeParentOf(tagNodes); | |
1305 this._endToken = endToken; | |
1306 } | |
1307 accept(XmlVisitor visitor) => visitor.visitHtmlUnit(this); | |
1308 Token get beginToken => _beginToken; | |
1309 /** | |
1310 * Return the element associated with this HTML unit. | |
1311 * @return the element or {@code null} if the receiver is not resolved | |
1312 */ | |
1313 HtmlElementImpl get element => _element; | |
1314 Token get endToken => _endToken; | |
1315 /** | |
1316 * Answer the tag nodes contained in the receiver. Callers should not manipula
te the returned list | |
1317 * to edit the AST structure. | |
1318 * @return the children (not {@code null}, contains no {@code null}s) | |
1319 */ | |
1320 List<XmlTagNode> get tagNodes => _tagNodes; | |
1321 /** | |
1322 * Set the element associated with this HTML unit. | |
1323 * @param element the element | |
1324 */ | |
1325 void set element(HtmlElementImpl element18) { | |
1326 this._element = element18; | |
1327 } | |
1328 void visitChildren(XmlVisitor<Object> visitor) { | |
1329 for (XmlTagNode node in _tagNodes) { | |
1330 node.accept(visitor); | |
1331 } | |
1332 } | |
1333 } | |
OLD | NEW |