1 /* 2 * Copyright László Szerémi 2022 - . 3 * Distributed under the Boost Software License, Version 1.0. 4 * (See accompanying file LICENSE_1_0.txt or copy at 5 * http://www.boost.org/LICENSE_1_0.txt) 6 */ 7 8 /++ 9 + Provides an implementation of the DOM Level 3 specification. 10 + 11 + Authors: 12 + Lodovico Giaretta 13 + László Szerémi 14 + 15 + License: 16 + <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>. 17 + 18 + Copyright: 19 + Copyright Lodovico Giaretta 2016 -- 20 +/ 21 22 module newxml.domimpl; 23 24 import newxml.interfaces; 25 import newxml.domstring; 26 import dom = newxml.dom; 27 import std.typecons : rebindable, Flag, BitFlags; 28 //import std.experimental.allocator;//import stdx.allocator; 29 //import std.experimental.allocator.gc_allocator;//import stdx.allocator.gc_allocator; 30 import std.range.primitives; 31 32 /* // this is needed because compilers up to at least DMD 2.071.1 suffer from issue 16319 33 private auto multiVersionMake(Type, Args...)(auto ref Args args) 34 { 35 static if (Args.length == 0 && __traits(compiles, allocator.make!Type(args))) 36 return allocator.make!Type(args); 37 else static if (Args.length > 0 && __traits(compiles, allocator.make!Type(args[0 .. $]))) 38 { 39 auto res = allocator.make!Type(args[0 .. $]); 40 return res; 41 } 42 else 43 static assert(0, "multiVersionMake failed..."); 44 } */ 45 46 /++ 47 + An implementation of $(LINK2 ../dom/DOMImplementation, `std.experimental.xml.dom.DOMImplementation`). 48 + 49 + It allows to specify a custom allocator to be used when creating instances of the DOM classes. 50 + As keeping track of the lifetime of every node would be very complex, this implementation 51 + does not try to do so. Instead, no object is ever deallocated; it is the users responsibility 52 + to directly free the allocator memory when all objects are no longer reachable. 53 +/ 54 class DOMImplementation : dom.DOMImplementation 55 { 56 @safe: 57 //mixin UsesAllocator!(Alloc, true); 58 this() @nogc @safe pure nothrow { 59 60 } 61 override 62 { 63 /++ 64 + Implementation of $(LINK2 ../dom/DOMImplementation.createDocumentType, 65 + `std.experimental.xml.dom.DOMImplementation.createDocumentType`). 66 +/ 67 DocumentType createDocumentType(DOMString qualifiedName, DOMString publicId, DOMString systemId) 68 { 69 DocumentType res = new DocumentType();//allocator.multiVersionMake!DocumentType(this); 70 res._name = qualifiedName; 71 res._publicId = publicId; 72 res._systemId = systemId; 73 return res; 74 } 75 /++ 76 + Implementation of $(LINK2 ../dom/DOMImplementation.createDocument, 77 + `std.experimental.xml.dom.DOMImplementation.createDocument`). 78 +/ 79 Document createDocument(DOMString namespaceURI, DOMString qualifiedName, dom.DocumentType _doctype) 80 { 81 DocumentType doctype = cast(DocumentType)_doctype; 82 if (_doctype && !doctype) 83 throw new DOMException(dom.ExceptionCode.wrongDocument);//allocator.multiVersionMake!DOMException(this, dom.ExceptionCode.wrongDocument); 84 85 Document doc = new Document();//allocator.multiVersionMake!Document(this); 86 doc._ownerDocument = doc; 87 doc._doctype = doctype; 88 doc._config = new DOMConfiguration();//allocator.multiVersionMake!DOMConfiguration(this); 89 90 if (namespaceURI) 91 { 92 if (!qualifiedName) 93 throw new DOMException(dom.ExceptionCode.namespace);//allocator.multiVersionMake!DOMException(this, dom.ExceptionCode.namespace); 94 doc.appendChild(doc.createElementNS(namespaceURI, qualifiedName)); 95 } 96 else if (qualifiedName) 97 doc.appendChild(doc.createElement(qualifiedName)); 98 99 return doc; 100 } 101 /++ 102 + Implementation of $(LINK2 ../dom/DOMImplementation.hasFeature, 103 + `std.experimental.xml.dom.DOMImplementation.hasFeature`). 104 + 105 + Only recognizes features `"Core"`and `"XML"` with versions `"1.0"`, 106 + `"2.0"` or `"3.0"`. 107 +/ 108 bool hasFeature(string feature, string version_) 109 { 110 import std.uni: sicmp; 111 return (!sicmp(feature, "Core") || !sicmp(feature, "XML")) 112 && (version_ == "1.0" || version_ == "2.0" || version_ == "3.0"); 113 } 114 /++ 115 + Implementation of $(LINK2 ../dom/DOMImplementation.hasFeature, 116 + `std.experimental.xml.dom.DOMImplementation.hasFeature`). 117 + 118 + Only recognizes features `"Core"`and `"XML"` with versions `"1.0"`, 119 + `"2.0"` or `"3.0"`. Always returns `this`. 120 +/ 121 DOMImplementation getFeature(string feature, string version_) 122 { 123 if (hasFeature(feature, version_)) 124 return this; 125 else 126 return null; 127 } 128 } 129 130 /++ 131 + The implementation of $(LINK2 ../dom/DOMException, `std.experimental.xml.dom.DOMException`) 132 + thrown by this DOM implementation. 133 +/ 134 class DOMException: dom.DOMException 135 { 136 /// Constructs a `DOMException` with a specific `dom.ExceptionCode`. 137 pure nothrow @nogc @safe this(dom.ExceptionCode code, string file = __FILE__, size_t line = __LINE__, 138 Throwable nextInChain = null) 139 { 140 _code = code; 141 super("", file, line); 142 } 143 @nogc @safe pure nothrow this(string msg, Throwable nextInChain, string file = __FILE__, size_t line = __LINE__) 144 { 145 super(msg, file, line, nextInChain); 146 } 147 /// Implementation of $(LINK2 ../dom/DOMException.code, `std.experimental.xml.dom.DOMException.code`). 148 override @property dom.ExceptionCode code() 149 { 150 return _code; 151 } 152 private dom.ExceptionCode _code; 153 } 154 /// Implementation of $(LINK2 ../dom/Node, `std.experimental.xml.dom.Node`) 155 abstract class Node : dom.Node 156 { 157 package this() { 158 159 } 160 override 161 { 162 /// Implementation of $(LINK2 ../dom/Node.ownerDocument, `std.experimental.xml.dom.Node.ownerDocument`). 163 @property Document ownerDocument() { return _ownerDocument; } 164 165 /// Implementation of $(LINK2 ../dom/Node.parentNode, `std.experimental.xml.dom.Node.parentNode`). 166 @property Node parentNode() { return _parentNode; } 167 /++ 168 + Implementation of $(LINK2 ../dom/Node.previousSibling, 169 + `std.experimental.xml.dom.Node.previousSibling`). 170 +/ 171 @property Node previousSibling() { return _previousSibling; } 172 /// Implementation of $(LINK2 ../dom/Node.nextSibling, `std.experimental.xml.dom.Node.nextSibling`). 173 @property Node nextSibling() { return _nextSibling; } 174 175 /++ 176 + Implementation of $(LINK2 ../dom/Node.isSameNode, `std.experimental.xml.dom.Node.isSameNode`). 177 + 178 + Equivalent to a call to `this is other`. 179 +/ 180 bool isSameNode(dom.Node other) 181 { 182 return this is other; 183 } 184 /// Implementation of $(LINK2 ../dom/Node.isEqualNode, `std.experimental.xml.dom.Node.isEqualNode`). 185 bool isEqualNode(dom.Node other) 186 { 187 import std.meta: AliasSeq; 188 189 if (!other || nodeType != other.nodeType) 190 return false; 191 192 foreach (field; AliasSeq!("nodeName", "localName", "namespaceURI", "prefix", "nodeValue")) 193 { 194 mixin("auto a = " ~ field ~ ";\n"); 195 mixin("auto b = other." ~ field ~ ";\n"); 196 if ((a is null && b !is null) || (b is null && a !is null) || (a !is null && b !is null && a != b)) 197 return false; 198 } 199 200 auto thisWithChildren = cast(NodeWithChildren)this; 201 if (thisWithChildren) 202 { 203 auto otherChild = other.firstChild; 204 foreach (child; thisWithChildren.childNodes) 205 { 206 if (!child.isEqualNode(otherChild)) 207 return false; 208 otherChild = otherChild.nextSibling; 209 } 210 if (otherChild !is null) 211 return false; 212 } 213 214 return true; 215 } 216 217 /// Implementation of $(LINK2 ../dom/Node.setUserData, `std.experimental.xml.dom.Node.setUserData`). 218 dom.UserData setUserData(string key, dom.UserData data, dom.UserDataHandler handler) @trusted 219 { 220 userData[key] = data; 221 if (handler) 222 userDataHandlers[key] = handler; 223 return data; 224 } 225 /// Implementation of $(LINK2 ../dom/Node.getUserData, `std.experimental.xml.dom.Node.getUserData`). 226 dom.UserData getUserData(string key) const @trusted 227 { 228 if (key in userData) 229 return userData[key]; 230 return dom.UserData(null); 231 } 232 233 /++ 234 + Implementation of $(LINK2 ../dom/Node.isSupported, `std.experimental.xml.dom.Node.isSupported`). 235 + 236 + Only recognizes features `"Core"`and `"XML"` with versions `"1.0"`, 237 + `"2.0"` or `"3.0"`. 238 +/ 239 bool isSupported(string feature, string version_) 240 { 241 return (feature == "Core" || feature == "XML") 242 && (version_ == "1.0" || version_ == "2.0" || version_ == "3.0"); 243 } 244 /++ 245 + Implementation of $(LINK2 ../dom/Node.getFeature, `std.experimental.xml.dom.Node.getFeature`). 246 + 247 + Only recognizes features `"Core"`and `"XML"` with versions `"1.0"`, 248 + `"2.0"` or `"3.0"`. Always returns this. 249 +/ 250 Node getFeature(string feature, string version_) 251 { 252 if (isSupported(feature, version_)) 253 return this; 254 else 255 return null; 256 } 257 258 /++ 259 + Implementation of $(LINK2 ../dom/Node.compareDocumentPosition, 260 + `std.experimental.xml.dom.Node.compareDocumentPosition`). 261 +/ 262 BitFlags!(dom.DocumentPosition) compareDocumentPosition(dom.Node _other) @trusted 263 { 264 enum Ret(dom.DocumentPosition flag) = cast(BitFlags!(dom.DocumentPosition)) flag; 265 266 Node other = cast(Node)_other; 267 if (!other) 268 return Ret!(dom.DocumentPosition.disconnected); 269 270 if (this is other) 271 return Ret!(dom.DocumentPosition.none); 272 273 Node node1 = other; 274 Node node2 = this; 275 Attr attr1 = cast(Attr)node1; 276 Attr attr2 = cast(Attr)node2; 277 278 if (attr1 && attr1.ownerElement) 279 node1 = attr1.ownerElement; 280 if (attr2 && attr2.ownerElement) 281 { 282 node2 = attr2.ownerElement; 283 if (attr1 && node2 is node1) 284 { 285 foreach (attr; (cast(Element)node2).attributes) with (dom.DocumentPosition) 286 { 287 if (attr is attr1) 288 return Ret!implementationSpecific | Ret!preceding; 289 else if (attr is attr2) { 290 return Ret!implementationSpecific | Ret!following; 291 } 292 } 293 } 294 } 295 void rootAndDepth(ref Node node, out int depth) 296 { 297 while (node.parentNode) 298 { 299 node = node.parentNode; 300 depth++; 301 } 302 } 303 Node root1 = node1, root2 = node2; 304 int depth1, depth2; 305 rootAndDepth(root1, depth1); 306 rootAndDepth(root2, depth2); 307 308 if (root1 !is root2) with (dom.DocumentPosition) 309 { 310 if (cast(void*)root1 < cast(void*)root2) 311 return Ret!disconnected | Ret!implementationSpecific | Ret!preceding; 312 else 313 return Ret!disconnected | Ret!implementationSpecific | Ret!following; 314 } 315 316 bool swapped = depth1 < depth2; 317 if (swapped) 318 { 319 import std.algorithm: swap; 320 swap(depth1, depth2); 321 swap(node1, node2); 322 swapped = true; 323 } 324 while (depth1-- > depth2) 325 { 326 node1 = node1.parentNode; 327 } 328 if (node1 is node2) with (dom.DocumentPosition) 329 { 330 if (swapped) 331 return Ret!contains | Ret!preceding; 332 else 333 return Ret!containedBy | Ret!following; 334 } 335 while(true) 336 { 337 if (node1.parentNode is node2.parentNode) 338 { 339 while (node1.nextSibling) 340 { 341 node1 = node1.nextSibling; 342 if (node1 is node2) 343 return Ret!(dom.DocumentPosition.preceding); 344 } 345 return Ret!(dom.DocumentPosition.following); 346 } 347 node1 = node1.parentNode; 348 node2 = node2.parentNode; 349 } 350 assert(0, "Control flow should never reach this...\nPlease file an issue"); 351 } 352 } 353 private 354 { 355 dom.UserData[string] userData; 356 dom.UserDataHandler[string] userDataHandlers; 357 Node _previousSibling, _nextSibling, _parentNode; 358 Document _ownerDocument; 359 bool _readonly = false; 360 361 // internal methods 362 Element parentElement() 363 { 364 auto parent = parentNode; 365 while (parent && parent.nodeType != dom.NodeType.element) 366 parent = parent.parentNode; 367 return cast(Element)parent; 368 } 369 void performClone(Node dest, bool deep) @trusted 370 { 371 foreach (data; userDataHandlers.byKeyValue) 372 { 373 auto value = data.value; 374 // putting data.value directly in the following line causes an error; should investigate further 375 value(dom.UserDataOperation.nodeCloned, new DOMString(data.key), userData[data.key], this, dest); 376 } 377 } 378 } 379 // method that must be overridden 380 // just because otherwise it doesn't work [bugzilla 16318] 381 abstract override DOMString nodeName(); 382 // methods specialized in NodeWithChildren 383 override 384 { 385 @property ChildList childNodes() 386 { 387 static ChildList emptyList; 388 if (!emptyList) 389 { 390 emptyList = new ChildList();//allocator.multiVersionMake!ChildList(this); 391 emptyList.currentChild = firstChild; 392 } 393 return emptyList; 394 } 395 @property Node firstChild() { return null; } 396 @property Node lastChild() { return null; } 397 398 Node insertBefore(dom.Node _newChild, dom.Node _refChild) 399 { 400 throw new DOMException(dom.ExceptionCode.hierarchyRequest);//allocator.multiVersionMake!DOMException(this.outer, dom.ExceptionCode.hierarchyRequest); 401 } 402 403 Node replaceChild(dom.Node newChild, dom.Node oldChild) 404 { 405 throw new DOMException(dom.ExceptionCode.hierarchyRequest);//allocator.multiVersionMake!DOMException(this.outer, dom.ExceptionCode.hierarchyRequest); 406 } 407 408 Node removeChild(dom.Node oldChild) 409 { 410 throw new DOMException(dom.ExceptionCode.notFound);//allocator.multiVersionMake!DOMException(this.outer, dom.ExceptionCode.notFound); 411 } 412 413 Node appendChild(dom.Node newChild) 414 { 415 throw new DOMException(dom.ExceptionCode.hierarchyRequest);//allocator.multiVersionMake!DOMException(this.outer, dom.ExceptionCode.hierarchyRequest); 416 } 417 418 bool hasChildNodes() const { return false; } 419 } 420 // methods specialized in Element 421 override 422 { 423 @property Element.Map attributes() { return null; } 424 bool hasAttributes() { return false; } 425 } 426 // methods specialized in various subclasses 427 override 428 { 429 @property DOMString nodeValue() { return null; } 430 @property void nodeValue(DOMString) {} 431 @property DOMString textContent() { return null; } 432 @property void textContent(DOMString) {} 433 @property DOMString baseURI() 434 { 435 if (parentNode) 436 return parentNode.baseURI; 437 return null; 438 } 439 440 Node cloneNode(bool deep) { return null; } 441 } 442 // methods specialized in Element and Attribute 443 override 444 { 445 @property DOMString localName() { return null; } 446 @property DOMString prefix() { return null; } 447 @property void prefix(DOMString) { } 448 @property DOMString namespaceURI() { return null; } 449 } 450 // methods specialized in Document, Element and Attribute 451 override 452 { 453 DOMString lookupPrefix(DOMString namespaceURI) 454 { 455 if (!namespaceURI) 456 return null; 457 458 switch (nodeType) with (dom.NodeType) 459 { 460 case entity: 461 case notation: 462 case documentFragment: 463 case documentType: 464 return null; 465 case attribute: 466 Attr attr = cast(Attr)this; 467 if (attr.ownerElement) 468 return attr.ownerElement.lookupNamespacePrefix(namespaceURI, attr.ownerElement); 469 return null; 470 default: 471 auto parentElement = parentElement(); 472 if (parentElement) 473 return parentElement.lookupNamespacePrefix(namespaceURI, parentElement); 474 return null; 475 } 476 } 477 DOMString lookupNamespaceURI(DOMString prefix) 478 { 479 switch (nodeType) with (dom.NodeType) 480 { 481 case entity: 482 case notation: 483 case documentType: 484 case documentFragment: 485 return null; 486 case attribute: 487 auto attr = cast(Attr)this; 488 if (attr.ownerElement) 489 return attr.ownerElement.lookupNamespaceURI(prefix); 490 return null; 491 default: 492 auto parentElement = parentElement(); 493 if (parentElement) 494 return parentElement.lookupNamespaceURI(prefix); 495 496 return null; 497 } 498 } 499 bool isDefaultNamespace(DOMString namespaceURI) 500 { 501 switch (nodeType) with (dom.NodeType) 502 { 503 case entity: 504 case notation: 505 case documentType: 506 case documentFragment: 507 return false; 508 case attribute: 509 auto attr = cast(Attr)this; 510 if (attr.ownerElement) 511 return attr.ownerElement.isDefaultNamespace(namespaceURI); 512 return false; 513 default: 514 auto parentElement = parentElement(); 515 if (parentElement) 516 return parentElement.isDefaultNamespace(namespaceURI); 517 return false; 518 } 519 } 520 } 521 // TODO methods 522 override 523 { 524 void normalize() {} 525 } 526 // inner class for use in NodeWithChildren 527 class ChildList : dom.NodeList 528 { 529 private Node currentChild; 530 package this() { 531 532 } 533 // methods specific to NodeList 534 override 535 { 536 Node item(size_t index) 537 { 538 auto result = rebindable(this.outer.firstChild); 539 for (size_t i = 0; i < index && result !is null; i++) 540 { 541 result = result.nextSibling; 542 } 543 return result; 544 } 545 @property size_t length() 546 { 547 auto child = rebindable(this.outer.firstChild); 548 size_t result = 0; 549 while (child) 550 { 551 result++; 552 child = child.nextSibling; 553 } 554 return result; 555 } 556 } 557 // more idiomatic methods 558 auto opIndex(size_t i) 559 { 560 return item(i); 561 } 562 // range interface 563 auto front() { return currentChild; } 564 void popFront() { currentChild = currentChild.nextSibling; } 565 bool empty() { return currentChild is null; } 566 } 567 // method not required by the spec, specialized in NodeWithChildren 568 bool isAncestor(Node other) { return false; } 569 /++ 570 + `true` if and only if this node is _readonly. 571 + 572 + The DOM specification defines a _readonly node as "a node that is immutable. 573 + This means its list of children, its content, and its attributes, when it is 574 + an element, cannot be changed in any way. However, a read only node can 575 + possibly be moved, when it is not itself contained in a read only node." 576 + 577 + For example, `Notation`s, `EntityReference`s and all of theirs descendants 578 + are always readonly. 579 +/ 580 // method not required by the spec, specialized in varous subclasses 581 @property bool readonly() { return _readonly; } 582 } 583 private abstract class NodeWithChildren : Node 584 { 585 package this() { 586 587 } 588 override 589 { 590 @property ChildList childNodes() 591 { 592 ChildList res = new ChildList();//allocator.multiVersionMake!ChildList(this); 593 res.currentChild = firstChild; 594 return res; 595 } 596 @property Node firstChild() 597 { 598 return _firstChild; 599 } 600 @property Node lastChild() 601 { 602 return _lastChild; 603 } 604 605 Node insertBefore(dom.Node _newChild, dom.Node _refChild) 606 { 607 if (readonly) 608 throw new DOMException(dom.ExceptionCode.noModificationAllowed); //allocator.multiVersionMake!DOMException(this.outer,dom.ExceptionCode.noModificationAllowed); 609 if (!_refChild) 610 return appendChild(_newChild); 611 612 Node newChild = cast(Node)_newChild; 613 Node refChild = cast(Node)_refChild; 614 if (!newChild || !refChild || newChild.ownerDocument !is ownerDocument) 615 throw new DOMException(dom.ExceptionCode.wrongDocument); //allocator.multiVersionMake!DOMException(this.outer, dom.ExceptionCode.wrongDocument); 616 if (this is newChild || newChild.isAncestor(this) || newChild is refChild) 617 throw new DOMException(dom.ExceptionCode.hierarchyRequest);//allocator.multiVersionMake!DOMException(this.outer, dom.ExceptionCode.hierarchyRequest); 618 if (refChild.parentNode !is this) 619 throw new DOMException(dom.ExceptionCode.notFound);//allocator.multiVersionMake!DOMException(this.outer, dom.ExceptionCode.notFound); 620 621 if (newChild.nodeType == dom.NodeType.documentFragment) 622 { 623 for (auto child = rebindable(newChild); child !is null; child = child.nextSibling) 624 insertBefore(child, refChild); 625 return newChild; 626 } 627 628 if (newChild.parentNode) 629 newChild.parentNode.removeChild(newChild); 630 newChild._parentNode = this; 631 if (refChild.previousSibling) 632 { 633 refChild.previousSibling._nextSibling = newChild; 634 newChild._previousSibling = refChild.previousSibling; 635 } 636 refChild._previousSibling = newChild; 637 newChild._nextSibling = refChild; 638 if (firstChild is refChild) 639 _firstChild = newChild; 640 return newChild; 641 } 642 Node replaceChild(dom.Node newChild, dom.Node oldChild) 643 { 644 insertBefore(newChild, oldChild); 645 return removeChild(oldChild); 646 } 647 Node removeChild(dom.Node _oldChild) 648 { 649 if (readonly) 650 throw new DOMException(dom.ExceptionCode.noModificationAllowed);//allocator.multiVersionMake!DOMException(this.outer, dom.ExceptionCode.noModificationAllowed); 651 Node oldChild = cast(Node)_oldChild; 652 if (!oldChild || oldChild.parentNode !is this) 653 throw new DOMException(dom.ExceptionCode.notFound);//allocator.multiVersionMake!DOMException(this.outer, dom.ExceptionCode.notFound); 654 655 if (oldChild is firstChild) 656 _firstChild = oldChild.nextSibling; 657 else 658 oldChild.previousSibling._nextSibling = oldChild.nextSibling; 659 660 if (oldChild is lastChild) 661 _lastChild = oldChild.previousSibling; 662 else 663 oldChild.nextSibling._previousSibling = oldChild.previousSibling; 664 665 oldChild._parentNode = null; 666 oldChild._previousSibling = null; 667 oldChild._nextSibling = null; 668 return oldChild; 669 } 670 Node appendChild(dom.Node _newChild) 671 { 672 if (readonly) 673 throw new DOMException(dom.ExceptionCode.noModificationAllowed);//allocator.multiVersionMake!DOMException(this.outer, dom.ExceptionCode.noModificationAllowed); 674 Node newChild = cast(Node)_newChild; 675 if (!newChild || newChild.ownerDocument !is ownerDocument) 676 throw new DOMException(dom.ExceptionCode.wrongDocument);//allocator.multiVersionMake!DOMException(this.outer, dom.ExceptionCode.wrongDocument); 677 if (this is newChild || newChild.isAncestor(this)) 678 throw new DOMException(dom.ExceptionCode.hierarchyRequest);//allocator.multiVersionMake!DOMException(this.outer, dom.ExceptionCode.hierarchyRequest); 679 if (newChild.parentNode !is null) 680 newChild.parentNode.removeChild(newChild); 681 682 if (newChild.nodeType == dom.NodeType.documentFragment) 683 { 684 for (auto node = rebindable(newChild.firstChild); node !is null; node = node.nextSibling) 685 appendChild(node); 686 return newChild; 687 } 688 689 newChild._parentNode = this; 690 if (lastChild) 691 { 692 newChild._previousSibling = lastChild; 693 lastChild._nextSibling = newChild; 694 } 695 else 696 _firstChild = newChild; 697 _lastChild = newChild; 698 return newChild; 699 } 700 bool hasChildNodes() const 701 { 702 return _firstChild !is null; 703 } 704 bool isAncestor(Node other) 705 { 706 for (auto child = rebindable(firstChild); child !is null; child = child.nextSibling) 707 { 708 if (child is other) 709 return true; 710 if (child.isAncestor(other)) 711 return true; 712 } 713 return false; 714 } 715 716 @property DOMString textContent() 717 { 718 //import std.experimental.xml.appender; 719 DOMString result; 720 //auto result = Appender!(typeof(this.textContent()[0]), typeof(*allocator))(allocator); 721 for (auto child = rebindable(firstChild); child !is null; child = child.nextSibling) 722 { 723 if (child.nodeType != dom.NodeType.comment && 724 child.nodeType != dom.NodeType.processingInstruction) 725 { 726 result ~= child.textContent();//result.put(child.textContent); 727 } 728 } 729 return result; 730 } 731 @property void textContent(DOMString newVal) 732 { 733 if (readonly) 734 throw new DOMException(dom.ExceptionCode.noModificationAllowed);// allocator.multiVersionMake!DOMException(this.outer, dom.ExceptionCode.noModificationAllowed); 735 736 while (firstChild) 737 removeChild(firstChild); 738 739 _firstChild = _lastChild = ownerDocument.createTextNode(newVal); 740 } 741 } 742 private 743 { 744 Node _firstChild, _lastChild; 745 746 void performClone(NodeWithChildren dest, bool deep) 747 { 748 super.performClone(dest, deep); 749 if (deep) 750 foreach (child; childNodes) 751 { 752 auto childClone = child.cloneNode(true); 753 dest.appendChild(childClone); 754 } 755 } 756 } 757 } 758 /// Implementation of $(LINK2 ../dom/DocumentFragment, `std.experimental.xml.dom.DocumentFragment`) 759 class DocumentFragment : NodeWithChildren, dom.DocumentFragment 760 { 761 package this() { 762 763 } 764 // inherited from Node 765 override 766 { 767 @property dom.NodeType nodeType() { return dom.NodeType.documentFragment; } 768 @property DOMString nodeName() { return new DOMString("#document-fragment"w); } 769 } 770 } 771 /// Implementation of $(LINK2 ../dom/Document, `std.experimental.xml.dom.Document`) 772 class Document : NodeWithChildren, dom.Document 773 { 774 package this() { 775 776 } 777 // specific to Document 778 override 779 { 780 @property DocumentType doctype() { return _doctype; } 781 @property DOMImplementation implementation() { return this.outer; } 782 @property Element documentElement() { return _root; } 783 784 Element createElement(DOMString tagName) 785 { 786 Element res = new Element(); //allocator.multiVersionMake!Element(this.outer); 787 res._name = tagName; 788 res._ownerDocument = this; 789 res._attrs = res.createMap();//new Element.Map(); //allocator.multiVersionMake!(Element.Map)(res); 790 return res; 791 } 792 Element createElementNS(DOMString namespaceURI, DOMString qualifiedName) 793 { 794 Element res = new Element();//allocator.multiVersionMake!Element(this.outer); 795 res.setQualifiedName(qualifiedName); 796 res._namespaceURI = namespaceURI; 797 res._ownerDocument = this; 798 res._attrs = res.createMap();//res._attrs = new Element.Map();//allocator.multiVersionMake!(Element.Map)(res); 799 return res; 800 } 801 DocumentFragment createDocumentFragment() 802 { 803 DocumentFragment res = new DocumentFragment();//allocator.multiVersionMake!DocumentFragment(this.outer); 804 res._ownerDocument = this; 805 return res; 806 } 807 Text createTextNode(DOMString data) 808 { 809 Text res = new Text();//allocator.multiVersionMake!Text(this.outer); 810 res._data = data; 811 res._ownerDocument = this; 812 return res; 813 } 814 Comment createComment(DOMString data) 815 { 816 Comment res = new Comment();//allocator.multiVersionMake!Comment(this.outer); 817 res._data = data; 818 res._ownerDocument = this; 819 return res; 820 } 821 CDATASection createCDATASection(DOMString data) 822 { 823 CDATASection res = new CDATASection();//allocator.multiVersionMake!CDATASection(this.outer); 824 res._data = data; 825 res._ownerDocument = this; 826 return res; 827 } 828 ProcessingInstruction createProcessingInstruction(DOMString target, DOMString data) 829 { 830 ProcessingInstruction res = new ProcessingInstruction();//allocator.multiVersionMake!ProcessingInstruction(this.outer); 831 res._target = target; 832 res._data = data; 833 res._ownerDocument = this; 834 return res; 835 } 836 Attr createAttribute(DOMString name) 837 { 838 Attr res = new Attr();//allocator.multiVersionMake!Attr(this.outer); 839 res._name = name; 840 res._ownerDocument = this; 841 return res; 842 } 843 Attr createAttributeNS(DOMString namespaceURI, DOMString qualifiedName) 844 { 845 Attr res = new Attr();//allocator.multiVersionMake!Attr(this.outer); 846 res.setQualifiedName(qualifiedName); 847 res._namespaceURI = namespaceURI; 848 res._ownerDocument = this; 849 return res; 850 } 851 EntityReference createEntityReference(DOMString name) { return null; } 852 853 ElementsByTagName getElementsByTagName(DOMString tagname) 854 { 855 ElementsByTagName res = new ElementsByTagName();//allocator.multiVersionMake!ElementsByTagName; 856 res.root = this; 857 res.tagname = tagname; 858 res.current = res.item(0); 859 return res; 860 } 861 ElementsByTagNameNS getElementsByTagNameNS(DOMString namespaceURI, DOMString localName) 862 { 863 ElementsByTagNameNS res = new ElementsByTagNameNS();//allocator.multiVersionMake!ElementsByTagNameNS; 864 res.root = this; 865 res.namespaceURI = namespaceURI; 866 res.localName = localName; 867 res.current = res.item(0); 868 return res; 869 } 870 Element getElementById(DOMString elementId) 871 { 872 Element find(dom.Node node) @safe 873 { 874 if (node.nodeType == dom.NodeType.element && node.hasAttributes) 875 foreach (attr; node.attributes) 876 { 877 if ((cast(Attr)attr).isId && attr.nodeValue == elementId) 878 return cast(Element)node; 879 } 880 foreach (child; node.childNodes) 881 { 882 auto res = find(child); 883 if (res) 884 return res; 885 } 886 return null; 887 } 888 return find(_root); 889 } 890 891 Node importNode(dom.Node node, bool deep) 892 { 893 switch (node.nodeType) with (dom.NodeType) 894 { 895 case attribute: 896 Attr result; 897 if (node.prefix) 898 result = createAttributeNS(node.namespaceURI, node.nodeName); 899 else 900 result = createAttribute(node.nodeName); 901 auto children = node.childNodes; 902 foreach (i; 0..children.length) 903 result.appendChild(importNode(children.item(i), true)); 904 return result; 905 case documentFragment: 906 auto result = createDocumentFragment(); 907 if (deep) 908 { 909 auto children = node.childNodes; 910 foreach (i; 0..children.length) 911 result.appendChild(importNode(children.item(i), deep)); 912 } 913 return result; 914 case element: 915 Element result; 916 if (node.prefix) 917 result = createElementNS(node.namespaceURI, node.nodeName); 918 else 919 result = createElement(node.nodeName); 920 if (node.hasAttributes) 921 { 922 auto attributes = node.attributes; 923 foreach (i; 0..attributes.length) 924 { 925 auto attr = cast(Attr)(importNode(attributes.item(i), deep)); 926 assert(attr); 927 result.setAttributeNode(attr); 928 } 929 } 930 if (deep) 931 { 932 auto children = node.childNodes; 933 foreach (i; 0..children.length) 934 result.appendChild(importNode(children.item(i), true)); 935 } 936 return result; 937 case processingInstruction: 938 return createProcessingInstruction(node.nodeName, node.nodeValue); 939 default: 940 throw new DOMException(dom.ExceptionCode.notSupported);//allocator.multiVersionMake!DOMException(this.outer, dom.ExceptionCode.notSupported); 941 } 942 } 943 Node adoptNode(dom.Node source) { return null; } 944 945 @property DOMString inputEncoding() { return null; } 946 @property DOMString xmlEncoding() { return null; } 947 948 @property bool xmlStandalone() { return _standalone; } 949 @property void xmlStandalone(bool b) { _standalone = b; } 950 951 @property DOMString xmlVersion() { return _xmlVersion; } 952 @property void xmlVersion(DOMString ver) 953 { 954 if (ver == "1.0" || ver == "1.1") 955 _xmlVersion = ver; 956 else 957 throw new DOMException(dom.ExceptionCode.notSupported);//allocator.multiVersionMake!DOMException(this.outer, dom.ExceptionCode.notSupported); 958 } 959 960 @property bool strictErrorChecking() { return _strictErrorChecking; } 961 @property void strictErrorChecking(bool b) { _strictErrorChecking = b; } 962 963 @property DOMString documentURI() { return _documentURI; } 964 @property void documentURI(DOMString uri) { _documentURI = uri; } 965 966 @property DOMConfiguration domConfig() { return _config; } 967 void normalizeDocument() { } 968 Node renameNode(dom.Node n, DOMString namespaceURI, DOMString qualifiedName) 969 { 970 auto node = cast(Node)n; 971 if (!node || node.ownerDocument !is this) 972 throw new DOMException(dom.ExceptionCode.wrongDocument);//allocator.multiVersionMake!DOMException(this.outer, dom.ExceptionCode.wrongDocument); 973 974 auto type = node.nodeType; 975 if (type != dom.NodeType.element && type != dom.NodeType.attribute) 976 throw new DOMException(dom.ExceptionCode.notSupported);//allocator.multiVersionMake!DOMException(this.outer, dom.ExceptionCode.notSupported); 977 978 auto withNs = (cast(NodeWithNamespace)node); 979 withNs.setQualifiedName(qualifiedName); 980 withNs._namespaceURI = namespaceURI; 981 return node; 982 } 983 } 984 private 985 { 986 DOMString _documentURI, _xmlVersion = new DOMString("1.0"w); 987 DocumentType _doctype; 988 Element _root; 989 DOMConfiguration _config; 990 bool _strictErrorChecking = true, _standalone = false; 991 } 992 // inherited from Node 993 override 994 { 995 @property dom.NodeType nodeType() { return dom.NodeType.document; } 996 @property DOMString nodeName() { return new DOMString("#document"w); } 997 998 DOMString lookupPrefix(DOMString namespaceURI) 999 { 1000 return documentElement.lookupPrefix(namespaceURI); 1001 } 1002 DOMString lookupNamespaceURI(DOMString prefix) 1003 { 1004 return documentElement.lookupNamespaceURI(prefix); 1005 } 1006 bool isDefaultNamespace(DOMString namespaceURI) 1007 { 1008 return documentElement.isDefaultNamespace(namespaceURI); 1009 } 1010 } 1011 // inherited from NodeWithChildren 1012 override 1013 { 1014 Node insertBefore(dom.Node newChild, dom.Node refChild) 1015 { 1016 if (newChild.nodeType == dom.NodeType.element) 1017 { 1018 if (_root) 1019 throw new DOMException(dom.ExceptionCode.hierarchyRequest);//allocator.multiVersionMake!DOMException(this.outer, dom.ExceptionCode.hierarchyRequest); 1020 1021 auto res = super.insertBefore(newChild, refChild); 1022 _root = cast(Element)newChild; 1023 return res; 1024 } 1025 else if (newChild.nodeType == dom.NodeType.documentType) 1026 { 1027 if (_doctype) 1028 throw new DOMException(dom.ExceptionCode.hierarchyRequest);//allocator.multiVersionMake!DOMException(this.outer, dom.ExceptionCode.hierarchyRequest); 1029 1030 auto res = super.insertBefore(newChild, refChild); 1031 _doctype = cast(DocumentType)newChild; 1032 return res; 1033 } 1034 else if (newChild.nodeType != dom.NodeType.comment && 1035 newChild.nodeType != dom.NodeType.processingInstruction) 1036 throw new DOMException(dom.ExceptionCode.hierarchyRequest);//allocator.multiVersionMake!DOMException(this.outer, dom.ExceptionCode.hierarchyRequest); 1037 else 1038 return super.insertBefore(newChild, refChild); 1039 } 1040 Node replaceChild(dom.Node newChild, dom.Node oldChild) 1041 { 1042 if (newChild.nodeType == dom.NodeType.element) 1043 { 1044 if (oldChild !is _root) 1045 throw new DOMException(dom.ExceptionCode.hierarchyRequest);//allocator.multiVersionMake!DOMException(this.outer, dom.ExceptionCode.hierarchyRequest); 1046 1047 auto res = super.replaceChild(newChild, oldChild); 1048 _root = cast(Element)newChild; 1049 return res; 1050 } 1051 else if (newChild.nodeType == dom.NodeType.documentType) 1052 { 1053 if (oldChild !is _doctype) 1054 throw new DOMException(dom.ExceptionCode.hierarchyRequest);//allocator.multiVersionMake!DOMException(this.outer, dom.ExceptionCode.hierarchyRequest); 1055 1056 auto res = super.replaceChild(newChild, oldChild); 1057 _doctype = cast(DocumentType)newChild; 1058 return res; 1059 } 1060 else if (newChild.nodeType != dom.NodeType.comment && 1061 newChild.nodeType != dom.NodeType.processingInstruction) 1062 throw new DOMException(dom.ExceptionCode.hierarchyRequest);//allocator.multiVersionMake!DOMException(this.outer, dom.ExceptionCode.hierarchyRequest); 1063 else 1064 return super.replaceChild(newChild, oldChild); 1065 } 1066 Node removeChild(dom.Node oldChild) 1067 { 1068 if (oldChild.nodeType == dom.NodeType.element) 1069 { 1070 auto res = super.removeChild(oldChild); 1071 _root = null; 1072 return res; 1073 } 1074 else if (oldChild.nodeType == dom.NodeType.documentType) 1075 { 1076 auto res = super.removeChild(oldChild); 1077 _doctype = null; 1078 return res; 1079 } 1080 else 1081 return super.removeChild(oldChild); 1082 } 1083 Node appendChild(dom.Node newChild) 1084 { 1085 if (newChild.nodeType == dom.NodeType.element) 1086 { 1087 if (_root) 1088 throw new DOMException(dom.ExceptionCode.hierarchyRequest);//allocator.multiVersionMake!DOMException(this.outer, dom.ExceptionCode.hierarchyRequest); 1089 1090 auto res = super.appendChild(newChild); 1091 _root = cast(Element)newChild; 1092 return res; 1093 } 1094 else if (newChild.nodeType == dom.NodeType.documentType) 1095 { 1096 if (_doctype) 1097 throw new DOMException(dom.ExceptionCode.hierarchyRequest);//allocator.multiVersionMake!DOMException(this.outer, dom.ExceptionCode.hierarchyRequest); 1098 1099 auto res = super.appendChild(newChild); 1100 _doctype = cast(DocumentType)newChild; 1101 return res; 1102 } 1103 else 1104 return super.appendChild(newChild); 1105 } 1106 } 1107 } 1108 alias ElementsByTagName = ElementsByTagNameImpl!false; 1109 alias ElementsByTagNameNS = ElementsByTagNameImpl!true; 1110 static class ElementsByTagNameImpl(bool ns) : dom.NodeList 1111 { 1112 package this() { 1113 1114 } 1115 private Node root; 1116 private Element current; 1117 static if (ns) 1118 private DOMString namespaceURI, localName; 1119 else 1120 private DOMString tagname; 1121 1122 private bool check(Node node) 1123 { 1124 static if (ns) 1125 { 1126 if (node.nodeType == dom.NodeType.element) 1127 { 1128 Element elem = cast(Element)node; 1129 return elem.namespaceURI == namespaceURI && elem.localName == localName; 1130 } 1131 else 1132 return false; 1133 } 1134 else 1135 return node.nodeType == dom.NodeType.element && node.nodeName == tagname; 1136 } 1137 1138 private Element findNext(Node node) 1139 { 1140 if (node.firstChild) 1141 { 1142 auto item = node.firstChild; 1143 if (check(item)) 1144 return cast(Element)item; 1145 else 1146 return findNext(item); 1147 } 1148 else 1149 return findNextBack(node); 1150 } 1151 private Element findNextBack(Node node) 1152 { 1153 if (node.nextSibling) 1154 { 1155 auto item = node.nextSibling; 1156 1157 if (check(item)) 1158 return cast(Element)item; 1159 else 1160 return findNext(item); 1161 } 1162 else if (node.parentNode && node.parentNode !is node.ownerDocument) 1163 return findNextBack(node.parentNode); 1164 else 1165 return null; 1166 } 1167 1168 // methods specific to NodeList 1169 override 1170 { 1171 @property size_t length() 1172 { 1173 size_t res = 0; 1174 auto node = findNext(root); 1175 while (node !is null) 1176 { 1177 //writeln("Found node ", node.nodeName); 1178 res++; 1179 node = findNext(node); 1180 } 1181 return res; 1182 } 1183 Element item(size_t i) 1184 { 1185 auto res = findNext(root); 1186 while (res && i > 0) 1187 { 1188 res = findNext(res); 1189 i--; 1190 } 1191 return res; 1192 } 1193 } 1194 // more idiomatic methods 1195 auto opIndex(size_t i) { return item(i); } 1196 1197 // range interface 1198 bool empty() { return current is null; } 1199 void popFront() { current = findNext(current); } 1200 auto front() { return current; } 1201 } 1202 /// Implementation of $(LINK2 ../dom/CharacterData, `std.experimental.xml.dom.CharacterData`) 1203 abstract class CharacterData : Node, dom.CharacterData 1204 { 1205 // specific to CharacterData 1206 override 1207 { 1208 @property DOMString data() { return _data; } 1209 @property void data(DOMString newVal) { _data = newVal; } 1210 @property size_t length() { return _data.length; } 1211 1212 DOMString substringData(size_t offset, size_t count) 1213 { 1214 if (offset > length) 1215 throw new DOMException(dom.ExceptionCode.indexSize);//allocator.multiVersionMake!DOMException(this.outer, dom.ExceptionCode.indexSize); 1216 1217 import std.algorithm : min; 1218 return _data[offset..min(offset + count, length)]; 1219 } 1220 void appendData(DOMString arg) 1221 { 1222 import std.traits : Unqual; 1223 1224 //auto newData = allocator.makeArray!(Unqual!(typeof(_data[0])))(_data.length + arg.length); 1225 1226 _data ~= arg; 1227 } 1228 void insertData(size_t offset, DOMString arg) 1229 { 1230 import std.traits : Unqual; 1231 1232 if (offset > length) 1233 throw new DOMException(dom.ExceptionCode.indexSize);//allocator.multiVersionMake!DOMException(this.outer, dom.ExceptionCode.indexSize); 1234 1235 //auto newData = allocator.makeArray!(Unqual!(typeof(_data[0])))(_data.length + arg.length); 1236 /+CharType[] newData; 1237 newData.reserve = _data.length + arg.length; 1238 newData = _data[0 .. offset]; 1239 newData[offset .. (offset + arg.length)] = arg; 1240 newData[(offset + arg.length) .. $] = _data[offset .. $]; 1241 1242 _data = cast(typeof(_data))newData;+/ 1243 _data.insertData(offset, arg); 1244 } 1245 void deleteData(size_t offset, size_t count) 1246 { 1247 _data.deleteData(offset, count); 1248 } 1249 void replaceData(size_t offset, size_t count, DOMString arg) 1250 { 1251 _data.deleteData(offset, count); 1252 _data.insertData(offset, arg); 1253 } 1254 } 1255 // inherited from Node 1256 override 1257 { 1258 @property DOMString nodeValue() { return data; } 1259 @property void nodeValue(DOMString newVal) 1260 { 1261 if (readonly) 1262 throw new DOMException(dom.ExceptionCode.noModificationAllowed);//allocator.multiVersionMake!DOMException(this.outer, dom.ExceptionCode.noModificationAllowed); 1263 data = newVal; 1264 } 1265 @property DOMString textContent() { return data; } 1266 @property void textContent(DOMString newVal) 1267 { 1268 if (readonly) 1269 throw new DOMException(dom.ExceptionCode.noModificationAllowed);//allocator.multiVersionMake!DOMException(this.outer, dom.ExceptionCode.noModificationAllowed); 1270 data = newVal; 1271 } 1272 } 1273 private 1274 { 1275 DOMString _data; 1276 1277 // internal method 1278 void performClone(CharacterData dest, bool deep) 1279 { 1280 super.performClone(dest, deep); 1281 dest._data = _data; 1282 } 1283 } 1284 } 1285 private abstract class NodeWithNamespace : NodeWithChildren 1286 { 1287 private 1288 { 1289 DOMString _name, _namespaceURI; 1290 size_t _colon; 1291 1292 void setQualifiedName(DOMString name) 1293 { 1294 import newxml.faststrings : fastIndexOf; 1295 1296 _name = name; 1297 ptrdiff_t i = name.getDString.fastIndexOf(':'); 1298 if (i > 0) 1299 _colon = i; 1300 } 1301 void performClone(NodeWithNamespace dest, bool deep) 1302 { 1303 super.performClone(dest, deep); 1304 dest._name = _name; 1305 dest._namespaceURI = namespaceURI; 1306 dest._colon = _colon; 1307 } 1308 } 1309 // inherited from Node 1310 override 1311 { 1312 @property DOMString nodeName() { return _name; } 1313 1314 @property DOMString localName() 1315 { 1316 if (!_colon) 1317 return null; 1318 return _name[(_colon+1)..$]; 1319 } 1320 @property DOMString prefix() 1321 { 1322 return _name[0.._colon]; 1323 } 1324 @property void prefix(DOMString pre) 1325 { 1326 if (readonly) 1327 throw new DOMException(dom.ExceptionCode.noModificationAllowed);//allocator.multiVersionMake!DOMException(this.outer, dom.ExceptionCode.noModificationAllowed); 1328 1329 import std.traits : Unqual; 1330 1331 /+auto newName = allocator.makeArray!(Unqual!(typeof(_name[0])))(pre.length + localName.length + 1); 1332 newName[0 .. pre.length] = pre[]; 1333 newName[pre.length] = ':'; 1334 newName[(pre.length + 1) .. $] = localName[];+/ 1335 1336 //_name = pre ~ ":" ~ localName; 1337 _name ~= pre; 1338 _name ~= "w"; 1339 _name ~= localName; 1340 _colon = pre.length; 1341 } 1342 @property DOMString namespaceURI() { return _namespaceURI; } 1343 } 1344 } 1345 /// Implementation of $(LINK2 ../dom/Attr, `std.experimental.xml.dom.Attr`) 1346 class Attr : NodeWithNamespace, dom.Attr 1347 { 1348 package this() { 1349 1350 } 1351 // specific to Attr 1352 override 1353 { 1354 /// Implementation of $(LINK2 ../dom/Attr.name, `std.experimental.xml.dom.Attr.name`). 1355 @property DOMString name() { return _name; } 1356 /// Implementation of $(LINK2 ../dom/Attr.specified, `std.experimental.xml.dom.Attr.specified`). 1357 @property bool specified() { return _specified; } 1358 /// Implementation of $(LINK2 ../dom/Attr.value, `std.experimental.xml.dom.Attr.value`). 1359 @property DOMString value() 1360 { 1361 1362 //auto result = Appender!(typeof(_name[0]), typeof(*allocator))(allocator); 1363 DOMString result = new DOMString(); 1364 auto child = rebindable(firstChild); 1365 while (child) 1366 { 1367 result ~= child.textContent;//result.put(child.textContent); 1368 child = child.nextSibling; 1369 } 1370 return result; 1371 } 1372 /// ditto 1373 @property void value(DOMString newVal) 1374 { 1375 while (firstChild) 1376 removeChild(firstChild); 1377 appendChild(ownerDocument.createTextNode(newVal)); 1378 } 1379 1380 /// Implementation of $(LINK2 ../dom/Attr.ownerElement, `std.experimental.xml.dom.Attr.ownerElement`). 1381 @property Element ownerElement() { return _ownerElement; } 1382 /// Implementation of $(LINK2 ../dom/Attr.schemaTypeInfo, `std.experimental.xml.dom.Attr.schemaTypeInfo`). 1383 @property dom.XMLTypeInfo schemaTypeInfo() { return null; } 1384 /// Implementation of $(LINK2 ../dom/Attr.isId, `std.experimental.xml.dom.Attr.isId`). 1385 @property bool isId() { return _isId; } 1386 } 1387 private 1388 { 1389 Element _ownerElement; 1390 bool _specified = true, _isId = false; 1391 @property Attr _nextAttr() { return cast(Attr)_nextSibling; } 1392 @property Attr _previousAttr() { return cast(Attr)_previousSibling; } 1393 } 1394 // inherited from Node 1395 override 1396 { 1397 @property dom.NodeType nodeType() { return dom.NodeType.attribute; } 1398 1399 @property DOMString nodeValue() { return value; } 1400 @property void nodeValue(DOMString newVal) 1401 { 1402 if (readonly) 1403 throw new DOMException(dom.ExceptionCode.noModificationAllowed);//allocator.multiVersionMake!DOMException(this.outer, dom.ExceptionCode.noModificationAllowed); 1404 value = newVal; 1405 } 1406 1407 // overridden because we reuse _nextSibling and _previousSibling with another meaning 1408 @property Attr nextSibling() { return null; } 1409 @property Attr previousSibling() { return null; } 1410 1411 Attr cloneNode(bool deep) 1412 { 1413 Attr cloned = new Attr();//allocator.multiVersionMake!Attr(this.outer); 1414 cloned._ownerDocument = _ownerDocument; 1415 super.performClone(cloned, true); 1416 cloned._specified = true; 1417 return cloned; 1418 } 1419 1420 DOMString lookupPrefix(DOMString namespaceURI) 1421 { 1422 if (ownerElement) 1423 return ownerElement.lookupPrefix(namespaceURI); 1424 return null; 1425 } 1426 DOMString lookupNamespaceURI(DOMString prefix) 1427 { 1428 if (ownerElement) 1429 return ownerElement.lookupNamespaceURI(prefix); 1430 return null; 1431 } 1432 bool isDefaultNamespace(DOMString namespaceURI) 1433 { 1434 if (ownerElement) 1435 return ownerElement.isDefaultNamespace(namespaceURI); 1436 return false; 1437 } 1438 } 1439 } 1440 /// Implementation of $(LINK2 ../dom/Element, `std.experimental.xml.dom.Element`) 1441 class Element : NodeWithNamespace, dom.Element 1442 { 1443 package this() { 1444 1445 } 1446 ///Created as a workaround to a common D compiler bug/artifact. 1447 package Map createMap() { 1448 return new Map(); 1449 } 1450 // specific to Element 1451 override 1452 { 1453 /// Implementation of $(LINK2 ../dom/Element.tagName, `std.experimental.xml.dom.Element.tagName`). 1454 @property DOMString tagName() { return _name; } 1455 1456 /++ 1457 + Implementation of $(LINK2 ../dom/Element.getAttribute, 1458 + `std.experimental.xml.dom.Element.getAttribute`). 1459 +/ 1460 DOMString getAttribute(DOMString name) 1461 { 1462 return _attrs.getNamedItem(name).value; 1463 } 1464 /++ 1465 + Implementation of $(LINK2 ../dom/Element.setAttribute, 1466 + `std.experimental.xml.dom.Element.setAttribute`). 1467 +/ 1468 void setAttribute(DOMString name, DOMString value) 1469 { 1470 auto attr = ownerDocument.createAttribute(name); 1471 attr.nodeValue = value; 1472 attr._ownerElement = this; 1473 _attrs.setNamedItem(attr); 1474 } 1475 /++ 1476 + Implementation of $(LINK2 ../dom/Element.removeAttribute, 1477 + `std.experimental.xml.dom.Element.removeAttribute`). 1478 +/ 1479 void removeAttribute(DOMString name) 1480 { 1481 _attrs.removeNamedItem(name); 1482 } 1483 1484 /++ 1485 + Implementation of $(LINK2 ../dom/Element.getAttributeNode, 1486 + `std.experimental.xml.dom.Element.getAttributeNode`). 1487 +/ 1488 Attr getAttributeNode(DOMString name) 1489 { 1490 return _attrs.getNamedItem(name); 1491 } 1492 /++ 1493 + Implementation of $(LINK2 ../dom/Element.setAttributeNode, 1494 + `std.experimental.xml.dom.Element.setAttributeNode`). 1495 +/ 1496 Attr setAttributeNode(dom.Attr newAttr) 1497 { 1498 return _attrs.setNamedItem(newAttr); 1499 } 1500 /++ 1501 + Implementation of $(LINK2 ../dom/Element.removeAttributeNode, 1502 + `std.experimental.xml.dom.Element.removeAttributeNode`). 1503 +/ 1504 Attr removeAttributeNode(dom.Attr oldAttr) 1505 { 1506 if (_attrs.getNamedItemNS(oldAttr.namespaceURI, oldAttr.name) is oldAttr) 1507 return _attrs.removeNamedItemNS(oldAttr.namespaceURI, oldAttr.name); 1508 else if (_attrs.getNamedItem(oldAttr.name) is oldAttr) 1509 return _attrs.removeNamedItem(oldAttr.name); 1510 1511 throw new DOMException(dom.ExceptionCode.notFound);//allocator.multiVersionMake!DOMException(this.outer, dom.ExceptionCode.notFound); 1512 } 1513 1514 /++ 1515 + Implementation of $(LINK2 ../dom/Element.getAttributeNS, 1516 + `std.experimental.xml.dom.Element.getAttributeNS`). 1517 +/ 1518 DOMString getAttributeNS(DOMString namespaceURI, DOMString localName) 1519 { 1520 return _attrs.getNamedItemNS(namespaceURI, localName).value; 1521 } 1522 /++ 1523 + Implementation of $(LINK2 ../dom/Element.setAttributeNS, 1524 + `std.experimental.xml.dom.Element.setAttributeNS`). 1525 +/ 1526 void setAttributeNS(DOMString namespaceURI, DOMString qualifiedName, DOMString value) 1527 { 1528 auto attr = ownerDocument.createAttributeNS(namespaceURI, qualifiedName); 1529 attr.nodeValue = value; 1530 attr._ownerElement = this; 1531 _attrs.setNamedItem(attr); 1532 } 1533 /++ 1534 + Implementation of $(LINK2 ../dom/Element.removeAttributeNS, 1535 + `std.experimental.xml.dom.Element.removeAttributeNS`). 1536 +/ 1537 void removeAttributeNS(DOMString namespaceURI, DOMString localName) 1538 { 1539 _attrs.removeNamedItemNS(namespaceURI, localName); 1540 } 1541 1542 /++ 1543 + Implementation of $(LINK2 ../dom/Element.getAttributeNodeNS, 1544 + `std.experimental.xml.dom.Element.getAttributeNodeNS`). 1545 +/ 1546 Attr getAttributeNodeNS(DOMString namespaceURI, DOMString localName) 1547 { 1548 return _attrs.getNamedItemNS(namespaceURI, localName); 1549 } 1550 /++ 1551 + Implementation of $(LINK2 ../dom/Element.setAttributeNodeNS, 1552 + `std.experimental.xml.dom.Element.setAttributeNodeNS`). 1553 +/ 1554 Attr setAttributeNodeNS(dom.Attr newAttr) 1555 { 1556 return _attrs.setNamedItemNS(newAttr); 1557 } 1558 1559 /++ 1560 + Implementation of $(LINK2 ../dom/Element.hasAttribute, 1561 + `std.experimental.xml.dom.Element.hasAttribute`). 1562 +/ 1563 bool hasAttribute(DOMString name) 1564 { 1565 return _attrs.getNamedItem(name) !is null; 1566 } 1567 /++ 1568 + Implementation of $(LINK2 ../dom/Element.hasAttributeNS, 1569 + `std.experimental.xml.dom.Element.hasAttributeNS`). 1570 +/ 1571 bool hasAttributeNS(DOMString namespaceURI, DOMString localName) 1572 { 1573 return _attrs.getNamedItemNS(namespaceURI, localName) !is null; 1574 } 1575 1576 /++ 1577 + Implementation of $(LINK2 ../dom/Element.setIdAttribute, 1578 + `std.experimental.xml.dom.Element.setIdAttribute`). 1579 +/ 1580 void setIdAttribute(DOMString name, bool isId) 1581 { 1582 auto attr = _attrs.getNamedItem(name); 1583 if (attr) 1584 attr._isId = isId; 1585 else 1586 throw new DOMException(dom.ExceptionCode.notFound);//allocator.multiVersionMake!DOMException(this.outer, dom.ExceptionCode.notFound); 1587 } 1588 /++ 1589 + Implementation of $(LINK2 ../dom/Element.setIdAttributeNS, 1590 + `std.experimental.xml.dom.Element.setIdAttributeNS`). 1591 +/ 1592 void setIdAttributeNS(DOMString namespaceURI, DOMString localName, bool isId) 1593 { 1594 auto attr = _attrs.getNamedItemNS(namespaceURI, localName); 1595 if (attr) 1596 attr._isId = isId; 1597 else 1598 throw new DOMException(dom.ExceptionCode.notFound);//allocator.multiVersionMake!DOMException(this.outer, dom.ExceptionCode.notFound); 1599 } 1600 /++ 1601 + Implementation of $(LINK2 ../dom/Element.getAttribute, 1602 + `std.experimental.xml.dom.Element.getAttribute`). 1603 +/ 1604 void setIdAttributeNode(dom.Attr idAttr, bool isId) 1605 { 1606 if (_attrs.getNamedItemNS(idAttr.namespaceURI, idAttr.name) is idAttr) 1607 (cast(Attr)idAttr)._isId = isId; 1608 else if (_attrs.getNamedItem(idAttr.name) is idAttr) 1609 (cast(Attr)idAttr)._isId = isId; 1610 else 1611 throw new DOMException(dom.ExceptionCode.notFound);//allocator.multiVersionMake!DOMException(this.outer, dom.ExceptionCode.notFound); 1612 } 1613 1614 /++ 1615 + Implementation of $(LINK2 ../dom/Element.getElementsByTagName, 1616 + `std.experimental.xml.dom.Element.getElementsByTagName`). 1617 +/ 1618 ElementsByTagName getElementsByTagName(DOMString tagname) 1619 { 1620 ElementsByTagName res = new ElementsByTagName();//allocator.multiVersionMake!ElementsByTagName; 1621 res.root = this; 1622 res.tagname = tagname; 1623 res.current = res.item(0); 1624 return res; 1625 } 1626 /++ 1627 + Implementation of $(LINK2 ../dom/Element.getElementsByTagNameNS, 1628 + `std.experimental.xml.dom.Element.getElementsByTagNameNS`). 1629 +/ 1630 ElementsByTagNameNS getElementsByTagNameNS(DOMString namespaceURI, DOMString localName) 1631 { 1632 ElementsByTagNameNS res = new ElementsByTagNameNS();//allocator.multiVersionMake!ElementsByTagNameNS; 1633 res.root = this; 1634 res.namespaceURI = namespaceURI; 1635 res.localName = localName; 1636 res.current = res.item(0); 1637 return res; 1638 } 1639 1640 /++ 1641 + Implementation of $(LINK2 ../dom/Element.schemaTypeInfo, 1642 + `std.experimental.xml.dom.Element.schemaTypeInfo`). 1643 +/ 1644 @property dom.XMLTypeInfo schemaTypeInfo() { return null; } 1645 } 1646 private 1647 { 1648 Map _attrs; 1649 1650 // internal methods 1651 DOMString lookupNamespacePrefix(DOMString namespaceURI, Element originalElement) 1652 { 1653 if (this.namespaceURI && this.namespaceURI == namespaceURI 1654 && this.prefix && originalElement.lookupNamespaceURI(this.prefix) == namespaceURI) 1655 { 1656 return this.prefix; 1657 } 1658 if (hasAttributes) 1659 foreach (attr; attributes) 1660 if (attr.prefix == "xmlns" && attr.nodeValue == namespaceURI && 1661 originalElement.lookupNamespaceURI(attr.localName) == namespaceURI) 1662 { 1663 return attr.localName; 1664 } 1665 auto parentElement = parentElement(); 1666 if (parentElement) 1667 return parentElement.lookupNamespacePrefix(namespaceURI, originalElement); 1668 return null; 1669 } 1670 } 1671 // inherited from Node 1672 override 1673 { 1674 @property dom.NodeType nodeType() { return dom.NodeType.element; } 1675 1676 @property Map attributes() { return _attrs.length > 0 ? _attrs : null; } 1677 bool hasAttributes() { return _attrs.length > 0; } 1678 1679 Element cloneNode(bool deep) 1680 { 1681 Element cloned = new Element();//allocator.multiVersionMake!Element(this.outer); 1682 cloned._ownerDocument = ownerDocument; 1683 cloned._attrs = new Map();//allocator.multiVersionMake!Map(this); 1684 super.performClone(cloned, deep); 1685 return cloned; 1686 } 1687 1688 DOMString lookupPrefix(DOMString namespaceURI) 1689 { 1690 return lookupNamespacePrefix(namespaceURI, this); 1691 } 1692 DOMString lookupNamespaceURI(DOMString prefix) 1693 { 1694 if (namespaceURI && prefix == prefix) 1695 return namespaceURI; 1696 1697 if (hasAttributes) 1698 { 1699 foreach (attr; attributes) 1700 if (attr.prefix == "xmlns" && attr.localName == prefix) 1701 return attr.nodeValue; 1702 else if (attr.nodeName == "xmlns" && !prefix) 1703 return attr.nodeValue; 1704 } 1705 auto parentElement = parentElement(); 1706 if (parentElement) 1707 return parentElement.lookupNamespaceURI(prefix); 1708 return null; 1709 } 1710 bool isDefaultNamespace(DOMString namespaceURI) 1711 { 1712 if (!prefix) 1713 return this.namespaceURI == namespaceURI; 1714 if (hasAttributes) 1715 { 1716 foreach (attr; attributes) 1717 if (attr.nodeName == "xmlns") 1718 return attr.nodeValue == namespaceURI; 1719 } 1720 auto parentElement = parentElement(); 1721 if (parentElement) 1722 return parentElement.isDefaultNamespace(namespaceURI); 1723 return false; 1724 } 1725 } 1726 1727 class Map : dom.NamedNodeMap 1728 { 1729 package this() { 1730 1731 } 1732 // specific to NamedNodeMap 1733 public override 1734 { 1735 size_t length() 1736 { 1737 size_t res = 0; 1738 auto attr = firstAttr; 1739 while (attr) 1740 { 1741 res++; 1742 attr = attr._nextAttr; 1743 } 1744 return res; 1745 } 1746 Attr item(size_t index) 1747 { 1748 size_t count = 0; 1749 auto res = firstAttr; 1750 while (res && count < index) 1751 { 1752 count++; 1753 res = res._nextAttr; 1754 } 1755 return res; 1756 } 1757 1758 Attr getNamedItem(DOMString name) 1759 { 1760 auto res = firstAttr; 1761 while (res && res.nodeName != name) 1762 res = res._nextAttr; 1763 return res; 1764 } 1765 Attr setNamedItem(dom.Node arg) 1766 { 1767 if (arg.ownerDocument !is this.outer.ownerDocument) 1768 throw new DOMException(dom.ExceptionCode.wrongDocument);//allocator.multiVersionMake!DOMException(this.outer.outer, dom.ExceptionCode.wrongDocument); 1769 1770 Attr attr = cast(Attr)arg; 1771 if (!attr) 1772 throw new DOMException(dom.ExceptionCode.hierarchyRequest);//allocator.multiVersionMake!DOMException(this.outer.outer, dom.ExceptionCode.hierarchyRequest); 1773 1774 if (attr._previousAttr) 1775 attr._previousAttr._nextSibling = attr._nextAttr; 1776 if (attr._nextAttr) 1777 attr._nextAttr._previousSibling = attr._previousAttr; 1778 1779 auto res = firstAttr; 1780 while (res && res.nodeName != attr.nodeName) 1781 res = res._nextAttr; 1782 1783 if (res) 1784 { 1785 attr._previousSibling = res._previousAttr; 1786 attr._nextSibling = res._nextAttr; 1787 if (res is firstAttr) firstAttr = attr; 1788 } 1789 else 1790 { 1791 attr._nextSibling = firstAttr; 1792 firstAttr = attr; 1793 attr._previousSibling = null; 1794 currentAttr = firstAttr; 1795 } 1796 1797 return res; 1798 } 1799 Attr removeNamedItem(DOMString name) 1800 { 1801 auto res = firstAttr; 1802 while (res && res.nodeName != name) 1803 res = res._nextAttr; 1804 1805 if (res) 1806 { 1807 if (res._previousAttr) 1808 res._previousAttr._nextSibling = res._nextAttr; 1809 if (res._nextAttr) 1810 res._nextAttr._previousSibling = res._previousAttr; 1811 return res; 1812 } 1813 else 1814 throw new DOMException(dom.ExceptionCode.notFound);//allocator.multiVersionMake!DOMException(this.outer.outer, dom.ExceptionCode.notFound); 1815 } 1816 1817 Attr getNamedItemNS(DOMString namespaceURI, DOMString localName) 1818 { 1819 auto res = firstAttr; 1820 while (res && (res.localName != localName || res.namespaceURI != namespaceURI)) 1821 res = res._nextAttr; 1822 return res; 1823 } 1824 Attr setNamedItemNS(dom.Node arg) 1825 { 1826 if (arg.ownerDocument !is this.outer.ownerDocument) 1827 throw new DOMException(dom.ExceptionCode.wrongDocument);//allocator.multiVersionMake!DOMException(this.outer.outer, dom.ExceptionCode.wrongDocument); 1828 1829 Attr attr = cast(Attr)arg; 1830 if (!attr) 1831 throw new DOMException(dom.ExceptionCode.hierarchyRequest);//allocator.multiVersionMake!DOMException(this.outer.outer, dom.ExceptionCode.hierarchyRequest); 1832 1833 if (attr._previousAttr) 1834 attr._previousAttr._nextSibling = attr._nextAttr; 1835 if (attr._nextAttr) 1836 attr._nextAttr._previousSibling = attr._previousAttr; 1837 1838 auto res = firstAttr; 1839 while (res && (res.localName != attr.localName || res.namespaceURI != attr.namespaceURI)) 1840 res = res._nextAttr; 1841 1842 if (res) 1843 { 1844 attr._previousSibling = res._previousAttr; 1845 attr._nextSibling = res._nextAttr; 1846 if (res is firstAttr) firstAttr = attr; 1847 } 1848 else 1849 { 1850 attr._nextSibling = firstAttr; 1851 firstAttr = attr; 1852 attr._previousSibling = null; 1853 currentAttr = firstAttr; 1854 } 1855 1856 return res; 1857 } 1858 Attr removeNamedItemNS(DOMString namespaceURI, DOMString localName) 1859 { 1860 auto res = firstAttr; 1861 while (res && (res.localName != localName || res.namespaceURI != namespaceURI)) 1862 res = res._nextAttr; 1863 1864 if (res) 1865 { 1866 if (res._previousAttr) 1867 res._previousAttr._nextSibling = res._nextAttr; 1868 if (res._nextAttr) 1869 res._nextAttr._previousSibling = res._previousAttr; 1870 return res; 1871 } 1872 else 1873 throw new DOMException(dom.ExceptionCode.notFound);//allocator.multiVersionMake!DOMException(this.outer.outer, dom.ExceptionCode.notFound); 1874 } 1875 } 1876 private 1877 { 1878 Attr firstAttr; 1879 Attr currentAttr; 1880 } 1881 // better methods 1882 auto opIndex(size_t i) { return item(i); } 1883 1884 // range interface 1885 auto opSlice() 1886 { 1887 struct Range 1888 { 1889 Attr currentAttr; 1890 1891 auto front() { return currentAttr; } 1892 void popFront() { currentAttr = currentAttr._nextAttr; } 1893 bool empty() { return currentAttr is null; } 1894 } 1895 return Range(firstAttr); 1896 } 1897 } 1898 } 1899 /// Implementation of $(LINK2 ../dom/Text, `std.experimental.xml.dom.Text`) 1900 class Text: CharacterData, dom.Text 1901 { 1902 package this() { 1903 1904 } 1905 // specific to Text 1906 override 1907 { 1908 /// Implementation of $(LINK2 ../dom/Text.splitText, `std.experimental.xml.dom.Text.splitText`). 1909 Text splitText(size_t offset) 1910 { 1911 if (offset > data.length) 1912 throw new DOMException(dom.ExceptionCode.indexSize);//allocator.multiVersionMake!DOMException(this.outer, dom.ExceptionCode.indexSize); 1913 auto second = ownerDocument.createTextNode(data[offset..$]); 1914 data = data[0..offset]; 1915 if (parentNode) 1916 { 1917 if (nextSibling) 1918 parentNode.insertBefore(second, nextSibling); 1919 else 1920 parentNode.appendChild(second); 1921 } 1922 return second; 1923 } 1924 /++ 1925 + Implementation of $(LINK2 ../dom/Text.isElementContentWhitespace, 1926 + `std.experimental.xml.dom.Text.isElementContentWhitespace`). 1927 +/ 1928 @property bool isElementContentWhitespace() 1929 { 1930 import newxml.faststrings: fastIndexOfNeither; 1931 1932 return _data.getDString.fastIndexOfNeither(" \r\n\t") == -1; 1933 } 1934 /// Implementation of $(LINK2 ../dom/Text.wholeText, `std.experimental.xml.dom.Text.wholeText`). 1935 @property DOMString wholeText() 1936 { 1937 Text findPreviousText(Text text) 1938 { 1939 Node node = text; 1940 do 1941 { 1942 if (node.previousSibling) 1943 switch (node.previousSibling.nodeType) with (dom.NodeType) 1944 { 1945 case text: 1946 case cdataSection: 1947 return cast(Text) node.previousSibling; 1948 case entityReference: 1949 if (cast(Text)(node.previousSibling.lastChild)) 1950 return cast(Text) node.previousSibling.lastChild; 1951 else 1952 return null; 1953 default: 1954 return null; 1955 } 1956 node = node.parentNode; 1957 } 1958 while (node && node.nodeType == dom.NodeType.entityReference); 1959 return null; 1960 } 1961 Text findNextText(Text text) 1962 { 1963 Node node = text; 1964 do 1965 { 1966 if (node.nextSibling) 1967 switch (node.nextSibling.nodeType) with (dom.NodeType) 1968 { 1969 case text: 1970 case cdataSection: 1971 return cast(Text) node.nextSibling; 1972 case entityReference: 1973 if (cast(Text)(node.nextSibling.firstChild)) 1974 return cast(Text) node.nextSibling.firstChild; 1975 else 1976 return null; 1977 default: 1978 return null; 1979 } 1980 node = node.parentNode; 1981 } 1982 while (node && node.nodeType == dom.NodeType.entityReference); 1983 return null; 1984 } 1985 1986 //import std.experimental.xml.appender; 1987 DOMString result;//auto result = Appender!(typeof(_data[0]), typeof(*allocator))(allocator); 1988 1989 Text node, prev = this; 1990 do 1991 { 1992 node = prev; 1993 prev = findPreviousText(node); 1994 } 1995 while (prev); 1996 while (node) 1997 { 1998 result ~= node.data; 1999 node = findNextText(node); 2000 } 2001 return result; 2002 } 2003 /++ 2004 + Implementation of $(LINK2 ../dom/Text.replaceWholeText, 2005 + `std.experimental.xml.dom.Text.replaceWholeText`). 2006 +/ 2007 // the W3C DOM spec explains the details of this 2008 @property Text replaceWholeText(DOMString newText) 2009 { 2010 bool hasOnlyText(Node reference) @safe 2011 { 2012 foreach (child; reference.childNodes) 2013 switch (child.nodeType) with (dom.NodeType) 2014 { 2015 case text: 2016 case cdataSection: 2017 break; 2018 case entityReference: 2019 if (!hasOnlyText(reference)) 2020 return false; 2021 break; 2022 default: 2023 return false; 2024 } 2025 return false; 2026 } 2027 bool startsWithText(Node reference) 2028 { 2029 if (!reference.firstChild) return false; 2030 switch (reference.firstChild.nodeType) with (dom.NodeType) 2031 { 2032 case text: 2033 case cdataSection: 2034 return true; 2035 case entityReference: 2036 return startsWithText(reference.firstChild); 2037 default: 2038 return false; 2039 } 2040 } 2041 bool endsWithText(Node reference) 2042 { 2043 if (!reference.lastChild) return false; 2044 switch (reference.lastChild.nodeType) with (dom.NodeType) 2045 { 2046 case text: 2047 case cdataSection: 2048 return true; 2049 case entityReference: 2050 return endsWithText(reference.lastChild); 2051 default: 2052 return false; 2053 } 2054 } 2055 2056 Node current; 2057 if (parentNode && parentNode.nodeType == dom.NodeType.entityReference) 2058 { 2059 current = parentNode; 2060 while (current.parentNode && current.parentNode.nodeType == dom.NodeType.entityReference) 2061 current = current.parentNode; 2062 2063 if (!hasOnlyText(current)) 2064 throw new DOMException(dom.ExceptionCode.noModificationAllowed);//allocator.multiVersionMake!DOMException(this.outer, dom.ExceptionCode.noModificationAllowed); 2065 } 2066 else if (readonly) 2067 throw new DOMException(dom.ExceptionCode.noModificationAllowed);//allocator.multiVersionMake!DOMException(this.outer, dom.ExceptionCode.noModificationAllowed); 2068 else 2069 current = this; 2070 2071 size_t previousToKill, nextToKill; 2072 auto node = current.previousSibling; 2073 while (node) 2074 { 2075 if (node.nodeType == dom.NodeType.entityReference) 2076 { 2077 if (endsWithText(node)) 2078 { 2079 if (!hasOnlyText(node)) 2080 throw new DOMException(dom.ExceptionCode.noModificationAllowed);//allocator.multiVersionMake!DOMException(this.outer, dom.ExceptionCode.noModificationAllowed); 2081 } 2082 else break; 2083 } 2084 else if (!cast(Text)node) 2085 break; 2086 2087 previousToKill++; 2088 node = node.previousSibling; 2089 } 2090 node = current.nextSibling; 2091 while (node) 2092 { 2093 if (node.nodeType == dom.NodeType.entityReference) 2094 { 2095 if (startsWithText(node)) 2096 { 2097 if (!hasOnlyText(node)) 2098 throw new DOMException(dom.ExceptionCode.noModificationAllowed);//allocator.multiVersionMake!DOMException(this.outer, dom.ExceptionCode.noModificationAllowed); 2099 } 2100 else break; 2101 } 2102 else if (!cast(Text)node) 2103 break; 2104 2105 nextToKill++; 2106 node = node.nextSibling; 2107 } 2108 2109 foreach (i; 0..previousToKill) 2110 current.parentNode.removeChild(current.previousSibling); 2111 foreach (i; 0..nextToKill) 2112 current.parentNode.removeChild(current.nextSibling); 2113 2114 if (current.nodeType == dom.NodeType.entityReference) 2115 { 2116 if (current.parentNode && newText) 2117 { 2118 auto result = ownerDocument.createTextNode(newText); 2119 current.parentNode.replaceChild(current, result); 2120 return result; 2121 } 2122 2123 if (current.parentNode) 2124 current.removeChild(current); 2125 2126 if (!newText) return null; 2127 2128 return ownerDocument.createTextNode(newText); 2129 } 2130 _data = newText; 2131 return this; 2132 } 2133 } 2134 // inherited from Node 2135 override 2136 { 2137 @property dom.NodeType nodeType() { return dom.NodeType.text; } 2138 @property DOMString nodeName() { return new DOMString("#text"); } 2139 2140 Text cloneNode(bool deep) 2141 { 2142 Text cloned = new Text();//allocator.multiVersionMake!Text(this.outer); 2143 cloned._ownerDocument = _ownerDocument; 2144 super.performClone(cloned, deep); 2145 return cloned; 2146 } 2147 } 2148 } 2149 /// Implementation of $(LINK2 ../dom/Comment, `std.experimental.xml.dom.Comment`) 2150 class Comment : CharacterData, dom.Comment 2151 { 2152 package this() { 2153 2154 } 2155 // inherited from Node 2156 override 2157 { 2158 @property dom.NodeType nodeType() { return dom.NodeType.comment; } 2159 @property DOMString nodeName() { return new DOMString("#comment"); } 2160 2161 Comment cloneNode(bool deep) 2162 { 2163 Comment cloned = new Comment();//allocator.multiVersionMake!Comment(this.outer); 2164 cloned._ownerDocument = _ownerDocument; 2165 super.performClone(cloned, deep); 2166 return cloned; 2167 } 2168 } 2169 } 2170 /// Implementation of $(LINK2 ../dom/DocumentType, `std.experimental.xml.dom.DocumentType`) 2171 class DocumentType : Node, dom.DocumentType 2172 { 2173 package this() { 2174 2175 } 2176 // specific to DocumentType 2177 override 2178 { 2179 /// Implementation of $(LINK2 ../dom/DocumentType.name, `std.experimental.xml.dom.DocumentType.name`). 2180 @property DOMString name() { return _name; } 2181 /++ 2182 + Implementation of $(LINK2 ../dom/DocumentType.entities, 2183 + `std.experimental.xml.dom.DocumentType.entities`). 2184 +/ 2185 @property dom.NamedNodeMap entities() { return null; } 2186 /++ 2187 + Implementation of $(LINK2 ../dom/DocumentType.notations, 2188 + `std.experimental.xml.dom.DocumentType.notations`). 2189 +/ 2190 @property dom.NamedNodeMap notations() { return null; } 2191 /++ 2192 + Implementation of $(LINK2 ../dom/DocumentType.publicId, 2193 + `std.experimental.xml.dom.DocumentType.publicId`). 2194 +/ 2195 @property DOMString publicId() { return _publicId; } 2196 /++ 2197 + Implementation of $(LINK2 ../dom/DocumentType.systemId, 2198 + `std.experimental.xml.dom.DocumentType.systemId`). 2199 +/ 2200 @property DOMString systemId() { return _systemId; } 2201 /++ 2202 + Implementation of $(LINK2 ../dom/DocumentType.internalSubset, 2203 + `std.experimental.xml.dom.DocumentType.internalSubset`). 2204 +/ 2205 @property DOMString internalSubset() { return _internalSubset; } 2206 } 2207 private DOMString _name, _publicId, _systemId, _internalSubset; 2208 // inherited from Node 2209 override 2210 { 2211 @property dom.NodeType nodeType() { return dom.NodeType.documentType; } 2212 @property DOMString nodeName() { return _name; } 2213 } 2214 } 2215 /// Implementation of $(LINK2 ../dom/CDATASection, `std.experimental.xml.dom.CDATASection`) 2216 class CDATASection : Text, dom.CDATASection 2217 { 2218 package this() { 2219 2220 } 2221 // inherited from Node 2222 override 2223 { 2224 @property dom.NodeType nodeType() { return dom.NodeType.cdataSection; } 2225 @property DOMString nodeName() { return new DOMString("#cdata-section"); } 2226 2227 CDATASection cloneNode(bool deep) 2228 { 2229 CDATASection cloned = new CDATASection();//allocator.multiVersionMake!CDATASection(this.outer); 2230 cloned._ownerDocument = _ownerDocument; 2231 super.performClone(cloned, deep); 2232 return cloned; 2233 } 2234 } 2235 } 2236 /// Implementation of $(LINK2 ../dom/ProcessingInstruction, `std.experimental.xml.dom.ProcessingInstruction`) 2237 class ProcessingInstruction : Node, dom.ProcessingInstruction 2238 { 2239 package this() { 2240 2241 } 2242 // specific to ProcessingInstruction 2243 override 2244 { 2245 /++ 2246 + Implementation of $(LINK2 ../dom/ProcessingInstruction.target, 2247 + `std.experimental.xml.dom.ProcessingInstruction.target`). 2248 +/ 2249 @property DOMString target() { return _target; } 2250 /++ 2251 + Implementation of $(LINK2 ../dom/ProcessingInstruction.data, 2252 + `std.experimental.xml.dom.ProcessingInstruction.data`). 2253 +/ 2254 @property DOMString data() { return _data; } 2255 /// ditto 2256 @property void data(DOMString newVal) { _data = newVal; } 2257 } 2258 private DOMString _target, _data; 2259 // inherited from Node 2260 override 2261 { 2262 @property dom.NodeType nodeType() { return dom.NodeType.processingInstruction; } 2263 @property DOMString nodeName() { return target; } 2264 @property DOMString nodeValue() { return _data; } 2265 @property void nodeValue(DOMString newVal) 2266 { 2267 if (readonly) 2268 throw new DOMException(dom.ExceptionCode.noModificationAllowed);//allocator.multiVersionMake!DOMException(this.outer, dom.ExceptionCode.noModificationAllowed); 2269 _data = newVal; 2270 } 2271 2272 @property DOMString textContent() { return _data; } 2273 @property void textContent(DOMString newVal) 2274 { 2275 if (readonly) 2276 throw new DOMException(dom.ExceptionCode.noModificationAllowed);//allocator.multiVersionMake!DOMException(this.outer, dom.ExceptionCode.noModificationAllowed); 2277 _data = newVal; 2278 } 2279 2280 ProcessingInstruction cloneNode(bool deep) 2281 { 2282 auto cloned = new ProcessingInstruction();//allocator.multiVersionMake!ProcessingInstruction(this.outer); 2283 cloned._ownerDocument = _ownerDocument; 2284 super.performClone(cloned, deep); 2285 cloned._target = _target; 2286 cloned._data = _data; 2287 return cloned; 2288 } 2289 } 2290 } 2291 /// Implementation of $(LINK2 ../dom/EntityReference, `std.experimental.xml.dom.EntityReference`) 2292 class EntityReference : NodeWithChildren, dom.EntityReference 2293 { 2294 package this() { 2295 2296 } 2297 // inherited from Node 2298 override 2299 { 2300 @property dom.NodeType nodeType() { return dom.NodeType.entityReference; } 2301 @property DOMString nodeName() { return _ent_name; } 2302 @property bool readonly() { return true; } 2303 } 2304 private DOMString _ent_name; 2305 } 2306 /// Implementation of $(LINK2 ../dom/DOMConfiguration, `std.experimental.xml.dom.DOMConfiguration`) 2307 class DOMConfiguration : dom.DOMConfiguration 2308 { 2309 import std.meta; 2310 import std.traits; 2311 package this() { 2312 2313 } 2314 private 2315 { 2316 enum string always = "((x) => true)"; 2317 2318 static struct Config 2319 { 2320 string name; 2321 string type; 2322 string settable; 2323 } 2324 2325 struct Params 2326 { 2327 @Config("cdata-sections", "bool", always) bool cdata_sections; 2328 @Config("comments", "bool", always) bool comments; 2329 @Config("entities", "bool", always) bool entities; 2330 //@Config("error-handler", "ErrorHandler", always) ErrorHandler error_handler; 2331 @Config("namespace-declarations", "bool", always) bool namespace_declarations; 2332 @Config("split-cdata-sections", "bool", always) bool split_cdata_sections; 2333 } 2334 Params params; 2335 2336 void assign(string field, string type)(dom.UserData val) @trusted 2337 { 2338 mixin("if (val.convertsTo!(" ~ type ~ ")) params." ~ field ~ " = val.get!(" ~ type ~ "); \n"); 2339 } 2340 bool canSet(string type, string settable)(dom.UserData val) @trusted 2341 { 2342 mixin("if (val.convertsTo!(" ~ type ~ ")) return " ~ settable ~ "(val.get!(" ~ type ~ ")); \n"); 2343 return false; 2344 } 2345 } 2346 // specific to DOMConfiguration 2347 override 2348 { 2349 /++ 2350 + Implementation of $(LINK2 ../dom/DOMConfiguration.setParameter, 2351 + `std.experimental.xml.dom.DOMConfiguration.setParameter`). 2352 +/ 2353 void setParameter(string name, dom.UserData value) @trusted 2354 { 2355 switch (name) 2356 { 2357 foreach (field; AliasSeq!(__traits(allMembers, Params))) 2358 { 2359 mixin("enum type = getUDAs!(Params." ~ field ~ ", Config)[0].type; \n"); 2360 mixin("case getUDAs!(Params." ~ field ~ ", Config)[0].name: assign!(field, type)(value); \n"); 2361 } 2362 default: 2363 throw new DOMException(dom.ExceptionCode.notFound);//allocator.multiVersionMake!DOMException(this.outer, dom.ExceptionCode.notFound); 2364 } 2365 } 2366 /++ 2367 + Implementation of $(LINK2 ../dom/DOMConfiguration.getParameter, 2368 + `std.experimental.xml.dom.DOMConfiguration.getParameter`). 2369 +/ 2370 dom.UserData getParameter(string name) @trusted 2371 { 2372 switch (name) 2373 { 2374 foreach (field; AliasSeq!(__traits(allMembers, Params))) 2375 { 2376 mixin("case getUDAs!(Params." ~ field ~ ", Config)[0].name: \n" ~ 2377 "return dom.UserData(params." ~ field ~ "); \n"); 2378 } 2379 default: 2380 throw new DOMException(dom.ExceptionCode.notFound);//allocator.multiVersionMake!DOMException(this.outer, dom.ExceptionCode.notFound); 2381 } 2382 } 2383 /++ 2384 + Implementation of $(LINK2 ../dom/DOMConfiguration.canSetParameter, 2385 + `std.experimental.xml.dom.DOMConfiguration.canSetParameter`). 2386 +/ 2387 bool canSetParameter(string name, dom.UserData value) @trusted 2388 { 2389 switch (name) 2390 { 2391 foreach (field; AliasSeq!(__traits(allMembers, Params))) 2392 { 2393 mixin("enum type = getUDAs!(Params." ~ field ~ ", Config)[0].type; \n"); 2394 mixin("enum settable = getUDAs!(Params." ~ field ~ ", Config)[0].settable; \n"); 2395 mixin("case getUDAs!(Params." ~ field ~ ", Config)[0].name: \n" ~ 2396 "return canSet!(type, settable)(value); \n"); 2397 } 2398 default: 2399 return false; 2400 } 2401 } 2402 /++ 2403 + Implementation of $(LINK2 ../dom/DOMConfiguration.parameterNames, 2404 + `std.experimental.xml.dom.DOMConfiguration.parameterNames`). 2405 +/ 2406 @property dom.DOMStringList parameterNames() 2407 { 2408 return new StringList(); 2409 } 2410 } 2411 2412 class StringList : dom.DOMStringList 2413 { 2414 package this() { 2415 2416 } 2417 private template MapToConfigName(Members...) 2418 { 2419 static if (Members.length > 0) 2420 mixin("alias MapToConfigName = AliasSeq!(getUDAs!(Params." ~ Members[0] ~ 2421 ", Config)[0].name, MapToConfigName!(Members[1..$])); \n"); 2422 else 2423 alias MapToConfigName = AliasSeq!(); 2424 } 2425 static immutable string[] arr = [MapToConfigName!(__traits(allMembers, Params))]; 2426 2427 // specific to DOMStringList 2428 override 2429 { 2430 DOMString item(size_t index) { return new DOMString(arr[index]); } 2431 size_t length() { return arr.length; } 2432 2433 bool contains(DOMString str) 2434 { 2435 import std.algorithm: canFind; 2436 return arr.canFind(str); 2437 } 2438 } 2439 //alias arr this; 2440 } 2441 } 2442 } 2443 2444 /++ 2445 + Instantiates a `DOMBuilder` specialized for the `DOMImplementation` implemented 2446 + in this module. 2447 +/ 2448 /+auto domBuilder(CursorType)(auto ref CursorType cursor) 2449 { 2450 //import std.experimental.allocator.gc_allocator;//import stdx.allocator.gc_allocator; 2451 import dompar = std.experimental.xml.domparser; 2452 return dompar.domBuilder(cursor, new DOMImplementation());//return dompar.domBuilder(cursor, new DOMImplementation!(CursorType.StringType, shared(GCAllocator))()); 2453 }+/ 2454 2455 unittest 2456 { 2457 2458 DOMImplementation impl = new DOMImplementation(); 2459 2460 auto doc = impl.createDocument(new DOMString("myNamespaceURI"), new DOMString("myPrefix:myRootElement"), null); 2461 auto root = doc.documentElement; 2462 assert(root.prefix == "myPrefix"); 2463 2464 auto attr = doc.createAttributeNS(new DOMString("myAttrNamespace"), new DOMString("myAttrPrefix:myAttrName")); 2465 root.setAttributeNode(attr); 2466 assert(root.attributes.length == 1); 2467 assert(root.getAttributeNodeNS(new DOMString("myAttrNamespace"), new DOMString("myAttrName")) is attr); 2468 2469 attr.nodeValue = new DOMString("myAttrValue"); 2470 assert(attr.childNodes.length == 1); 2471 assert(attr.firstChild.nodeType == dom.NodeType.text); 2472 assert(attr.firstChild.nodeValue == attr.nodeValue); 2473 2474 auto elem = doc.createElementNS(new DOMString("myOtherNamespace"), new DOMString("myOtherPrefix:myOtherElement")); 2475 assert(root.ownerDocument is doc); 2476 assert(elem.ownerDocument is doc); 2477 root.appendChild(elem); 2478 assert(root.firstChild is elem); 2479 assert(root.firstChild.namespaceURI == "myOtherNamespace"); 2480 2481 auto comm = doc.createComment(new DOMString("myWonderfulComment")); 2482 doc.insertBefore(comm, root); 2483 assert(doc.childNodes.length == 2); 2484 assert(doc.firstChild is comm); 2485 2486 assert(comm.substringData(1, 4) == "yWon"); 2487 comm.replaceData(0, 2, new DOMString("your")); 2488 comm.deleteData(4, 9); 2489 comm.insertData(4, new DOMString("Questionable")); 2490 assert(comm.data == "yourQuestionableComment"); 2491 2492 auto pi = doc.createProcessingInstruction(new DOMString("myPITarget"), new DOMString("myPIData")); 2493 elem.appendChild(pi); 2494 assert(elem.lastChild is pi); 2495 auto cdata = doc.createCDATASection(new DOMString("mycdataContent")); 2496 elem.replaceChild(cdata, pi); 2497 assert(elem.lastChild is cdata); 2498 elem.removeChild(cdata); 2499 assert(elem.childNodes.length == 0); 2500 2501 assert(doc.getElementsByTagNameNS(new DOMString("myOtherNamespace"), new DOMString("myOtherElement")).item(0) is elem); 2502 2503 doc.setUserData("userDataKey1", dom.UserData(3.14), null); 2504 doc.setUserData("userDataKey2", dom.UserData(new Object()), null); 2505 doc.setUserData("userDataKey3", dom.UserData(null), null); 2506 assert(doc.getUserData("userDataKey1") == 3.14); 2507 assert(doc.getUserData("userDataKey2").type == typeid(Object)); 2508 assert(doc.getUserData("userDataKey3").peek!long is null); 2509 2510 assert(elem.lookupNamespaceURI(new DOMString("myOtherPrefix")) == "myOtherNamespace"); 2511 assert(doc.lookupPrefix(new DOMString("myNamespaceURI")) == "myPrefix"); 2512 2513 assert(elem.isEqualNode(elem.cloneNode(false))); 2514 assert(root.isEqualNode(root.cloneNode(true))); 2515 assert(comm.isEqualNode(comm.cloneNode(false))); 2516 assert(pi.isEqualNode(pi.cloneNode(false))); 2517 } 2518 2519 unittest 2520 { 2521 import newxml.lexers; 2522 import newxml.parser; 2523 import newxml.cursor; 2524 //import newxml.domparser; 2525 import std.stdio; 2526 2527 string xml = q"{ 2528 <?xml version = '1.0' standalone = 'yes'?> 2529 <books> 2530 <book ISBN = '078-5342635362'> 2531 <title>The D Programming Language</title> 2532 <author>A. Alexandrescu</author> 2533 </book> 2534 <book ISBN = '978-1515074601'> 2535 <title>Programming in D</title> 2536 <author>Ali Çehreli</author> 2537 </book> 2538 <book ISBN = '978-0201704310' about-d = 'no'> 2539 <title>Modern C++ Design</title> 2540 <author>A. Alexandrescu</author> 2541 </book> 2542 </books> 2543 }"; 2544 2545 /+auto builder = 2546 xml 2547 .lexer 2548 .parser 2549 .cursor 2550 .domBuilder; 2551 2552 builder.setSource(xml); 2553 builder.buildRecursive; 2554 2555 auto doc = builder.getDocument; 2556 auto books = doc.getElementsByTagName("book"); 2557 auto authors = doc.getElementsByTagName("author"); 2558 auto titles = doc.getElementsByTagName("title"); 2559 2560 assert(doc.xmlVersion == "1.0"); 2561 assert(doc.xmlStandalone); 2562 2563 enum Pos(dom.DocumentPosition pos) = cast(BitFlags!(dom.DocumentPosition))pos; 2564 with (dom.DocumentPosition) 2565 { 2566 assert(books[1].compareDocumentPosition(authors[2]) == Pos!following); 2567 assert(authors[2].compareDocumentPosition(titles[0]) == Pos!preceding); 2568 assert(books[1].compareDocumentPosition(titles[1]) == (Pos!containedBy | Pos!following)); 2569 assert(authors[0].compareDocumentPosition(books[0]) == (Pos!contains | Pos!preceding)); 2570 assert(titles[2].compareDocumentPosition(titles[2]) == Pos!none); 2571 assert(books[2].attributes[0].compareDocumentPosition(books[2].attributes[1]) 2572 == (Pos!implementationSpecific | Pos!following)); 2573 assert(books[2].attributes[1].compareDocumentPosition(books[2].attributes[0]) 2574 == (Pos!implementationSpecific | Pos!preceding)); 2575 } 2576 2577 assert(books[1].cloneNode(true).childNodes[1].isEqualNode(authors[1])); 2578 2579 books[2].setIdAttributeNode(books[2].attributes[1], true); 2580 assert(books[2].attributes[1].isId); 2581 assert(doc.getElementById("978-0201704310") is books[2]); 2582 2583 alias Text = typeof(doc.implementation).Text; 2584 titles[1].appendChild(doc.createTextNode(" for Dummies")); 2585 assert((cast(Text)(titles[1].firstChild)).wholeText == "Programming in D for Dummies"); 2586 (cast(Text)(titles[1].lastChild)).replaceWholeText(titles[1].firstChild.textContent); 2587 assert(titles[1].textContent == "Programming in D"); 2588 assert(titles[1].childNodes.length == 1);+/ 2589 } 2590