| OLD | NEW |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 // 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 `Token` represent a token that was scanned from the in
put. Each | 22 * Instances of the class `XmlExpression` represent an abstract expression embed
ded into |
| 23 * token knows which token follows it, acting as the head of a linked list of to
kens. | 23 * [XmlNode]. |
| 24 */ | 24 */ |
| 25 class Token { | 25 abstract class XmlExpression { |
| 26 /** | 26 /** |
| 27 * The offset from the beginning of the file to the first character in the tok
en. | 27 * An empty array of expressions. |
| 28 */ | 28 */ |
| 29 final int offset; | 29 static List<XmlExpression> EMPTY_ARRAY = new List<XmlExpression>(0); |
| 30 | 30 |
| 31 /** | 31 /** |
| 32 * The previous token in the token stream. | 32 * Check if the given offset belongs to the expression's source range. |
| 33 */ | 33 */ |
| 34 Token previous; | 34 bool contains(int offset) => this.offset <= offset && offset < end; |
| 35 | 35 |
| 36 /** | 36 /** |
| 37 * The next token in the token stream. | 37 * Return the offset of the character immediately following the last character
of this |
| 38 */ | 38 * expression's source range. This is equivalent to `getOffset() + getLength()
`. |
| 39 Token _next; | 39 * |
| 40 | 40 * @return the offset of the character just past the expression's source range |
| 41 /** | 41 */ |
| 42 * The type of the token. | 42 int get end; |
| 43 */ | 43 |
| 44 final TokenType type; | 44 /** |
| 45 | 45 * Return the number of characters in the expression's source range. |
| 46 /** | 46 */ |
| 47 * The lexeme represented by this token. | 47 int get length; |
| 48 */ | 48 |
| 49 String _value; | 49 /** |
| 50 | 50 * Return the offset of the first character in the expression's source range. |
| 51 /** | 51 */ |
| 52 * Initialize a newly created token. | 52 int get offset; |
| 53 * | 53 |
| 54 * @param type the token type (not `null`) | 54 /** |
| 55 * @param offset the offset from the beginning of the file to the first charac
ter in the token | 55 * Return the [Reference] at the given offset. |
| 56 */ | 56 * |
| 57 Token.con1(TokenType type, int offset) : this.con2(type, offset, type.lexeme); | 57 * @param offset the offset from the beginning of the file |
| 58 | 58 * @return the [Reference] at the given offset, maybe `null` |
| 59 /** | 59 */ |
| 60 * Initialize a newly created token. | 60 XmlExpression_Reference getReference(int offset); |
| 61 * | |
| 62 * @param type the token type (not `null`) | |
| 63 * @param offset the offset from the beginning of the file to the first charac
ter in the token | |
| 64 * @param value the lexeme represented by this token (not `null`) | |
| 65 */ | |
| 66 Token.con2(this.type, this.offset, String value) { | |
| 67 this._value = StringUtilities.intern(value); | |
| 68 } | |
| 69 | |
| 70 /** | |
| 71 * Return the offset from the beginning of the file to the character after las
t character of the | |
| 72 * token. | |
| 73 * | |
| 74 * @return the offset from the beginning of the file to the first character af
ter last character | |
| 75 * of the token | |
| 76 */ | |
| 77 int get end => offset + length; | |
| 78 | |
| 79 /** | |
| 80 * Return the number of characters in the node's source range. | |
| 81 * | |
| 82 * @return the number of characters in the node's source range | |
| 83 */ | |
| 84 int get length => lexeme.length; | |
| 85 | |
| 86 /** | |
| 87 * Return the lexeme that represents this token. | |
| 88 * | |
| 89 * @return the lexeme (not `null`) | |
| 90 */ | |
| 91 String get lexeme => _value; | |
| 92 | |
| 93 /** | |
| 94 * Return the next token in the token stream. | |
| 95 * | |
| 96 * @return the next token in the token stream | |
| 97 */ | |
| 98 Token get next => _next; | |
| 99 | |
| 100 /** | |
| 101 * Return `true` if this token is a synthetic token. A synthetic token is a to
ken that was | |
| 102 * introduced by the parser in order to recover from an error in the code. Syn
thetic tokens always | |
| 103 * have a length of zero (`0`). | |
| 104 * | |
| 105 * @return `true` if this token is a synthetic token | |
| 106 */ | |
| 107 bool get isSynthetic => length == 0; | |
| 108 | |
| 109 /** | |
| 110 * Set the next token in the token stream to the given token. This has the sid
e-effect of setting | |
| 111 * this token to be the previous token for the given token. | |
| 112 * | |
| 113 * @param token the next token in the token stream | |
| 114 * @return the token that was passed in | |
| 115 */ | |
| 116 Token setNext(Token token) { | |
| 117 _next = token; | |
| 118 token.previous = this; | |
| 119 return token; | |
| 120 } | |
| 121 | |
| 122 @override | |
| 123 String toString() => lexeme; | |
| 124 } | 61 } |
| 125 | 62 |
| 126 /** | 63 /** |
| 127 * Implementation of [XmlExpression] for an [Expression] embedded without any wr
apping | 64 * The reference to the [Element]. |
| 128 * characters. | |
| 129 */ | 65 */ |
| 130 class RawXmlExpression extends XmlExpression { | 66 class XmlExpression_Reference { |
| 131 final Expression expression; | 67 Element element; |
| 132 | 68 |
| 133 RawXmlExpression(this.expression); | |
| 134 | |
| 135 @override | |
| 136 int get end => expression.end; | |
| 137 | |
| 138 @override | |
| 139 int get length => expression.length; | |
| 140 | |
| 141 @override | |
| 142 int get offset => expression.offset; | |
| 143 | |
| 144 @override | |
| 145 XmlExpression_Reference getReference(int offset) { | |
| 146 AstNode node = new NodeLocator.con1(offset).searchWithin(expression); | |
| 147 if (node != null) { | |
| 148 Element element = ElementLocator.locate(node); | |
| 149 return new XmlExpression_Reference(element, node.offset, node.length); | |
| 150 } | |
| 151 return null; | |
| 152 } | |
| 153 } | |
| 154 | |
| 155 /** | |
| 156 * Instances of the class `RecursiveXmlVisitor` implement an XML visitor that wi
ll recursively | |
| 157 * visit all of the nodes in an XML structure. For example, using an instance of
this class to visit | |
| 158 * a [XmlTagNode] will also cause all of the contained [XmlAttributeNode]s and | |
| 159 * [XmlTagNode]s to be visited. | |
| 160 * | |
| 161 * Subclasses that override a visit method must either invoke the overridden vis
it method or must | |
| 162 * explicitly ask the visited node to visit its children. Failure to do so will
cause the children | |
| 163 * of the visited node to not be visited. | |
| 164 */ | |
| 165 class RecursiveXmlVisitor<R> implements XmlVisitor<R> { | |
| 166 @override | |
| 167 R visitHtmlScriptTagNode(HtmlScriptTagNode node) { | |
| 168 node.visitChildren(this); | |
| 169 return null; | |
| 170 } | |
| 171 | |
| 172 @override | |
| 173 R visitHtmlUnit(HtmlUnit node) { | |
| 174 node.visitChildren(this); | |
| 175 return null; | |
| 176 } | |
| 177 | |
| 178 @override | |
| 179 R visitXmlAttributeNode(XmlAttributeNode node) { | |
| 180 node.visitChildren(this); | |
| 181 return null; | |
| 182 } | |
| 183 | |
| 184 @override | |
| 185 R visitXmlTagNode(XmlTagNode node) { | |
| 186 node.visitChildren(this); | |
| 187 return null; | |
| 188 } | |
| 189 } | |
| 190 | |
| 191 /** | |
| 192 * Utilities locating [Expression]s and [Element]s in [HtmlUnit]. | |
| 193 */ | |
| 194 class HtmlUnitUtils { | |
| 195 /** | |
| 196 * Returns the [XmlAttributeNode] that is part of the given [HtmlUnit] and enc
loses | |
| 197 * the given offset. | |
| 198 */ | |
| 199 static XmlAttributeNode getAttributeNode(HtmlUnit htmlUnit, int offset) { | |
| 200 if (htmlUnit == null) { | |
| 201 return null; | |
| 202 } | |
| 203 List<XmlAttributeNode> result = [null]; | |
| 204 try { | |
| 205 htmlUnit.accept(new RecursiveXmlVisitor_HtmlUnitUtils_getAttributeNode(off
set, result)); | |
| 206 } on HtmlUnitUtils_FoundAttributeNodeError catch (e) { | |
| 207 return result[0]; | |
| 208 } | |
| 209 return null; | |
| 210 } | |
| 211 | |
| 212 /** | |
| 213 * Returns the best [Element] of the given [Expression]. | |
| 214 */ | |
| 215 static Element getElement(Expression expression) { | |
| 216 if (expression == null) { | |
| 217 return null; | |
| 218 } | |
| 219 return ElementLocator.locate(expression); | |
| 220 } | |
| 221 | |
| 222 /** | |
| 223 * Returns the [Element] of the [Expression] in the given [HtmlUnit], enclosin
g | |
| 224 * the given offset. | |
| 225 */ | |
| 226 static Element getElementAtOffset(HtmlUnit htmlUnit, int offset) { | |
| 227 Expression expression = getExpression(htmlUnit, offset); | |
| 228 return getElement(expression); | |
| 229 } | |
| 230 | |
| 231 /** | |
| 232 * Returns the [Element] to open when requested at the given [Expression]. | |
| 233 */ | |
| 234 static Element getElementToOpen(HtmlUnit htmlUnit, Expression expression) { | |
| 235 Element element = getElement(expression); | |
| 236 { | |
| 237 AngularElement angularElement = AngularHtmlUnitResolver.getAngularElement(
element); | |
| 238 if (angularElement != null) { | |
| 239 return angularElement; | |
| 240 } | |
| 241 } | |
| 242 return element; | |
| 243 } | |
| 244 | |
| 245 /** | |
| 246 * Returns the [XmlTagNode] that is part of the given [HtmlUnit] and encloses
the | |
| 247 * given offset. | |
| 248 */ | |
| 249 static XmlTagNode getEnclosingTagNode(HtmlUnit htmlUnit, int offset) { | |
| 250 if (htmlUnit == null) { | |
| 251 return null; | |
| 252 } | |
| 253 List<XmlTagNode> result = [null]; | |
| 254 try { | |
| 255 htmlUnit.accept(new RecursiveXmlVisitor_HtmlUnitUtils_getEnclosingTagNode(
offset, result)); | |
| 256 } on HtmlUnitUtils_FoundTagNodeError catch (e) { | |
| 257 return result[0]; | |
| 258 } | |
| 259 return null; | |
| 260 } | |
| 261 | |
| 262 /** | |
| 263 * Returns the [Expression] that is part of the given [HtmlUnit] and encloses
the | |
| 264 * given offset. | |
| 265 */ | |
| 266 static Expression getExpression(HtmlUnit htmlUnit, int offset) { | |
| 267 if (htmlUnit == null) { | |
| 268 return null; | |
| 269 } | |
| 270 List<Expression> result = [null]; | |
| 271 try { | |
| 272 // TODO(scheglov) this code is very Angular specific | |
| 273 htmlUnit.accept(new ExpressionVisitor_HtmlUnitUtils_getExpression(offset,
result)); | |
| 274 } on HtmlUnitUtils_FoundExpressionError catch (e) { | |
| 275 return result[0]; | |
| 276 } | |
| 277 return null; | |
| 278 } | |
| 279 | |
| 280 /** | |
| 281 * Returns the [XmlTagNode] that is part of the given [HtmlUnit] and its open
or | |
| 282 * closing tag name encloses the given offset. | |
| 283 */ | |
| 284 static XmlTagNode getTagNode(HtmlUnit htmlUnit, int offset) { | |
| 285 XmlTagNode node = getEnclosingTagNode(htmlUnit, offset); | |
| 286 // do we have an enclosing tag at all? | |
| 287 if (node == null) { | |
| 288 return null; | |
| 289 } | |
| 290 // is "offset" in the open tag? | |
| 291 Token openTag = node.tagToken; | |
| 292 if (openTag.offset <= offset && offset <= openTag.end) { | |
| 293 return node; | |
| 294 } | |
| 295 // is "offset" in the open tag? | |
| 296 Token closeTag = node.closingTag; | |
| 297 if (closeTag != null && closeTag.offset <= offset && offset <= closeTag.end)
{ | |
| 298 return node; | |
| 299 } | |
| 300 // not on a tag name | |
| 301 return null; | |
| 302 } | |
| 303 | |
| 304 /** | |
| 305 * Returns the [Expression] that is part of the given root [AstNode] and enclo
ses the | |
| 306 * given offset. | |
| 307 */ | |
| 308 static Expression _getExpressionAt(AstNode root, int offset) { | |
| 309 if (root.offset <= offset && offset <= root.end) { | |
| 310 AstNode dartNode = new NodeLocator.con1(offset).searchWithin(root); | |
| 311 if (dartNode is Expression) { | |
| 312 return dartNode; | |
| 313 } | |
| 314 } | |
| 315 return null; | |
| 316 } | |
| 317 } | |
| 318 | |
| 319 class HtmlUnitUtils_FoundAttributeNodeError extends Error { | |
| 320 } | |
| 321 | |
| 322 class HtmlUnitUtils_FoundExpressionError extends Error { | |
| 323 } | |
| 324 | |
| 325 class HtmlUnitUtils_FoundTagNodeError extends Error { | |
| 326 } | |
| 327 | |
| 328 class RecursiveXmlVisitor_HtmlUnitUtils_getAttributeNode extends RecursiveXmlVis
itor<Object> { | |
| 329 int offset = 0; | 69 int offset = 0; |
| 330 | 70 |
| 331 List<XmlAttributeNode> result; | 71 int length = 0; |
| 332 | 72 |
| 333 RecursiveXmlVisitor_HtmlUnitUtils_getAttributeNode(this.offset, this.result) :
super(); | 73 XmlExpression_Reference(Element element, int offset, int length) { |
| 334 | 74 this.element = element; |
| 335 @override | 75 this.offset = offset; |
| 336 Object visitXmlAttributeNode(XmlAttributeNode node) { | 76 this.length = length; |
| 337 Token nameToken = node.nameToken; | |
| 338 if (nameToken.offset <= offset && offset <= nameToken.end) { | |
| 339 result[0] = node; | |
| 340 throw new HtmlUnitUtils_FoundAttributeNodeError(); | |
| 341 } | |
| 342 return super.visitXmlAttributeNode(node); | |
| 343 } | |
| 344 } | |
| 345 | |
| 346 class RecursiveXmlVisitor_HtmlUnitUtils_getEnclosingTagNode extends RecursiveXml
Visitor<Object> { | |
| 347 int offset = 0; | |
| 348 | |
| 349 List<XmlTagNode> result; | |
| 350 | |
| 351 RecursiveXmlVisitor_HtmlUnitUtils_getEnclosingTagNode(this.offset, this.result
) : super(); | |
| 352 | |
| 353 @override | |
| 354 Object visitXmlTagNode(XmlTagNode node) { | |
| 355 if (node.offset <= offset && offset < node.end) { | |
| 356 result[0] = node; | |
| 357 super.visitXmlTagNode(node); | |
| 358 throw new HtmlUnitUtils_FoundTagNodeError(); | |
| 359 } | |
| 360 return null; | |
| 361 } | |
| 362 } | |
| 363 | |
| 364 class ExpressionVisitor_HtmlUnitUtils_getExpression extends ExpressionVisitor { | |
| 365 int offset = 0; | |
| 366 | |
| 367 List<Expression> result; | |
| 368 | |
| 369 ExpressionVisitor_HtmlUnitUtils_getExpression(this.offset, this.result) : supe
r(); | |
| 370 | |
| 371 @override | |
| 372 void visitExpression(Expression expression) { | |
| 373 Expression at = HtmlUnitUtils._getExpressionAt(expression, offset); | |
| 374 if (at != null) { | |
| 375 result[0] = at; | |
| 376 throw new HtmlUnitUtils_FoundExpressionError(); | |
| 377 } | |
| 378 } | |
| 379 } | |
| 380 | |
| 381 /** | |
| 382 * Instances of the class `HtmlScriptTagNode` represent a script tag within an H
TML file that | |
| 383 * references a Dart script. | |
| 384 */ | |
| 385 class HtmlScriptTagNode extends XmlTagNode { | |
| 386 /** | |
| 387 * The AST structure representing the Dart code within this tag. | |
| 388 */ | |
| 389 CompilationUnit _script; | |
| 390 | |
| 391 /** | |
| 392 * The element representing this script. | |
| 393 */ | |
| 394 HtmlScriptElement scriptElement; | |
| 395 | |
| 396 /** | |
| 397 * Initialize a newly created node to represent a script tag within an HTML fi
le that references a | |
| 398 * Dart script. | |
| 399 * | |
| 400 * @param nodeStart the token marking the beginning of the tag | |
| 401 * @param tag the name of the tag | |
| 402 * @param attributes the attributes in the tag | |
| 403 * @param attributeEnd the token terminating the region where attributes can b
e | |
| 404 * @param tagNodes the children of the tag | |
| 405 * @param contentEnd the token that starts the closing tag | |
| 406 * @param closingTag the name of the tag that occurs in the closing tag | |
| 407 * @param nodeEnd the last token in the tag | |
| 408 */ | |
| 409 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); | |
| 410 | |
| 411 @override | |
| 412 accept(XmlVisitor visitor) => visitor.visitHtmlScriptTagNode(this); | |
| 413 | |
| 414 /** | |
| 415 * Return the AST structure representing the Dart code within this tag, or `nu
ll` if this | |
| 416 * tag references an external script. | |
| 417 * | |
| 418 * @return the AST structure representing the Dart code within this tag | |
| 419 */ | |
| 420 CompilationUnit get script => _script; | |
| 421 | |
| 422 /** | |
| 423 * Set the AST structure representing the Dart code within this tag to the giv
en compilation unit. | |
| 424 * | |
| 425 * @param unit the AST structure representing the Dart code within this tag | |
| 426 */ | |
| 427 void set script(CompilationUnit unit) { | |
| 428 _script = unit; | |
| 429 } | |
| 430 } | |
| 431 | |
| 432 /** | |
| 433 * The abstract class `XmlNode` defines behavior common to all XML/HTML nodes. | |
| 434 */ | |
| 435 abstract class XmlNode { | |
| 436 /** | |
| 437 * The parent of the node, or `null` if the node is the root of an AST structu
re. | |
| 438 */ | |
| 439 XmlNode _parent; | |
| 440 | |
| 441 /** | |
| 442 * The element associated with this node or `null` if the receiver is not reso
lved. | |
| 443 */ | |
| 444 Element _element; | |
| 445 | |
| 446 /** | |
| 447 * Use the given visitor to visit this node. | |
| 448 * | |
| 449 * @param visitor the visitor that will visit this node | |
| 450 * @return the value returned by the visitor as a result of visiting this node | |
| 451 */ | |
| 452 accept(XmlVisitor visitor); | |
| 453 | |
| 454 /** | |
| 455 * Return the first token included in this node's source range. | |
| 456 * | |
| 457 * @return the first token or `null` if none | |
| 458 */ | |
| 459 Token get beginToken; | |
| 460 | |
| 461 /** | |
| 462 * Return the element associated with this node. | |
| 463 * | |
| 464 * @return the element or `null` if the receiver is not resolved | |
| 465 */ | |
| 466 Element get element => _element; | |
| 467 | |
| 468 /** | |
| 469 * Return the offset of the character immediately following the last character
of this node's | |
| 470 * source range. This is equivalent to `node.getOffset() + node.getLength()`.
For an html | |
| 471 * unit this will be equal to the length of the unit's source. | |
| 472 * | |
| 473 * @return the offset of the character just past the node's source range | |
| 474 */ | |
| 475 int get end => offset + length; | |
| 476 | |
| 477 /** | |
| 478 * Return the last token included in this node's source range. | |
| 479 * | |
| 480 * @return the last token or `null` if none | |
| 481 */ | |
| 482 Token get endToken; | |
| 483 | |
| 484 /** | |
| 485 * Return the number of characters in the node's source range. | |
| 486 * | |
| 487 * @return the number of characters in the node's source range | |
| 488 */ | |
| 489 int get length { | |
| 490 Token beginToken = this.beginToken; | |
| 491 Token endToken = this.endToken; | |
| 492 if (beginToken == null || endToken == null) { | |
| 493 return -1; | |
| 494 } | |
| 495 return endToken.offset + endToken.length - beginToken.offset; | |
| 496 } | |
| 497 | |
| 498 /** | |
| 499 * Return the offset from the beginning of the file to the first character in
the node's source | |
| 500 * range. | |
| 501 * | |
| 502 * @return the offset from the beginning of the file to the first character in
the node's source | |
| 503 * range | |
| 504 */ | |
| 505 int get offset { | |
| 506 Token beginToken = this.beginToken; | |
| 507 if (beginToken == null) { | |
| 508 return -1; | |
| 509 } | |
| 510 return this.beginToken.offset; | |
| 511 } | |
| 512 | |
| 513 /** | |
| 514 * Return this node's parent node, or `null` if this node is the root of an AS
T structure. | |
| 515 * | |
| 516 * Note that the relationship between an AST node and its parent node may chan
ge over the lifetime | |
| 517 * of a node. | |
| 518 * | |
| 519 * @return the parent of this node, or `null` if none | |
| 520 */ | |
| 521 XmlNode get parent => _parent; | |
| 522 | |
| 523 /** | |
| 524 * Set the element associated with this node. | |
| 525 * | |
| 526 * @param element the element | |
| 527 */ | |
| 528 void set element(Element element) { | |
| 529 this._element = element; | |
| 530 } | |
| 531 | |
| 532 @override | |
| 533 String toString() { | |
| 534 PrintStringWriter writer = new PrintStringWriter(); | |
| 535 accept(new ToSourceVisitor(writer)); | |
| 536 return writer.toString(); | |
| 537 } | |
| 538 | |
| 539 /** | |
| 540 * Use the given visitor to visit all of the children of this node. The childr
en will be visited | |
| 541 * in source order. | |
| 542 * | |
| 543 * @param visitor the visitor that will be used to visit the children of this
node | |
| 544 */ | |
| 545 void visitChildren(XmlVisitor visitor); | |
| 546 | |
| 547 /** | |
| 548 * Make this node the parent of the given child node. | |
| 549 * | |
| 550 * @param child the node that will become a child of this node | |
| 551 * @return the node that was made a child of this node | |
| 552 */ | |
| 553 XmlNode becomeParentOf(XmlNode child) { | |
| 554 if (child != null) { | |
| 555 XmlNode node = child; | |
| 556 node.parent = this; | |
| 557 } | |
| 558 return child; | |
| 559 } | |
| 560 | |
| 561 /** | |
| 562 * Make this node the parent of the given child nodes. | |
| 563 * | |
| 564 * @param children the nodes that will become the children of this node | |
| 565 * @param ifEmpty the (empty) nodes to return if "children" is empty | |
| 566 * @return the nodes that were made children of this node | |
| 567 */ | |
| 568 List becomeParentOfAll(List children, {List ifEmpty}) { | |
| 569 if (children == null || children.isEmpty) { | |
| 570 if (ifEmpty != null) { | |
| 571 return ifEmpty; | |
| 572 } | |
| 573 } | |
| 574 if (children != null) { | |
| 575 for (JavaIterator iter = new JavaIterator(children); iter.hasNext;) { | |
| 576 XmlNode node = iter.next(); | |
| 577 node.parent = this; | |
| 578 } | |
| 579 // This will create ArrayList for exactly given number of elements. | |
| 580 return new List.from(children); | |
| 581 } | |
| 582 return children; | |
| 583 } | |
| 584 | |
| 585 /** | |
| 586 * This method exists for debugging purposes only. | |
| 587 */ | |
| 588 void _appendIdentifier(JavaStringBuilder builder, XmlNode node) { | |
| 589 if (node is XmlTagNode) { | |
| 590 builder.append(node.tag); | |
| 591 } else if (node is XmlAttributeNode) { | |
| 592 builder.append(node.name); | |
| 593 } else { | |
| 594 builder.append("htmlUnit"); | |
| 595 } | |
| 596 } | |
| 597 | |
| 598 /** | |
| 599 * This method exists for debugging purposes only. | |
| 600 */ | |
| 601 String _buildRecursiveStructureMessage(XmlNode newParent) { | |
| 602 JavaStringBuilder builder = new JavaStringBuilder(); | |
| 603 builder.append("Attempt to create recursive structure: "); | |
| 604 XmlNode current = newParent; | |
| 605 while (current != null) { | |
| 606 if (!identical(current, newParent)) { | |
| 607 builder.append(" -> "); | |
| 608 } | |
| 609 if (identical(current, this)) { | |
| 610 builder.appendChar(0x2A); | |
| 611 _appendIdentifier(builder, current); | |
| 612 builder.appendChar(0x2A); | |
| 613 } else { | |
| 614 _appendIdentifier(builder, current); | |
| 615 } | |
| 616 current = current.parent; | |
| 617 } | |
| 618 return builder.toString(); | |
| 619 } | |
| 620 | |
| 621 /** | |
| 622 * Set the parent of this node to the given node. | |
| 623 * | |
| 624 * @param newParent the node that is to be made the parent of this node | |
| 625 */ | |
| 626 void set parent(XmlNode newParent) { | |
| 627 XmlNode current = newParent; | |
| 628 while (current != null) { | |
| 629 if (identical(current, this)) { | |
| 630 AnalysisEngine.instance.logger.logError2("Circular structure while setti
ng an XML node's parent", new IllegalArgumentException(_buildRecursiveStructureM
essage(newParent))); | |
| 631 return; | |
| 632 } | |
| 633 current = current.parent; | |
| 634 } | |
| 635 _parent = newParent; | |
| 636 } | 77 } |
| 637 } | 78 } |
| 638 | 79 |
| 639 /** | 80 /** |
| 640 * Instances of the class `SimpleXmlVisitor` implement an AST visitor that will
do nothing | 81 * Instances of the class `SimpleXmlVisitor` implement an AST visitor that will
do nothing |
| 641 * when visiting an AST node. It is intended to be a superclass for classes that
use the visitor | 82 * when visiting an AST node. It is intended to be a superclass for classes that
use the visitor |
| 642 * pattern primarily as a dispatch mechanism (and hence don't need to recursivel
y visit a whole | 83 * pattern primarily as a dispatch mechanism (and hence don't need to recursivel
y visit a whole |
| 643 * structure) and that only need to visit a small number of node types. | 84 * structure) and that only need to visit a small number of node types. |
| 644 */ | 85 */ |
| 645 class SimpleXmlVisitor<R> implements XmlVisitor<R> { | 86 class SimpleXmlVisitor<R> implements XmlVisitor<R> { |
| (...skipping 323 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 969 } else { | 410 } else { |
| 970 // a non-char token | 411 // a non-char token |
| 971 _emitWithOffsetAndLength(TokenType.TEXT, start, 0); | 412 _emitWithOffsetAndLength(TokenType.TEXT, start, 0); |
| 972 c = advance(); | 413 c = advance(); |
| 973 } | 414 } |
| 974 } | 415 } |
| 975 } | 416 } |
| 976 } | 417 } |
| 977 | 418 |
| 978 /** | 419 /** |
| 979 * Instances of the class `StringScanner` implement a scanner that reads from a
string. The | 420 * Instances of the class `Token` represent a token that was scanned from the in
put. Each |
| 980 * scanning logic is in the superclass. | 421 * token knows which token follows it, acting as the head of a linked list of to
kens. |
| 981 */ | 422 */ |
| 982 class StringScanner extends AbstractScanner { | 423 class Token { |
| 983 /** | 424 /** |
| 984 * The string from which characters will be read. | 425 * The offset from the beginning of the file to the first character in the tok
en. |
| 985 */ | 426 */ |
| 986 final String _string; | 427 final int offset; |
| 987 | 428 |
| 988 /** | 429 /** |
| 989 * The number of characters in the string. | 430 * The previous token in the token stream. |
| 990 */ | 431 */ |
| 991 int _stringLength = 0; | 432 Token previous; |
| 992 | 433 |
| 993 /** | 434 /** |
| 994 * The index, relative to the string, of the last character that was read. | 435 * The next token in the token stream. |
| 995 */ | 436 */ |
| 996 int _charOffset = 0; | 437 Token _next; |
| 997 | 438 |
| 998 /** | 439 /** |
| 999 * Initialize a newly created scanner to scan the characters in the given stri
ng. | 440 * The type of the token. |
| 1000 * | 441 */ |
| 1001 * @param source the source being scanned | 442 final TokenType type; |
| 1002 * @param string the string from which characters will be read | 443 |
| 1003 */ | 444 /** |
| 1004 StringScanner(Source source, this._string) : super(source) { | 445 * The lexeme represented by this token. |
| 1005 this._stringLength = _string.length; | 446 */ |
| 1006 this._charOffset = -1; | 447 String _value; |
| 1007 } | 448 |
| 1008 | 449 /** |
| 1009 @override | 450 * Initialize a newly created token. |
| 1010 int get offset => _charOffset; | 451 * |
| 1011 | 452 * @param type the token type (not `null`) |
| 1012 void set offset(int offset) { | 453 * @param offset the offset from the beginning of the file to the first charac
ter in the token |
| 1013 _charOffset = offset; | 454 */ |
| 1014 } | 455 Token.con1(TokenType type, int offset) : this.con2(type, offset, type.lexeme); |
| 1015 | 456 |
| 1016 @override | 457 /** |
| 1017 int advance() { | 458 * Initialize a newly created token. |
| 1018 if (++_charOffset < _stringLength) { | 459 * |
| 1019 return _string.codeUnitAt(_charOffset); | 460 * @param type the token type (not `null`) |
| 1020 } | 461 * @param offset the offset from the beginning of the file to the first charac
ter in the token |
| 1021 _charOffset = _stringLength; | 462 * @param value the lexeme represented by this token (not `null`) |
| 1022 return -1; | 463 */ |
| 1023 } | 464 Token.con2(this.type, this.offset, String value) { |
| 1024 | 465 this._value = StringUtilities.intern(value); |
| 1025 @override | 466 } |
| 1026 String getString(int start, int endDelta) => _string.substring(start, _charOff
set + 1 + endDelta).toString(); | 467 |
| 1027 | 468 /** |
| 1028 @override | 469 * Return the offset from the beginning of the file to the character after las
t character of the |
| 1029 int peek() { | 470 * token. |
| 1030 if (_charOffset + 1 < _stringLength) { | 471 * |
| 1031 return _string.codeUnitAt(_charOffset + 1); | 472 * @return the offset from the beginning of the file to the first character af
ter last character |
| 1032 } | 473 * of the token |
| 1033 return -1; | 474 */ |
| 1034 } | 475 int get end => offset + length; |
| 476 |
| 477 /** |
| 478 * Return the number of characters in the node's source range. |
| 479 * |
| 480 * @return the number of characters in the node's source range |
| 481 */ |
| 482 int get length => lexeme.length; |
| 483 |
| 484 /** |
| 485 * Return the lexeme that represents this token. |
| 486 * |
| 487 * @return the lexeme (not `null`) |
| 488 */ |
| 489 String get lexeme => _value; |
| 490 |
| 491 /** |
| 492 * Return the next token in the token stream. |
| 493 * |
| 494 * @return the next token in the token stream |
| 495 */ |
| 496 Token get next => _next; |
| 497 |
| 498 /** |
| 499 * Return `true` if this token is a synthetic token. A synthetic token is a to
ken that was |
| 500 * introduced by the parser in order to recover from an error in the code. Syn
thetic tokens always |
| 501 * have a length of zero (`0`). |
| 502 * |
| 503 * @return `true` if this token is a synthetic token |
| 504 */ |
| 505 bool get isSynthetic => length == 0; |
| 506 |
| 507 /** |
| 508 * Set the next token in the token stream to the given token. This has the sid
e-effect of setting |
| 509 * this token to be the previous token for the given token. |
| 510 * |
| 511 * @param token the next token in the token stream |
| 512 * @return the token that was passed in |
| 513 */ |
| 514 Token setNext(Token token) { |
| 515 _next = token; |
| 516 token.previous = this; |
| 517 return token; |
| 518 } |
| 519 |
| 520 @override |
| 521 String toString() => lexeme; |
| 1035 } | 522 } |
| 1036 | 523 |
| 1037 /** | 524 /** |
| 1038 * Instances of the class `ToSourceVisitor` write a source representation of a v
isited XML | 525 * Instances of `XmlTagNode` represent XML or HTML elements such as `` and |
| 1039 * node (and all of it's children) to a writer. | 526 * `<body foo="bar"> ... </body>`. |
| 1040 */ | 527 */ |
| 1041 class ToSourceVisitor implements XmlVisitor<Object> { | 528 class XmlTagNode extends XmlNode { |
| 1042 /** | 529 /** |
| 1043 * The writer to which the source is to be written. | 530 * Constant representing empty list of attributes. |
| 1044 */ | 531 */ |
| 1045 final PrintWriter _writer; | 532 static List<XmlAttributeNode> NO_ATTRIBUTES = new UnmodifiableListView(new Lis
t<XmlAttributeNode>()); |
| 1046 | 533 |
| 1047 /** | 534 /** |
| 1048 * Initialize a newly created visitor to write source code representing the vi
sited nodes to the | 535 * Constant representing empty list of tag nodes. |
| 1049 * given writer. | 536 */ |
| 1050 * | 537 static List<XmlTagNode> NO_TAG_NODES = new UnmodifiableListView(new List<XmlTa
gNode>()); |
| 1051 * @param writer the writer to which the source is to be written | 538 |
| 1052 */ | 539 /** |
| 1053 ToSourceVisitor(this._writer); | 540 * The starting [TokenType#LT] token (not `null`). |
| 1054 | 541 */ |
| 1055 @override | 542 final Token nodeStart; |
| 1056 Object visitHtmlScriptTagNode(HtmlScriptTagNode node) => visitXmlTagNode(node)
; | 543 |
| 1057 | 544 /** |
| 1058 @override | 545 * The [TokenType#TAG] token after the starting '<' (not `null`). |
| 1059 Object visitHtmlUnit(HtmlUnit node) { | 546 */ |
| 1060 for (XmlTagNode child in node.tagNodes) { | 547 final Token _tag; |
| 1061 _visit(child); | 548 |
| 549 /** |
| 550 * The attributes contained by the receiver (not `null`, contains no `null`s). |
| 551 */ |
| 552 List<XmlAttributeNode> _attributes; |
| 553 |
| 554 /** |
| 555 * The [TokenType#GT] or [TokenType#SLASH_GT] token after the attributes (not |
| 556 * `null`). The token may be the same token as [nodeEnd] if there are no child |
| 557 * [tagNodes]. |
| 558 */ |
| 559 final Token attributeEnd; |
| 560 |
| 561 /** |
| 562 * The tag nodes contained in the receiver (not `null`, contains no `null`s). |
| 563 */ |
| 564 List<XmlTagNode> _tagNodes; |
| 565 |
| 566 /** |
| 567 * The token (not `null`) after the content, which may be |
| 568 * * (1) [TokenType#LT_SLASH] for nodes with open and close tags, or |
| 569 * * (2) the [TokenType#LT] nodeStart of the next sibling node if this node is
self |
| 570 * closing or the attributeEnd is [TokenType#SLASH_GT], or |
| 571 * * (3) [TokenType#EOF] if the node does not have a closing tag and is the la
st node in |
| 572 * the stream [TokenType#LT_SLASH] token after the content, or `null` if there
is no |
| 573 * content and the attributes ended with [TokenType#SLASH_GT]. |
| 574 */ |
| 575 final Token contentEnd; |
| 576 |
| 577 /** |
| 578 * The closing [TokenType#TAG] after the child elements or `null` if there is
no |
| 579 * content and the attributes ended with [TokenType#SLASH_GT] |
| 580 */ |
| 581 final Token closingTag; |
| 582 |
| 583 /** |
| 584 * The ending [TokenType#GT] or [TokenType#SLASH_GT] token (not `null`). |
| 585 */ |
| 586 final Token nodeEnd; |
| 587 |
| 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 '<' (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 } |
| 1062 } | 636 } |
| 1063 return null; | 637 return null; |
| 1064 } | 638 } |
| 1065 | 639 |
| 1066 @override | 640 /** |
| 1067 Object visitXmlAttributeNode(XmlAttributeNode node) { | 641 * Answer the receiver's attributes. Callers should not manipulate the returne
d list to edit the |
| 1068 String name = node.name; | 642 * AST structure. |
| 1069 Token value = node.valueToken; | 643 * |
| 1070 if (name.length == 0) { | 644 * @return the attributes (not `null`, contains no `null`s) |
| 1071 _writer.print("__"); | 645 */ |
| 1072 } else { | 646 List<XmlAttributeNode> get attributes => _attributes; |
| 1073 _writer.print(name); | 647 |
| 1074 } | 648 /** |
| 1075 _writer.print("="); | 649 * Find the attribute with the given name (see [getAttribute] and answer the l
exeme |
| 1076 if (value == null) { | 650 * for the attribute's value token without the leading and trailing quotes (se
e |
| 1077 _writer.print("__"); | 651 * [XmlAttributeNode#getText]). |
| 1078 } else { | 652 * |
| 1079 _writer.print(value.lexeme); | 653 * @param name the attribute name |
| 1080 } | 654 * @return the attribute text or `null` if no matching attribute is found |
| 1081 return null; | 655 */ |
| 1082 } | 656 String getAttributeText(String name) { |
| 1083 | 657 XmlAttributeNode attribute = getAttribute(name); |
| 1084 @override | 658 return attribute != null ? attribute.text : null; |
| 1085 Object visitXmlTagNode(XmlTagNode node) { | 659 } |
| 1086 _writer.print("<"); | 660 |
| 1087 String tagName = node.tag; | 661 @override |
| 1088 _writer.print(tagName); | 662 Token get beginToken => nodeStart; |
| 1089 for (XmlAttributeNode attribute in node.attributes) { | 663 |
| 1090 _writer.print(" "); | 664 /** |
| 1091 _visit(attribute); | 665 * Answer a string representing the content contained in the receiver. This in
cludes the textual |
| 1092 } | 666 * representation of any child tag nodes ([getTagNodes]). Whitespace between '
<', |
| 1093 _writer.print(node.attributeEnd.lexeme); | 667 * '</', and '>', '/>' is discarded, but all other whitespace is preserved. |
| 1094 if (node.closingTag != null) { | 668 * |
| 1095 for (XmlTagNode child in node.tagNodes) { | 669 * @return the content (not `null`) |
| 1096 _visit(child); | 670 */ |
| 1097 } | 671 String get content { |
| 1098 _writer.print("</"); | 672 Token token = attributeEnd.next; |
| 1099 _writer.print(tagName); | 673 if (identical(token, contentEnd)) { |
| 1100 _writer.print(">"); | 674 return ""; |
| 1101 } | 675 } |
| 1102 return null; | 676 //TODO (danrubel): handle CDATA and replace HTML character encodings with th
e actual characters |
| 1103 } | 677 String content = token.lexeme; |
| 1104 | 678 token = token.next; |
| 1105 /** | 679 if (identical(token, contentEnd)) { |
| 1106 * Safely visit the given node. | 680 return content; |
| 1107 * | 681 } |
| 1108 * @param node the node to be visited | 682 JavaStringBuilder buffer = new JavaStringBuilder(); |
| 1109 */ | 683 while (!identical(token, contentEnd)) { |
| 1110 void _visit(XmlNode node) { | 684 buffer.append(token.lexeme); |
| 1111 if (node != null) { | 685 token = token.next; |
| 1112 node.accept(this); | 686 } |
| 1113 } | 687 return buffer.toString(); |
| 1114 } | 688 } |
| 1115 } | 689 |
| 1116 | 690 @override |
| 1117 /** | 691 Token get endToken { |
| 1118 * The enumeration `TokenType` defines the types of tokens that can be returned
by the | 692 if (nodeEnd != null) { |
| 1119 * scanner. | 693 return nodeEnd; |
| 1120 */ | 694 } |
| 1121 class TokenType extends Enum<TokenType> { | 695 if (closingTag != null) { |
| 1122 /** | 696 return closingTag; |
| 1123 * The type of the token that marks the end of the input. | 697 } |
| 1124 */ | 698 if (contentEnd != null) { |
| 1125 static const TokenType EOF = const TokenType_EOF('EOF', 0, ""); | 699 return contentEnd; |
| 1126 | 700 } |
| 1127 static const TokenType EQ = const TokenType('EQ', 1, "="); | 701 if (!_tagNodes.isEmpty) { |
| 1128 | 702 return _tagNodes[_tagNodes.length - 1].endToken; |
| 1129 static const TokenType GT = const TokenType('GT', 2, ">"); | 703 } |
| 1130 | 704 if (attributeEnd != null) { |
| 1131 static const TokenType LT_SLASH = const TokenType('LT_SLASH', 3, "</"); | 705 return attributeEnd; |
| 1132 | 706 } |
| 1133 static const TokenType LT = const TokenType('LT', 4, "<"); | 707 if (!_attributes.isEmpty) { |
| 1134 | 708 return _attributes[_attributes.length - 1].endToken; |
| 1135 static const TokenType SLASH_GT = const TokenType('SLASH_GT', 5, "/>"); | 709 } |
| 1136 | 710 return _tag; |
| 1137 static const TokenType COMMENT = const TokenType('COMMENT', 6, null); | 711 } |
| 1138 | 712 |
| 1139 static const TokenType DECLARATION = const TokenType('DECLARATION', 7, null); | 713 /** |
| 1140 | 714 * Answer the tag name after the starting '<'. |
| 1141 static const TokenType DIRECTIVE = const TokenType('DIRECTIVE', 8, null); | 715 * |
| 1142 | 716 * @return the tag name (not `null`) |
| 1143 static const TokenType STRING = const TokenType('STRING', 9, null); | 717 */ |
| 1144 | 718 String get tag => _tag.lexeme; |
| 1145 static const TokenType TAG = const TokenType('TAG', 10, null); | 719 |
| 1146 | 720 /** |
| 1147 static const TokenType TEXT = const TokenType('TEXT', 11, null); | 721 * Answer the tag nodes contained in the receiver. Callers should not manipula
te the returned list |
| 1148 | 722 * to edit the AST structure. |
| 1149 static const List<TokenType> values = const [ | 723 * |
| 1150 EOF, | 724 * @return the children (not `null`, contains no `null`s) |
| 1151 EQ, | 725 */ |
| 1152 GT, | 726 List<XmlTagNode> get tagNodes => _tagNodes; |
| 1153 LT_SLASH, | 727 |
| 1154 LT, | 728 /** |
| 1155 SLASH_GT, | 729 * Answer the [TokenType#TAG] token after the starting '<'. |
| 1156 COMMENT, | 730 * |
| 1157 DECLARATION, | 731 * @return the token (not `null`) |
| 1158 DIRECTIVE, | 732 */ |
| 1159 STRING, | 733 Token get tagToken => _tag; |
| 1160 TAG, | |
| 1161 TEXT]; | |
| 1162 | |
| 1163 /** | |
| 1164 * The lexeme that defines this type of token, or `null` if there is more than
one possible | |
| 1165 * lexeme for this type of token. | |
| 1166 */ | |
| 1167 final String lexeme; | |
| 1168 | |
| 1169 const TokenType(String name, int ordinal, this.lexeme) : super(name, ordinal); | |
| 1170 } | |
| 1171 | |
| 1172 class TokenType_EOF extends TokenType { | |
| 1173 const TokenType_EOF(String name, int ordinal, String arg0) : super(name, ordin
al, arg0); | |
| 1174 | |
| 1175 @override | |
| 1176 String toString() => "-eof-"; | |
| 1177 } | |
| 1178 | |
| 1179 /** | |
| 1180 * Instances of `XmlAttributeNode` represent name/value pairs owned by an [XmlTa
gNode]. | |
| 1181 */ | |
| 1182 class XmlAttributeNode extends XmlNode { | |
| 1183 final Token _name; | |
| 1184 | |
| 1185 final Token equals; | |
| 1186 | |
| 1187 final Token _value; | |
| 1188 | |
| 1189 List<XmlExpression> expressions = XmlExpression.EMPTY_ARRAY; | |
| 1190 | |
| 1191 /** | |
| 1192 * Construct a new instance representing an XML attribute. | |
| 1193 * | |
| 1194 * @param name the name token (not `null`). This may be a zero length token if
the attribute | |
| 1195 * is badly formed. | |
| 1196 * @param equals the equals sign or `null` if none | |
| 1197 * @param value the value token (not `null`) | |
| 1198 */ | |
| 1199 XmlAttributeNode(this._name, this.equals, this._value); | |
| 1200 | |
| 1201 @override | |
| 1202 accept(XmlVisitor visitor) => visitor.visitXmlAttributeNode(this); | |
| 1203 | |
| 1204 @override | |
| 1205 Token get beginToken => _name; | |
| 1206 | |
| 1207 @override | |
| 1208 Token get endToken => _value; | |
| 1209 | |
| 1210 /** | |
| 1211 * Answer the attribute name. This may be a zero length string if the attribut
e is badly formed. | |
| 1212 * | |
| 1213 * @return the name (not `null`) | |
| 1214 */ | |
| 1215 String get name => _name.lexeme; | |
| 1216 | |
| 1217 /** | |
| 1218 * Answer the attribute name token. This may be a zero length token if the att
ribute is badly | |
| 1219 * formed. | |
| 1220 * | |
| 1221 * @return the name token (not `null`) | |
| 1222 */ | |
| 1223 Token get nameToken => _name; | |
| 1224 | |
| 1225 /** | |
| 1226 * Answer the lexeme for the value token without the leading and trailing quot
es. | |
| 1227 * | |
| 1228 * @return the text or `null` if the value is not specified | |
| 1229 */ | |
| 1230 String get text { | |
| 1231 if (_value == null) { | |
| 1232 return null; | |
| 1233 } | |
| 1234 //TODO (danrubel): replace HTML character encodings with the actual characte
rs | |
| 1235 String text = _value.lexeme; | |
| 1236 int len = text.length; | |
| 1237 if (len > 0) { | |
| 1238 if (text.codeUnitAt(0) == 0x22) { | |
| 1239 if (len > 1 && text.codeUnitAt(len - 1) == 0x22) { | |
| 1240 return text.substring(1, len - 1); | |
| 1241 } else { | |
| 1242 return text.substring(1); | |
| 1243 } | |
| 1244 } else if (text.codeUnitAt(0) == 0x27) { | |
| 1245 if (len > 1 && text.codeUnitAt(len - 1) == 0x27) { | |
| 1246 return text.substring(1, len - 1); | |
| 1247 } else { | |
| 1248 return text.substring(1); | |
| 1249 } | |
| 1250 } | |
| 1251 } | |
| 1252 return text; | |
| 1253 } | |
| 1254 | |
| 1255 /** | |
| 1256 * Answer the offset of the value after the leading quote. | |
| 1257 * | |
| 1258 * @return the offset of the value, or `-1` if the value is not specified | |
| 1259 */ | |
| 1260 int get textOffset { | |
| 1261 if (_value == null) { | |
| 1262 return -1; | |
| 1263 } | |
| 1264 String text = _value.lexeme; | |
| 1265 if (StringUtilities.startsWithChar(text, 0x22) || StringUtilities.startsWith
Char(text, 0x27)) { | |
| 1266 return _value.offset + 1; | |
| 1267 } | |
| 1268 return _value.offset; | |
| 1269 } | |
| 1270 | |
| 1271 /** | |
| 1272 * Answer the attribute value token. A properly formed value will start and en
d with matching | |
| 1273 * quote characters, but the value returned may not be properly formed. | |
| 1274 * | |
| 1275 * @return the value token or `null` if this represents a badly formed attribu
te | |
| 1276 */ | |
| 1277 Token get valueToken => _value; | |
| 1278 | 734 |
| 1279 @override | 735 @override |
| 1280 void visitChildren(XmlVisitor visitor) { | 736 void visitChildren(XmlVisitor visitor) { |
| 1281 } | 737 for (XmlAttributeNode node in _attributes) { |
| 1282 } | 738 node.accept(visitor); |
| 1283 | 739 } |
| 1284 /** | 740 for (XmlTagNode node in _tagNodes) { |
| 1285 * The interface `XmlVisitor` defines the behavior of objects that can be used t
o visit an | 741 node.accept(visitor); |
| 1286 * [XmlNode] structure. | 742 } |
| 1287 */ | |
| 1288 abstract class XmlVisitor<R> { | |
| 1289 R visitHtmlScriptTagNode(HtmlScriptTagNode node); | |
| 1290 | |
| 1291 R visitHtmlUnit(HtmlUnit htmlUnit); | |
| 1292 | |
| 1293 R visitXmlAttributeNode(XmlAttributeNode xmlAttributeNode); | |
| 1294 | |
| 1295 R visitXmlTagNode(XmlTagNode xmlTagNode); | |
| 1296 } | |
| 1297 | |
| 1298 /** | |
| 1299 * Instances of the class `XmlExpression` represent an abstract expression embed
ded into | |
| 1300 * [XmlNode]. | |
| 1301 */ | |
| 1302 abstract class XmlExpression { | |
| 1303 /** | |
| 1304 * An empty array of expressions. | |
| 1305 */ | |
| 1306 static List<XmlExpression> EMPTY_ARRAY = new List<XmlExpression>(0); | |
| 1307 | |
| 1308 /** | |
| 1309 * Check if the given offset belongs to the expression's source range. | |
| 1310 */ | |
| 1311 bool contains(int offset) => this.offset <= offset && offset < end; | |
| 1312 | |
| 1313 /** | |
| 1314 * Return the offset of the character immediately following the last character
of this | |
| 1315 * expression's source range. This is equivalent to `getOffset() + getLength()
`. | |
| 1316 * | |
| 1317 * @return the offset of the character just past the expression's source range | |
| 1318 */ | |
| 1319 int get end; | |
| 1320 | |
| 1321 /** | |
| 1322 * Return the number of characters in the expression's source range. | |
| 1323 */ | |
| 1324 int get length; | |
| 1325 | |
| 1326 /** | |
| 1327 * Return the offset of the first character in the expression's source range. | |
| 1328 */ | |
| 1329 int get offset; | |
| 1330 | |
| 1331 /** | |
| 1332 * Return the [Reference] at the given offset. | |
| 1333 * | |
| 1334 * @param offset the offset from the beginning of the file | |
| 1335 * @return the [Reference] at the given offset, maybe `null` | |
| 1336 */ | |
| 1337 XmlExpression_Reference getReference(int offset); | |
| 1338 } | |
| 1339 | |
| 1340 /** | |
| 1341 * The reference to the [Element]. | |
| 1342 */ | |
| 1343 class XmlExpression_Reference { | |
| 1344 Element element; | |
| 1345 | |
| 1346 int offset = 0; | |
| 1347 | |
| 1348 int length = 0; | |
| 1349 | |
| 1350 XmlExpression_Reference(Element element, int offset, int length) { | |
| 1351 this.element = element; | |
| 1352 this.offset = offset; | |
| 1353 this.length = length; | |
| 1354 } | 743 } |
| 1355 } | 744 } |
| 1356 | 745 |
| 1357 /** | 746 /** |
| 1358 * Instances of the class `XmlParser` are used to parse tokens into a AST struct
ure comprised | 747 * Instances of the class `XmlParser` are used to parse tokens into a AST struct
ure comprised |
| 1359 * of [XmlNode]s. | 748 * of [XmlNode]s. |
| 1360 */ | 749 */ |
| 1361 class XmlParser { | 750 class XmlParser { |
| 1362 /** | 751 /** |
| 1363 * The source being parsed. | 752 * The source being parsed. |
| (...skipping 242 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1606 } | 995 } |
| 1607 | 996 |
| 1608 /** | 997 /** |
| 1609 * Report the current token as unexpected | 998 * Report the current token as unexpected |
| 1610 */ | 999 */ |
| 1611 void _reportUnexpectedToken() { | 1000 void _reportUnexpectedToken() { |
| 1612 } | 1001 } |
| 1613 } | 1002 } |
| 1614 | 1003 |
| 1615 /** | 1004 /** |
| 1616 * Instances of `XmlTagNode` represent XML or HTML elements such as `` and | 1005 * The abstract class `XmlNode` defines behavior common to all XML/HTML nodes. |
| 1617 * `<body foo="bar"> ... </body>`. | |
| 1618 */ | 1006 */ |
| 1619 class XmlTagNode extends XmlNode { | 1007 abstract class XmlNode { |
| 1620 /** | 1008 /** |
| 1621 * Constant representing empty list of attributes. | 1009 * The parent of the node, or `null` if the node is the root of an AST structu
re. |
| 1622 */ | 1010 */ |
| 1623 static List<XmlAttributeNode> NO_ATTRIBUTES = new UnmodifiableListView(new Lis
t<XmlAttributeNode>()); | 1011 XmlNode _parent; |
| 1624 | 1012 |
| 1625 /** | 1013 /** |
| 1626 * Constant representing empty list of tag nodes. | 1014 * The element associated with this node or `null` if the receiver is not reso
lved. |
| 1627 */ | 1015 */ |
| 1628 static List<XmlTagNode> NO_TAG_NODES = new UnmodifiableListView(new List<XmlTa
gNode>()); | 1016 Element _element; |
| 1629 | 1017 |
| 1630 /** | 1018 /** |
| 1631 * The starting [TokenType#LT] token (not `null`). | 1019 * Use the given visitor to visit this node. |
| 1632 */ | 1020 * |
| 1633 final Token nodeStart; | 1021 * @param visitor the visitor that will visit this node |
| 1634 | 1022 * @return the value returned by the visitor as a result of visiting this node |
| 1635 /** | 1023 */ |
| 1636 * The [TokenType#TAG] token after the starting '<' (not `null`). | 1024 accept(XmlVisitor visitor); |
| 1637 */ | 1025 |
| 1638 final Token _tag; | 1026 /** |
| 1639 | 1027 * Return the first token included in this node's source range. |
| 1640 /** | 1028 * |
| 1641 * The attributes contained by the receiver (not `null`, contains no `null`s). | 1029 * @return the first token or `null` if none |
| 1642 */ | 1030 */ |
| 1643 List<XmlAttributeNode> _attributes; | 1031 Token get beginToken; |
| 1644 | 1032 |
| 1645 /** | 1033 /** |
| 1646 * The [TokenType#GT] or [TokenType#SLASH_GT] token after the attributes (not | 1034 * Return the element associated with this node. |
| 1647 * `null`). The token may be the same token as [nodeEnd] if there are no child | 1035 * |
| 1648 * [tagNodes]. | 1036 * @return the element or `null` if the receiver is not resolved |
| 1649 */ | 1037 */ |
| 1650 final Token attributeEnd; | 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 } |
| 1210 |
| 1211 /** |
| 1212 * Implementation of [XmlExpression] for an [Expression] embedded without any wr
apping |
| 1213 * characters. |
| 1214 */ |
| 1215 class RawXmlExpression extends XmlExpression { |
| 1216 final Expression expression; |
| 1217 |
| 1218 RawXmlExpression(this.expression); |
| 1219 |
| 1220 @override |
| 1221 int get end => expression.end; |
| 1222 |
| 1223 @override |
| 1224 int get length => expression.length; |
| 1225 |
| 1226 @override |
| 1227 int get offset => expression.offset; |
| 1228 |
| 1229 @override |
| 1230 XmlExpression_Reference getReference(int offset) { |
| 1231 AstNode node = new NodeLocator.con1(offset).searchWithin(expression); |
| 1232 if (node != null) { |
| 1233 Element element = ElementLocator.locate(node); |
| 1234 return new XmlExpression_Reference(element, node.offset, node.length); |
| 1235 } |
| 1236 return null; |
| 1237 } |
| 1238 } |
| 1239 |
| 1240 /** |
| 1241 * Utilities locating [Expression]s and [Element]s in [HtmlUnit]. |
| 1242 */ |
| 1243 class HtmlUnitUtils { |
| 1244 /** |
| 1245 * Returns the [XmlAttributeNode] that is part of the given [HtmlUnit] and enc
loses |
| 1246 * the given offset. |
| 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; |
| 1259 } |
| 1260 |
| 1261 /** |
| 1262 * Returns the best [Element] of the given [Expression]. |
| 1263 */ |
| 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; |
| 1309 } |
| 1310 |
| 1311 /** |
| 1312 * Returns the [Expression] that is part of the given [HtmlUnit] and encloses
the |
| 1313 * given offset. |
| 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; |
| 1327 } |
| 1328 |
| 1329 /** |
| 1330 * Returns the [XmlTagNode] that is part of the given [HtmlUnit] and its open
or |
| 1331 * closing tag name encloses the given offset. |
| 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; |
| 1351 } |
| 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 } |
| 1376 |
| 1377 class RecursiveXmlVisitor_HtmlUnitUtils_getAttributeNode extends RecursiveXmlVis
itor<Object> { |
| 1378 int offset = 0; |
| 1379 |
| 1380 List<XmlAttributeNode> result; |
| 1381 |
| 1382 RecursiveXmlVisitor_HtmlUnitUtils_getAttributeNode(this.offset, this.result) :
super(); |
| 1383 |
| 1384 @override |
| 1385 Object visitXmlAttributeNode(XmlAttributeNode node) { |
| 1386 Token nameToken = node.nameToken; |
| 1387 if (nameToken.offset <= offset && offset <= nameToken.end) { |
| 1388 result[0] = node; |
| 1389 throw new HtmlUnitUtils_FoundAttributeNodeError(); |
| 1390 } |
| 1391 return super.visitXmlAttributeNode(node); |
| 1392 } |
| 1393 } |
| 1394 |
| 1395 class RecursiveXmlVisitor_HtmlUnitUtils_getEnclosingTagNode extends RecursiveXml
Visitor<Object> { |
| 1396 int offset = 0; |
| 1397 |
| 1398 List<XmlTagNode> result; |
| 1399 |
| 1400 RecursiveXmlVisitor_HtmlUnitUtils_getEnclosingTagNode(this.offset, this.result
) : super(); |
| 1401 |
| 1402 @override |
| 1403 Object visitXmlTagNode(XmlTagNode node) { |
| 1404 if (node.offset <= offset && offset < node.end) { |
| 1405 result[0] = node; |
| 1406 super.visitXmlTagNode(node); |
| 1407 throw new HtmlUnitUtils_FoundTagNodeError(); |
| 1408 } |
| 1409 return null; |
| 1410 } |
| 1411 } |
| 1412 |
| 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 /** |
| 1431 * The interface `XmlVisitor` defines the behavior of objects that can be used t
o visit an |
| 1432 * [XmlNode] structure. |
| 1433 */ |
| 1434 abstract class XmlVisitor<R> { |
| 1435 R visitHtmlScriptTagNode(HtmlScriptTagNode node); |
| 1436 |
| 1437 R visitHtmlUnit(HtmlUnit htmlUnit); |
| 1438 |
| 1439 R visitXmlAttributeNode(XmlAttributeNode xmlAttributeNode); |
| 1440 |
| 1441 R visitXmlTagNode(XmlTagNode xmlTagNode); |
| 1442 } |
| 1443 |
| 1444 /** |
| 1445 * 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. |
| 1447 */ |
| 1448 class ToSourceVisitor implements XmlVisitor<Object> { |
| 1449 /** |
| 1450 * The writer to which the source is to be written. |
| 1451 */ |
| 1452 final PrintWriter _writer; |
| 1453 |
| 1454 /** |
| 1455 * Initialize a newly created visitor to write source code representing the vi
sited nodes to the |
| 1456 * given writer. |
| 1457 * |
| 1458 * @param writer the writer to which the source is to be written |
| 1459 */ |
| 1460 ToSourceVisitor(this._writer); |
| 1461 |
| 1462 @override |
| 1463 Object visitHtmlScriptTagNode(HtmlScriptTagNode node) => visitXmlTagNode(node)
; |
| 1464 |
| 1465 @override |
| 1466 Object visitHtmlUnit(HtmlUnit node) { |
| 1467 for (XmlTagNode child in node.tagNodes) { |
| 1468 _visit(child); |
| 1469 } |
| 1470 return null; |
| 1471 } |
| 1472 |
| 1473 @override |
| 1474 Object visitXmlAttributeNode(XmlAttributeNode node) { |
| 1475 String name = node.name; |
| 1476 Token value = node.valueToken; |
| 1477 if (name.length == 0) { |
| 1478 _writer.print("__"); |
| 1479 } else { |
| 1480 _writer.print(name); |
| 1481 } |
| 1482 _writer.print("="); |
| 1483 if (value == null) { |
| 1484 _writer.print("__"); |
| 1485 } else { |
| 1486 _writer.print(value.lexeme); |
| 1487 } |
| 1488 return null; |
| 1489 } |
| 1490 |
| 1491 @override |
| 1492 Object visitXmlTagNode(XmlTagNode node) { |
| 1493 _writer.print("<"); |
| 1494 String tagName = node.tag; |
| 1495 _writer.print(tagName); |
| 1496 for (XmlAttributeNode attribute in node.attributes) { |
| 1497 _writer.print(" "); |
| 1498 _visit(attribute); |
| 1499 } |
| 1500 _writer.print(node.attributeEnd.lexeme); |
| 1501 if (node.closingTag != null) { |
| 1502 for (XmlTagNode child in node.tagNodes) { |
| 1503 _visit(child); |
| 1504 } |
| 1505 _writer.print("</"); |
| 1506 _writer.print(tagName); |
| 1507 _writer.print(">"); |
| 1508 } |
| 1509 return null; |
| 1510 } |
| 1511 |
| 1512 /** |
| 1513 * Safely visit the given node. |
| 1514 * |
| 1515 * @param node the node to be visited |
| 1516 */ |
| 1517 void _visit(XmlNode node) { |
| 1518 if (node != null) { |
| 1519 node.accept(this); |
| 1520 } |
| 1521 } |
| 1522 } |
| 1523 |
| 1524 /** |
| 1525 * Instances of the class `HtmlUnit` represent the contents of an HTML file. |
| 1526 */ |
| 1527 class HtmlUnit extends XmlNode { |
| 1528 /** |
| 1529 * The first token in the token stream that was parsed to form this HTML unit. |
| 1530 */ |
| 1531 final Token beginToken; |
| 1532 |
| 1533 /** |
| 1534 * The last token in the token stream that was parsed to form this compilation
unit. This token |
| 1535 * should always have a type of [TokenType.EOF]. |
| 1536 */ |
| 1537 final Token endToken; |
| 1651 | 1538 |
| 1652 /** | 1539 /** |
| 1653 * The tag nodes contained in the receiver (not `null`, contains no `null`s). | 1540 * The tag nodes contained in the receiver (not `null`, contains no `null`s). |
| 1654 */ | 1541 */ |
| 1655 List<XmlTagNode> _tagNodes; | 1542 List<XmlTagNode> _tagNodes; |
| 1656 | 1543 |
| 1657 /** | 1544 /** |
| 1658 * The token (not `null`) after the content, which may be | 1545 * Construct a new instance representing the content of an HTML file. |
| 1659 * * (1) [TokenType#LT_SLASH] for nodes with open and close tags, or | 1546 * |
| 1660 * * (2) the [TokenType#LT] nodeStart of the next sibling node if this node is
self | 1547 * @param beginToken the first token in the file (not `null`) |
| 1661 * closing or the attributeEnd is [TokenType#SLASH_GT], or | 1548 * @param tagNodes child tag nodes of the receiver (not `null`, contains no `n
ull`s) |
| 1662 * * (3) [TokenType#EOF] if the node does not have a closing tag and is the la
st node in | 1549 * @param endToken the last token in the token stream which should be of type |
| 1663 * the stream [TokenType#LT_SLASH] token after the content, or `null` if there
is no | 1550 * [TokenType.EOF] |
| 1664 * content and the attributes ended with [TokenType#SLASH_GT]. | |
| 1665 */ | 1551 */ |
| 1666 final Token contentEnd; | 1552 HtmlUnit(this.beginToken, List<XmlTagNode> tagNodes, this.endToken) { |
| 1667 | 1553 this._tagNodes = becomeParentOfAll(tagNodes); |
| 1668 /** | |
| 1669 * The closing [TokenType#TAG] after the child elements or `null` if there is
no | |
| 1670 * content and the attributes ended with [TokenType#SLASH_GT] | |
| 1671 */ | |
| 1672 final Token closingTag; | |
| 1673 | |
| 1674 /** | |
| 1675 * The ending [TokenType#GT] or [TokenType#SLASH_GT] token (not `null`). | |
| 1676 */ | |
| 1677 final Token nodeEnd; | |
| 1678 | |
| 1679 /** | |
| 1680 * The expressions that are embedded in the tag's content. | |
| 1681 */ | |
| 1682 List<XmlExpression> expressions = XmlExpression.EMPTY_ARRAY; | |
| 1683 | |
| 1684 /** | |
| 1685 * Construct a new instance representing an XML or HTML element | |
| 1686 * | |
| 1687 * @param nodeStart the starting [TokenType#LT] token (not `null`) | |
| 1688 * @param tag the [TokenType#TAG] token after the starting '<' (not `null`)
. | |
| 1689 * @param attributes the attributes associated with this element or [NO_ATTRIB
UTES] (not | |
| 1690 * `null`, contains no `null`s) | |
| 1691 * @param attributeEnd The [TokenType#GT] or [TokenType#SLASH_GT] token after
the | |
| 1692 * attributes (not `null`). The token may be the same token as [nodeE
nd] if | |
| 1693 * there are no child [tagNodes]. | |
| 1694 * @param tagNodes child tag nodes of the receiver or [NO_TAG_NODES] (not `nul
l`, | |
| 1695 * contains no `null`s) | |
| 1696 * @param contentEnd the token (not `null`) after the content, which may be | |
| 1697 * * (1) [TokenType#LT_SLASH] for nodes with open and close tags, or | |
| 1698 * * (2) the [TokenType#LT] nodeStart of the next sibling node if thi
s node is | |
| 1699 * self closing or the attributeEnd is [TokenType#SLASH_GT], or | |
| 1700 * * (3) [TokenType#EOF] if the node does not have a closing tag and
is the last | |
| 1701 * node in the stream [TokenType#LT_SLASH] token after the content, o
r `null` | |
| 1702 * if there is no content and the attributes ended with [TokenType#SL
ASH_GT]. | |
| 1703 * @param closingTag the closing [TokenType#TAG] after the child elements or `
null` if | |
| 1704 * there is no content and the attributes ended with [TokenType#SLASH
_GT] | |
| 1705 * @param nodeEnd the ending [TokenType#GT] or [TokenType#SLASH_GT] token (not | |
| 1706 * `null`) | |
| 1707 */ | |
| 1708 XmlTagNode(this.nodeStart, this._tag, List<XmlAttributeNode> attributes, this.
attributeEnd, List<XmlTagNode> tagNodes, this.contentEnd, this.closingTag, this.
nodeEnd) { | |
| 1709 this._attributes = becomeParentOfAll(attributes, ifEmpty: NO_ATTRIBUTES); | |
| 1710 this._tagNodes = becomeParentOfAll(tagNodes, ifEmpty: NO_TAG_NODES); | |
| 1711 } | 1554 } |
| 1712 | 1555 |
| 1713 @override | 1556 @override |
| 1714 accept(XmlVisitor visitor) => visitor.visitXmlTagNode(this); | 1557 accept(XmlVisitor visitor) => visitor.visitHtmlUnit(this); |
| 1715 | 1558 |
| 1716 /** | 1559 /** |
| 1717 * Answer the attribute with the specified name. | 1560 * Return the element associated with this HTML unit. |
| 1718 * | 1561 * |
| 1719 * @param name the attribute name | 1562 * @return the element or `null` if the receiver is not resolved |
| 1720 * @return the attribute or `null` if no matching attribute is found | |
| 1721 */ | 1563 */ |
| 1722 XmlAttributeNode getAttribute(String name) { | |
| 1723 for (XmlAttributeNode attribute in _attributes) { | |
| 1724 if (attribute.name == name) { | |
| 1725 return attribute; | |
| 1726 } | |
| 1727 } | |
| 1728 return null; | |
| 1729 } | |
| 1730 | |
| 1731 /** | |
| 1732 * Answer the receiver's attributes. Callers should not manipulate the returne
d list to edit the | |
| 1733 * AST structure. | |
| 1734 * | |
| 1735 * @return the attributes (not `null`, contains no `null`s) | |
| 1736 */ | |
| 1737 List<XmlAttributeNode> get attributes => _attributes; | |
| 1738 | |
| 1739 /** | |
| 1740 * Find the attribute with the given name (see [getAttribute] and answer the l
exeme | |
| 1741 * for the attribute's value token without the leading and trailing quotes (se
e | |
| 1742 * [XmlAttributeNode#getText]). | |
| 1743 * | |
| 1744 * @param name the attribute name | |
| 1745 * @return the attribute text or `null` if no matching attribute is found | |
| 1746 */ | |
| 1747 String getAttributeText(String name) { | |
| 1748 XmlAttributeNode attribute = getAttribute(name); | |
| 1749 return attribute != null ? attribute.text : null; | |
| 1750 } | |
| 1751 | |
| 1752 @override | 1564 @override |
| 1753 Token get beginToken => nodeStart; | 1565 HtmlElement get element => super.element as HtmlElement; |
| 1754 | |
| 1755 /** | |
| 1756 * Answer a string representing the content contained in the receiver. This in
cludes the textual | |
| 1757 * representation of any child tag nodes ([getTagNodes]). Whitespace between '
<', | |
| 1758 * '</', and '>', '/>' is discarded, but all other whitespace is preserved. | |
| 1759 * | |
| 1760 * @return the content (not `null`) | |
| 1761 */ | |
| 1762 String get content { | |
| 1763 Token token = attributeEnd.next; | |
| 1764 if (identical(token, contentEnd)) { | |
| 1765 return ""; | |
| 1766 } | |
| 1767 //TODO (danrubel): handle CDATA and replace HTML character encodings with th
e actual characters | |
| 1768 String content = token.lexeme; | |
| 1769 token = token.next; | |
| 1770 if (identical(token, contentEnd)) { | |
| 1771 return content; | |
| 1772 } | |
| 1773 JavaStringBuilder buffer = new JavaStringBuilder(); | |
| 1774 while (!identical(token, contentEnd)) { | |
| 1775 buffer.append(token.lexeme); | |
| 1776 token = token.next; | |
| 1777 } | |
| 1778 return buffer.toString(); | |
| 1779 } | |
| 1780 | |
| 1781 @override | |
| 1782 Token get endToken { | |
| 1783 if (nodeEnd != null) { | |
| 1784 return nodeEnd; | |
| 1785 } | |
| 1786 if (closingTag != null) { | |
| 1787 return closingTag; | |
| 1788 } | |
| 1789 if (contentEnd != null) { | |
| 1790 return contentEnd; | |
| 1791 } | |
| 1792 if (!_tagNodes.isEmpty) { | |
| 1793 return _tagNodes[_tagNodes.length - 1].endToken; | |
| 1794 } | |
| 1795 if (attributeEnd != null) { | |
| 1796 return attributeEnd; | |
| 1797 } | |
| 1798 if (!_attributes.isEmpty) { | |
| 1799 return _attributes[_attributes.length - 1].endToken; | |
| 1800 } | |
| 1801 return _tag; | |
| 1802 } | |
| 1803 | |
| 1804 /** | |
| 1805 * Answer the tag name after the starting '<'. | |
| 1806 * | |
| 1807 * @return the tag name (not `null`) | |
| 1808 */ | |
| 1809 String get tag => _tag.lexeme; | |
| 1810 | 1566 |
| 1811 /** | 1567 /** |
| 1812 * Answer the tag nodes contained in the receiver. Callers should not manipula
te the returned list | 1568 * Answer the tag nodes contained in the receiver. Callers should not manipula
te the returned list |
| 1813 * to edit the AST structure. | 1569 * to edit the AST structure. |
| 1814 * | 1570 * |
| 1815 * @return the children (not `null`, contains no `null`s) | 1571 * @return the children (not `null`, contains no `null`s) |
| 1816 */ | 1572 */ |
| 1817 List<XmlTagNode> get tagNodes => _tagNodes; | 1573 List<XmlTagNode> get tagNodes => _tagNodes; |
| 1818 | 1574 |
| 1819 /** | 1575 @override |
| 1820 * Answer the [TokenType#TAG] token after the starting '<'. | 1576 void set element(Element element) { |
| 1821 * | 1577 if (element != null && element is! HtmlElement) { |
| 1822 * @return the token (not `null`) | 1578 throw new IllegalArgumentException("HtmlElement expected, but ${element.ru
ntimeType} given"); |
| 1823 */ | 1579 } |
| 1824 Token get tagToken => _tag; | 1580 super.element = element; |
| 1581 } |
| 1825 | 1582 |
| 1826 @override | 1583 @override |
| 1827 void visitChildren(XmlVisitor visitor) { | 1584 void visitChildren(XmlVisitor visitor) { |
| 1828 for (XmlAttributeNode node in _attributes) { | |
| 1829 node.accept(visitor); | |
| 1830 } | |
| 1831 for (XmlTagNode node in _tagNodes) { | 1585 for (XmlTagNode node in _tagNodes) { |
| 1832 node.accept(visitor); | 1586 node.accept(visitor); |
| 1833 } | 1587 } |
| 1834 } | 1588 } |
| 1835 } | 1589 } |
| 1836 | 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 |
| 1837 /** | 1650 /** |
| 1838 * Instances of the class `HtmlParser` are used to parse tokens into a AST struc
ture comprised | 1651 * Instances of the class `HtmlParser` are used to parse tokens into a AST struc
ture comprised |
| 1839 * of [XmlNode]s. | 1652 * of [XmlNode]s. |
| 1840 */ | 1653 */ |
| 1841 class HtmlParser extends XmlParser { | 1654 class HtmlParser extends XmlParser { |
| 1842 /** | 1655 /** |
| 1843 * The line information associated with the source being parsed. | 1656 * The line information associated with the source being parsed. |
| 1844 */ | 1657 */ |
| 1845 LineInfo _lineInfo; | 1658 LineInfo _lineInfo; |
| 1846 | 1659 |
| (...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1969 return true; | 1782 return true; |
| 1970 } | 1783 } |
| 1971 } | 1784 } |
| 1972 } | 1785 } |
| 1973 } | 1786 } |
| 1974 return false; | 1787 return false; |
| 1975 } | 1788 } |
| 1976 } | 1789 } |
| 1977 | 1790 |
| 1978 /** | 1791 /** |
| 1979 * Instances of the class `HtmlUnit` represent the contents of an HTML file. | 1792 * Instances of the class `RecursiveXmlVisitor` implement an XML visitor that wi
ll recursively |
| 1980 */ | 1793 * visit all of the nodes in an XML structure. For example, using an instance of
this class to visit |
| 1981 class HtmlUnit extends XmlNode { | 1794 * a [XmlTagNode] will also cause all of the contained [XmlAttributeNode]s and |
| 1982 /** | 1795 * [XmlTagNode]s to be visited. |
| 1983 * The first token in the token stream that was parsed to form this HTML unit. | 1796 * |
| 1984 */ | 1797 * Subclasses that override a visit method must either invoke the overridden vis
it method or must |
| 1985 final Token beginToken; | 1798 * explicitly ask the visited node to visit its children. Failure to do so will
cause the children |
| 1986 | 1799 * of the visited node to not be visited. |
| 1987 /** | 1800 */ |
| 1988 * The last token in the token stream that was parsed to form this compilation
unit. This token | 1801 class RecursiveXmlVisitor<R> implements XmlVisitor<R> { |
| 1989 * should always have a type of [TokenType.EOF]. | 1802 @override |
| 1990 */ | 1803 R visitHtmlScriptTagNode(HtmlScriptTagNode node) { |
| 1991 final Token endToken; | 1804 node.visitChildren(this); |
| 1992 | 1805 return null; |
| 1993 /** | 1806 } |
| 1994 * The tag nodes contained in the receiver (not `null`, contains no `null`s). | 1807 |
| 1995 */ | 1808 @override |
| 1996 List<XmlTagNode> _tagNodes; | 1809 R visitHtmlUnit(HtmlUnit node) { |
| 1997 | 1810 node.visitChildren(this); |
| 1998 /** | 1811 return null; |
| 1999 * Construct a new instance representing the content of an HTML file. | 1812 } |
| 2000 * | 1813 |
| 2001 * @param beginToken the first token in the file (not `null`) | 1814 @override |
| 2002 * @param tagNodes child tag nodes of the receiver (not `null`, contains no `n
ull`s) | 1815 R visitXmlAttributeNode(XmlAttributeNode node) { |
| 2003 * @param endToken the last token in the token stream which should be of type | 1816 node.visitChildren(this); |
| 2004 * [TokenType.EOF] | 1817 return null; |
| 2005 */ | 1818 } |
| 2006 HtmlUnit(this.beginToken, List<XmlTagNode> tagNodes, this.endToken) { | 1819 |
| 2007 this._tagNodes = becomeParentOfAll(tagNodes); | 1820 @override |
| 2008 } | 1821 R visitXmlTagNode(XmlTagNode node) { |
| 2009 | 1822 node.visitChildren(this); |
| 2010 @override | 1823 return null; |
| 2011 accept(XmlVisitor visitor) => visitor.visitHtmlUnit(this); | 1824 } |
| 2012 | 1825 } |
| 2013 /** | 1826 |
| 2014 * Return the element associated with this HTML unit. | 1827 /** |
| 2015 * | 1828 * Instances of the class `HtmlScriptTagNode` represent a script tag within an H
TML file that |
| 2016 * @return the element or `null` if the receiver is not resolved | 1829 * references a Dart script. |
| 2017 */ | 1830 */ |
| 2018 @override | 1831 class HtmlScriptTagNode extends XmlTagNode { |
| 2019 HtmlElement get element => super.element as HtmlElement; | 1832 /** |
| 2020 | 1833 * The AST structure representing the Dart code within this tag. |
| 2021 /** | 1834 */ |
| 2022 * Answer the tag nodes contained in the receiver. Callers should not manipula
te the returned list | 1835 CompilationUnit _script; |
| 2023 * to edit the AST structure. | 1836 |
| 2024 * | 1837 /** |
| 2025 * @return the children (not `null`, contains no `null`s) | 1838 * The element representing this script. |
| 2026 */ | 1839 */ |
| 2027 List<XmlTagNode> get tagNodes => _tagNodes; | 1840 HtmlScriptElement scriptElement; |
| 2028 | 1841 |
| 2029 @override | 1842 /** |
| 2030 void set element(Element element) { | 1843 * Initialize a newly created node to represent a script tag within an HTML fi
le that references a |
| 2031 if (element != null && element is! HtmlElement) { | 1844 * Dart script. |
| 2032 throw new IllegalArgumentException("HtmlElement expected, but ${element.ru
ntimeType} given"); | 1845 * |
| 2033 } | 1846 * @param nodeStart the token marking the beginning of the tag |
| 2034 super.element = element; | 1847 * @param tag the name of the tag |
| 2035 } | 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 } |
| 1877 |
| 1878 /** |
| 1879 * The enumeration `TokenType` defines the types of tokens that can be returned
by the |
| 1880 * scanner. |
| 1881 */ |
| 1882 class TokenType extends Enum<TokenType> { |
| 1883 /** |
| 1884 * The type of the token that marks the end of the input. |
| 1885 */ |
| 1886 static const TokenType EOF = const TokenType_EOF('EOF', 0, ""); |
| 1887 |
| 1888 static const TokenType EQ = const TokenType('EQ', 1, "="); |
| 1889 |
| 1890 static const TokenType GT = const TokenType('GT', 2, ">"); |
| 1891 |
| 1892 static const TokenType LT_SLASH = const TokenType('LT_SLASH', 3, "</"); |
| 1893 |
| 1894 static const TokenType LT = const TokenType('LT', 4, "<"); |
| 1895 |
| 1896 static const TokenType SLASH_GT = const TokenType('SLASH_GT', 5, "/>"); |
| 1897 |
| 1898 static const TokenType COMMENT = const TokenType('COMMENT', 6, null); |
| 1899 |
| 1900 static const TokenType DECLARATION = const TokenType('DECLARATION', 7, null); |
| 1901 |
| 1902 static const TokenType DIRECTIVE = const TokenType('DIRECTIVE', 8, null); |
| 1903 |
| 1904 static const TokenType STRING = const TokenType('STRING', 9, null); |
| 1905 |
| 1906 static const TokenType TAG = const TokenType('TAG', 10, null); |
| 1907 |
| 1908 static const TokenType TEXT = const TokenType('TEXT', 11, null); |
| 1909 |
| 1910 static const List<TokenType> values = const [ |
| 1911 EOF, |
| 1912 EQ, |
| 1913 GT, |
| 1914 LT_SLASH, |
| 1915 LT, |
| 1916 SLASH_GT, |
| 1917 COMMENT, |
| 1918 DECLARATION, |
| 1919 DIRECTIVE, |
| 1920 STRING, |
| 1921 TAG, |
| 1922 TEXT]; |
| 1923 |
| 1924 /** |
| 1925 * The lexeme that defines this type of token, or `null` if there is more than
one possible |
| 1926 * lexeme for this type of token. |
| 1927 */ |
| 1928 final String lexeme; |
| 1929 |
| 1930 const TokenType(String name, int ordinal, this.lexeme) : super(name, ordinal); |
| 1931 } |
| 1932 |
| 1933 class TokenType_EOF extends TokenType { |
| 1934 const TokenType_EOF(String name, int ordinal, String arg0) : super(name, ordin
al, arg0); |
| 1935 |
| 1936 @override |
| 1937 String toString() => "-eof-"; |
| 1938 } |
| 1939 |
| 1940 /** |
| 1941 * Instances of `XmlAttributeNode` represent name/value pairs owned by an [XmlTa
gNode]. |
| 1942 */ |
| 1943 class XmlAttributeNode extends XmlNode { |
| 1944 final Token _name; |
| 1945 |
| 1946 final Token equals; |
| 1947 |
| 1948 final Token _value; |
| 1949 |
| 1950 List<XmlExpression> expressions = XmlExpression.EMPTY_ARRAY; |
| 1951 |
| 1952 /** |
| 1953 * Construct a new instance representing an XML attribute. |
| 1954 * |
| 1955 * @param name the name token (not `null`). This may be a zero length token if
the attribute |
| 1956 * is badly formed. |
| 1957 * @param equals the equals sign or `null` if none |
| 1958 * @param value the value token (not `null`) |
| 1959 */ |
| 1960 XmlAttributeNode(this._name, this.equals, this._value); |
| 1961 |
| 1962 @override |
| 1963 accept(XmlVisitor visitor) => visitor.visitXmlAttributeNode(this); |
| 1964 |
| 1965 @override |
| 1966 Token get beginToken => _name; |
| 1967 |
| 1968 @override |
| 1969 Token get endToken => _value; |
| 1970 |
| 1971 /** |
| 1972 * Answer the attribute name. This may be a zero length string if the attribut
e is badly formed. |
| 1973 * |
| 1974 * @return the name (not `null`) |
| 1975 */ |
| 1976 String get name => _name.lexeme; |
| 1977 |
| 1978 /** |
| 1979 * Answer the attribute name token. This may be a zero length token if the att
ribute is badly |
| 1980 * formed. |
| 1981 * |
| 1982 * @return the name token (not `null`) |
| 1983 */ |
| 1984 Token get nameToken => _name; |
| 1985 |
| 1986 /** |
| 1987 * Answer the lexeme for the value token without the leading and trailing quot
es. |
| 1988 * |
| 1989 * @return the text or `null` if the value is not specified |
| 1990 */ |
| 1991 String get text { |
| 1992 if (_value == null) { |
| 1993 return null; |
| 1994 } |
| 1995 //TODO (danrubel): replace HTML character encodings with the actual characte
rs |
| 1996 String text = _value.lexeme; |
| 1997 int len = text.length; |
| 1998 if (len > 0) { |
| 1999 if (text.codeUnitAt(0) == 0x22) { |
| 2000 if (len > 1 && text.codeUnitAt(len - 1) == 0x22) { |
| 2001 return text.substring(1, len - 1); |
| 2002 } else { |
| 2003 return text.substring(1); |
| 2004 } |
| 2005 } else if (text.codeUnitAt(0) == 0x27) { |
| 2006 if (len > 1 && text.codeUnitAt(len - 1) == 0x27) { |
| 2007 return text.substring(1, len - 1); |
| 2008 } else { |
| 2009 return text.substring(1); |
| 2010 } |
| 2011 } |
| 2012 } |
| 2013 return text; |
| 2014 } |
| 2015 |
| 2016 /** |
| 2017 * Answer the offset of the value after the leading quote. |
| 2018 * |
| 2019 * @return the offset of the value, or `-1` if the value is not specified |
| 2020 */ |
| 2021 int get textOffset { |
| 2022 if (_value == null) { |
| 2023 return -1; |
| 2024 } |
| 2025 String text = _value.lexeme; |
| 2026 if (StringUtilities.startsWithChar(text, 0x22) || StringUtilities.startsWith
Char(text, 0x27)) { |
| 2027 return _value.offset + 1; |
| 2028 } |
| 2029 return _value.offset; |
| 2030 } |
| 2031 |
| 2032 /** |
| 2033 * 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. |
| 2035 * |
| 2036 * @return the value token or `null` if this represents a badly formed attribu
te |
| 2037 */ |
| 2038 Token get valueToken => _value; |
| 2036 | 2039 |
| 2037 @override | 2040 @override |
| 2038 void visitChildren(XmlVisitor visitor) { | 2041 void visitChildren(XmlVisitor visitor) { |
| 2039 for (XmlTagNode node in _tagNodes) { | |
| 2040 node.accept(visitor); | |
| 2041 } | |
| 2042 } | 2042 } |
| 2043 } | 2043 } |
| OLD | NEW |