1 /** 2 * @fileoverview A class which implements the xrx.xdm interface. 3 */ 4 5 goog.provide('xrx.node'); 6 7 goog.require('xrx.xdm'); 8 9 10 11 /** 12 * A class which implements the xrx.xdm interface. 13 * 14 * @constructor 15 * @implements {xrx.xdm} 16 */ 17 xrx.node = function(pilot, type, token) { 18 goog.base(this, pilot, xrx.node.ELEMENT, token); 19 20 /** 21 * @private 22 */ 23 this.pilot_ = pilot; 24 25 /** 26 * @private 27 */ 28 this.token_ = token; 29 30 /** 31 * @private 32 */ 33 this.type_ = type; 34 }; 35 goog.inherits(xrx.node, xrx.xdm); 36 37 38 39 /** @const */ xrx.node.DOCUMENT = 0; 40 /** @const */ xrx.node.ELEMENT = 1; 41 /** @const */ xrx.node.ATTRIBUTE = 2; 42 /** @const */ xrx.node.NAMESPACE = 3; 43 /** @const */ xrx.node.PI = 4; 44 /** @const */ xrx.node.COMMENT = 5; 45 /** @const */ xrx.node.TEXT = 6; 46 /** @const */ xrx.node.NODE = 7; 47 48 49 50 /** 51 * @return 52 */ 53 xrx.node.prototype.token = function() { 54 return this.token_; 55 }; 56 57 58 59 /** 60 * @return 61 */ 62 xrx.node.prototype.type = function() { 63 return this.type_; 64 }; 65 66 67 68 /** 69 * @return 70 */ 71 xrx.node.prototype.label = function() { 72 return this.token_.label(); 73 }; 74 75 76 77 /** 78 * @return 79 */ 80 xrx.node.prototype.offset = function() { 81 return this.token_.offset(); 82 }; 83 84 85 86 /** 87 * Returns the string-value of the required type from a node. 88 * 89 * @param {!xrx.node} node The node to get value from. 90 * @return {string} The value required. 91 */ 92 xrx.node.getValueAsString = function(node) { 93 return node.stringValue(); 94 }; 95 96 97 98 /** 99 * Returns the string-value of the required type from a node, casted to number. 100 * 101 * @param {!xrx.node} node The node to get value from. 102 * @return {number} The value required. 103 */ 104 xrx.node.getValueAsNumber = function(node) { 105 return +xrx.node.getValueAsString(node); 106 }; 107 108 109 110 /** 111 * Returns the string-value of the required type from a node, casted to boolean. 112 * 113 * @param {!xrx.node} node The node to get value from. 114 * @return {boolean} The value required. 115 */ 116 xrx.node.getValueAsBool = function(node) { 117 return !!xrx.node.getValueAsString(node); 118 }; 119 120 121 122 /** 123 * Returns whether two nodes are the same. 124 * 125 * @param {xrx.node} node The node to test against. 126 * @return {boolean} Whether the nodes are the same. 127 */ 128 xrx.node.prototype.sameAs = function(node) { 129 return this.type() === node.type() && this.label().sameAs( 130 node.label()); 131 }; 132 133 134 135 /** 136 * @return 137 */ 138 xrx.node.prototype.isBefore = function(node) { 139 return this.type_ <= node.type() && this.label().isBefore( 140 node.label()); 141 }; 142 143 144 145 /** 146 * @return 147 */ 148 xrx.node.compareOrder = function(node1, node2) { 149 150 if (node1.sameAs(node2)) { 151 return 0; 152 } else if (node1.isBefore(node2)) { 153 return -1; 154 } else { 155 return 1; 156 } 157 }; 158 159 160 161 /** 162 * @private 163 */ 164 xrx.node.prototype.forward = function() { 165 var node = this; 166 var stream = node.pilot_.stream(); 167 var label = node.label().clone(); 168 var first = true; 169 var lastTag; 170 if (label.value(0) === 0) label.child(); 171 172 stream.rowStartTag = function(offset, length1, length2) { 173 if (first) { 174 first = false; 175 } else if (lastTag === xrx.token.START_TAG) { 176 label.child(); 177 } else { 178 label.nextSibling(); 179 } 180 node.nodeElement(new xrx.token.StartEmptyTag(label.clone(), offset, length1)); 181 182 if (length1 !== length2) { 183 var lbl = label.clone(); 184 lbl.push0(); 185 node.nodeText(new xrx.token.NotTag(lbl.clone(), 186 offset + length1, length2 - length1)); 187 } 188 lastTag = xrx.token.START_TAG; 189 }; 190 191 stream.rowEndTag = function(offset, length1, length2) { 192 if (lastTag !== xrx.token.START_TAG) label.parent(); 193 if (length1 !== length2) { 194 var lbl = label.clone(); 195 node.nodeText(new xrx.token.NotTag(lbl.clone(), 196 offset + length1, length2 - length1)); 197 } 198 lastTag = xrx.token.END_TAG; 199 }; 200 201 stream.rowEmptyTag = function(offset, length1, length2) { 202 if (first) { 203 first = false; 204 } else if (lastTag === xrx.token.START_TAG) { 205 label.child(); 206 } else { 207 label.nextSibling(); 208 } 209 node.nodeElement(new xrx.token.StartEmptyTag(label.clone(), offset, length1)); 210 if (length1 !== length2) { 211 var lbl = label.clone(); 212 node.nodeText(new xrx.token.NotTag(lbl.clone(), 213 offset + length1, length2 - length1)); 214 } 215 lastTag = xrx.token.END_TAG; 216 }; 217 218 stream.forward(node.offset()); 219 }; 220 221 222 223 /** 224 * @private 225 */ 226 xrx.node.prototype.backward = function() { 227 var node = this; 228 var stream = node.pilot_.stream(); 229 var label = node.label().clone(); 230 var lastTag = xrx.token.START_TAG; 231 232 stream.rowStartTag = function(offset, length1, length2) { 233 if (lastTag !== xrx.token.END_TAG) label.parent(); 234 node.nodeElement(new xrx.token.StartEmptyTag(label.clone(), offset, length1)); 235 236 if (length1 !== length2) { 237 var lbl = label.clone(); 238 lbl.push0(); 239 node.nodeText(new xrx.token.NotTag(lbl.clone(), 240 offset + length1, length2 - length1)); 241 } 242 lastTag = xrx.token.START_TAG; 243 }; 244 245 stream.rowEndTag = function(offset, length1, length2) { 246 lastTag === xrx.token.END_TAG ? label.child() : label.precedingSibling(); 247 if (length1 !== length2) { 248 var lbl = label.clone(); 249 node.nodeText(new xrx.token.NotTag(lbl.clone(), 250 offset + length1, length2 - length1)); 251 } 252 lastTag = xrx.token.END_TAG; 253 }; 254 255 stream.rowEmptyTag = function(offset, length1, length2) { 256 lastTag === xrx.token.END_TAG ? label.child() : label.precedingSibling(); 257 node.nodeElement(new xrx.token.StartEmptyTag(label.clone(), offset, length1)); 258 if (length1 !== length2) { 259 var lbl = label.clone(); 260 node.nodeText(new xrx.token.NotTag(lbl.clone(), 261 offset + length1, length2 - length1)); 262 } 263 lastTag = xrx.token.START_TAG; 264 }; 265 stream.backward(node.offset()); 266 }; 267 268 269 /** 270 * @private 271 */ 272 xrx.node.prototype.find = function(test, axisTest, reverse) { 273 var nodeset = new xrx.xpath.NodeSet(); 274 var pilot = this.pilot_; 275 var elmnt = null; 276 277 this.nodeElement = function(token) { 278 elmnt = new xrx.node.Element(pilot, token); 279 if (axisTest.call(this.label(), token.label()) && test.matches(elmnt)) { 280 reverse ? nodeset.unshift(elmnt) : nodeset.add(elmnt); 281 } 282 }; 283 284 this.nodeText = function(token) { 285 var txt = new xrx.node.Text(pilot, token, elmnt); 286 if (axisTest.call(this.label(), token.label()) && test.matches(txt)) { 287 reverse ? nodeset.unshift(txt) : nodeset.add(txt); 288 } 289 }; 290 291 reverse ? this.backward() : this.forward(); 292 return nodeset; 293 }; 294 295 296 297 /** 298 * @override 299 */ 300 xrx.node.prototype.expandedName = function() { return ''; }; 301 302 303 /** 304 * @override 305 */ 306 xrx.node.prototype.namespaceUri = function() { return undefined; }; 307 308 309 310 /** 311 * @override 312 */ 313 xrx.node.prototype.getAncestorNodes = function(test) { 314 315 return this.find(test, xrx.label.prototype.isDescendantOf, true); 316 }; 317 318 319 /** 320 * @override 321 */ 322 xrx.node.prototype.getChildNodes = function(test) { 323 324 return this.find(test, xrx.label.prototype.isParentOf); 325 }; 326 327 328 /** 329 * Returns the descendants of a node. 330 * 331 * @private 332 * @param {!xrx.xpath.NodeTest} test A NodeTest for matching nodes. 333 * @param {!xrx.node} node The node to get descendants from. 334 * @param {?string} opt_attrName The attribute name to match, if any. 335 * @param {?string} opt_attrValue The attribute value to match, if any. 336 * @return {!xrx.xpath.NodeSet} The node-set with descendants. 337 */ 338 xrx.node.prototype.getDescendantNodes = function(test) { 339 340 return this.find(test, xrx.label.prototype.isAncestorOf); 341 }; 342 343 344 /** 345 * @override 346 */ 347 xrx.node.prototype.getFollowingSiblingNodes = function(test) { 348 349 return this.find(test, xrx.label.prototype.isPrecedingSiblingOf); 350 }; 351 352 353 /** 354 * @override 355 */ 356 xrx.node.prototype.getFollowingNodes = function(test) { 357 358 return this.find(test, xrx.label.prototype.isBefore); 359 }; 360 361 362 /** 363 * @override 364 */ 365 xrx.node.prototype.getAttributeNodes = function(test) { 366 var nodeset = new xrx.xpath.NodeSet(); 367 var stream = this.pilot_.stream(); 368 var label = this.label().clone(); 369 label.child(); 370 var attribute = new xrx.token.Attribute(label); 371 372 for(;;) { 373 attribute = stream.attrName(this.token_, attribute, attribute.offset()); 374 if (!attribute) break; 375 var node = new xrx.node.Attribute(this.pilot_, attribute, this) 376 if (test.matches(node)) { 377 nodeset.add(node); 378 break; 379 } 380 attribute.label().nextSibling(); 381 } 382 return nodeset; 383 }; 384 385 386 387 /** 388 * @override 389 */ 390 xrx.node.prototype.getParentNodes = function(test) { 391 392 return this.find(test, xrx.label.prototype.isChildOf, true); 393 }; 394 395 396 397 /** 398 * @constructor 399 */ 400 xrx.node.Element = function(pilot, token) { 401 goog.base(this, pilot, xrx.node.ELEMENT, token); 402 }; 403 goog.inherits(xrx.node.Element, xrx.node); 404 405 406 407 /** 408 * @override 409 */ 410 xrx.node.Element.prototype.expandedName = function() { 411 var pilot = this.pilot_; 412 return '' + pilot.xml(pilot.tagName(this.token_, this.token_)); 413 }; 414 415 416 417 /** 418 * @override 419 */ 420 xrx.node.Element.prototype.namespaceUri = function() { 421 return undefined; 422 }; 423 424 425 /** 426 * @constructor 427 */ 428 xrx.node.Attribute = function(pilot, token, parent) { 429 goog.base(this, pilot, xrx.node.ATTRIBUTE, token); 430 this.parent_ = parent; 431 }; 432 goog.inherits(xrx.node.Attribute, xrx.node); 433 434 435 436 /** 437 * @override 438 */ 439 xrx.node.Attribute.prototype.getChildNodes = function() { 440 return new xrx.xpath.NodeSet(); 441 }; 442 443 444 445 /** 446 * @override 447 */ 448 xrx.node.Attribute.prototype.getDescendantNodes = function() { 449 return new xrx.xpath.NodeSet(); 450 }; 451 452 453 454 /** 455 * @override 456 */ 457 xrx.node.Attribute.prototype.getFollowingSiblingNodes = function() { 458 return new xrx.xpath.NodeSet(); 459 }; 460 461 462 463 /** 464 * @override 465 */ 466 xrx.node.Attribute.prototype.getFollowingNodes = function() { 467 return new xrx.xpath.NodeSet(); 468 }; 469 470 471 472 /** 473 * @override 474 */ 475 xrx.node.Attribute.prototype.getAttributeNodes = function() { 476 return new xrx.xpath.NodeSet(); 477 }; 478 479 480 481 /** 482 * @override 483 */ 484 xrx.node.Attribute.prototype.getParentNodes = function(test) { 485 var nodeset = new xrx.xpath.NodeSet(); 486 if (test.matches(this.parent_)) nodeset.add(this.parent_); 487 488 return nodeset; 489 }; 490 491 492 493 /** 494 * @override 495 */ 496 xrx.node.Attribute.prototype.expandedName = function() { 497 var stream = this.pilot_.stream(); 498 var attrName = new xrx.token.AttrName(this.label().clone()); 499 var token = stream.attrName(this.parent_.token(), attrName, this.offset()); 500 501 return '' + this.pilot_.xml(token); 502 }; 503 504 505 506 /** 507 * @override 508 */ 509 xrx.node.Attribute.prototype.stringValue = function() { 510 var stream = this.pilot_.stream(); 511 var attrValue = new xrx.token.AttrValue(this.label().clone()); 512 var token = stream.attrValue(this.parent_.token(), attrValue, this.offset()); 513 514 return this.pilot_.xml(token); 515 }; 516 517 518 519 /** 520 * @override 521 */ 522 xrx.node.Attribute.prototype.namespaceUri = function() { 523 return undefined; 524 }; 525 526 527 528 /** 529 * @constructor 530 */ 531 xrx.node.Document = function(pilot) { 532 goog.base(this, pilot, xrx.node.DOCUMENT, new xrx.token.Root()); 533 }; 534 goog.inherits(xrx.node.Document, xrx.node); 535 536 537 538 /** 539 * @overwrite 540 */ 541 xrx.node.Document.prototype.getAncestorNodes = function() { 542 return new xrx.xpath.NodeSet(); 543 }; 544 545 546 547 /** 548 * @overwrite 549 */ 550 xrx.node.Document.prototype.getAttributeNodes = function() { 551 return new xrx.xpath.NodeSet(); 552 }; 553 554 555 556 /** 557 * @overwrite 558 */ 559 xrx.node.Document.prototype.xml = function() { 560 return this.pilot_.xml(); 561 }; 562 563 564 565 /** 566 * @constructor 567 */ 568 xrx.node.Text = function(pilot, token, parent) { 569 goog.base(this, pilot, xrx.node.TEXT, token); 570 this.parent_ = parent; 571 }; 572 goog.inherits(xrx.node.Text, xrx.node); 573 574 575 576 /** 577 * @overwrite 578 */ 579 xrx.node.Text.prototype.getChildNodes = function() { 580 return new xrx.xpath.NodeSet(); 581 }; 582 583 584 585 /** 586 * @overwrite 587 */ 588 xrx.node.Text.prototype.getDescendantNodes = function() { 589 return new xrx.xpath.NodeSet(); 590 }; 591 592 593 594 /** 595 * @overwrite 596 */ 597 xrx.node.Text.prototype.getFollowingSiblingNodes = function(test) { 598 599 return this.parent_.find(test, xrx.label.prototype.isPrecedingSiblingOf); 600 }; 601 602 603 604 /** 605 * @overwrite 606 */ 607 xrx.node.Text.prototype.getFollowingNodes = function(test) { 608 609 return this.parent_.find(test, xrx.label.prototype.isBefore); 610 }; 611 612 613 614 /** 615 * @overwrite 616 */ 617 xrx.node.Text.prototype.getAttributeNodes = function() { 618 return new xrx.xpath.NodeSet(); 619 }; 620 621 622 623 /** 624 * @override 625 */ 626 xrx.node.Text.prototype.getParentNodes = function(test) { 627 var nodeset = new xrx.xpath.NodeSet(); 628 if (test.matches(this.parent_)) nodeset.add(this.parent_); 629 630 return nodeset; 631 }; 632 633 634 xrx.node.Text.prototype.stringValue = function() { 635 return this.pilot_.xml(this.token_); 636 }; 637