1 /** 2 * Copyright: Copyright (c) 2009 Jacob Carlborg. 3 * Authors: Jacob Carlborg 4 * Version: Initial created: May 18, 2009 5 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) 6 */ 7 module mambo.xml.SimpleXML; 8 9 10 import tango.io.device.File; 11 import tango.text.xml.Document; 12 import tango.text.xml.DocPrinter; 13 import tango.text.xml.PullParser; 14 import Convert = tango.util.Convert; 15 16 /** 17 * XML documents. The tradeoff with such a scheme is that copying 18 * 19 * 20 * Implements an easy to use interface on top of a DOM Document. 21 * 22 * Parse example: 23 * --- 24 * auto doc = new SimpleXML!(char)(content); 25 * Stdout(doc).newline; 26 * --- 27 * 28 * API example: 29 * --- 30 * auto doc = new SimpleXML!(char); 31 * 32 * // attach an xml header 33 * doc.header; 34 * 35 * // attach an element with some attributes, plus 36 * // a child element with an attached data value 37 * doc ~ "element" 38 * ~ doc.Attribute("attrib1", "value") 39 * ~ doc.Attribute("attrib2") 40 * ~ doc.Element("child", "value"); 41 * 42 * // attach a single child element to the root element 43 * // and a single attribute to the root element 44 * doc ~= "element2"; 45 * doc.attribute ~= "attri3"; 46 * --- 47 * 48 * X-Path example: 49 * --- 50 * auto doc = new SimpleXML!(char); 51 * 52 * // attach an xml header 53 * doc.header; 54 * 55 * // attach an element with some attributes, plus 56 * // a child element with an attached data value 57 * doc ~ "element" 58 * ~ doc.Attribute("attrib1", "value") 59 * ~ doc.Attribute("attrib2") 60 * ~ doc.Element("child", "value"); 61 * 62 * // select named-element 63 * auto set = doc["child"]; 64 * 65 * // select the first attribute named " 66 * // attrib2" in the root element 67 * auto att = doc.attribute("attrib2"); 68 * 69 * // get the value of the first attribute 70 * // named attrib1 in the root element 71 * char[] value = doc.attribute["attrib1"]; 72 * --- 73 */ 74 class SimpleXML (T) : Document!(T) 75 { 76 private DocPrinter!(T) printer; 77 78 /** 79 * XML documents. The tradeoff with such a scheme is that copying 80 * 81 * Constructs a SimpleXML instance. 82 * 83 * Params: 84 * nodes = the initial number of nodes assigned to the freelist 85 */ 86 this (uint nodes = 1000) 87 { 88 super(nodes); 89 printer = new DocPrinter!(char); 90 91 if (elements) 92 attribute.node = elements; 93 94 else 95 attribute.node = tree; 96 } 97 98 /** 99 * Constructs a SimpleXML instance and parses the given content. 100 * 101 * Params: 102 * xml = the content to parse 103 */ 104 this (T[] xml) 105 { 106 this(); 107 parse(xml); 108 } 109 110 /** 111 * Prepend an XML header to the document tree. 112 * 113 * Params: 114 * encoding = the encoding of the XML header 115 * 116 * Returns: this 117 */ 118 final SimpleXML header (T[] encoding = null) 119 { 120 super.header = encoding; 121 return this; 122 } 123 124 /** 125 * Returns the first child element of the root element, which 126 * matches the given name. 127 * 128 * Params: 129 * name = the name of the child element 130 * 131 * Returns: the child element or null 132 */ 133 final Node opIndex (T[] name) 134 { 135 if (!elements) 136 return Node.init; 137 138 auto set = elements.query[name]; 139 140 if (set.count > 0) 141 return Node(set.nodes[0]); 142 143 return Node.init; 144 } 145 146 /** 147 * Finds the first child element of the root element with the given 148 * name and sets it's value to the given value. If the element 149 * could not be found it's created and the give value is set. 150 * 151 * Params: 152 * value = the value to set 153 * name = the name of the element 154 */ 155 final void opIndexAssign (T2) (T2 value, T[] name) 156 { 157 auto node = this[name]; 158 T[] v; 159 160 static if (is(T2 : T[])) 161 v = value; 162 163 else static if (is(T2 == bool)) 164 T[] v = value ? "1" : "0"; 165 166 else static if (is(T2 : long)) 167 T[] v = Convert.to!(T[])(value); 168 169 if (node.node) 170 node.value = value; 171 172 else 173 { 174 if (elements) 175 elements.element(null, name, value); 176 177 else 178 tree.element(null, name, value); 179 } 180 } 181 182 /** 183 * Iterates over the children of the root node 184 * 185 * Params: 186 * dg = the node and the value of the node will be passed to this for every iteration 187 * 188 * Returns: 189 */ 190 final int opApply (int delegate(ref Node) dg) 191 { 192 if (!elements) 193 return 0; 194 195 int result; 196 197 foreach (n ; elements.children) 198 if (n.type == XmlNodeType.Element && (result = dg(Node(n))) != 0) 199 break; 200 201 return result; 202 } 203 204 /** 205 * Adds a new element to the root element of the document. 206 * 207 * Params: 208 * name = the name of the element 209 * value = the value of the element 210 * 211 * Returns:a reference to the element 212 */ 213 final Node add (T[] name, T[] value = null) 214 { 215 if (elements) 216 return Node(elements.element(null, name, value)); 217 218 Node node = Node(tree.element(null, name, value)); 219 attribute.node = node.node; 220 221 return node; 222 } 223 224 /** 225 * Adds a new element to the root element of the document. 226 * 227 * Params: 228 * name = the name of the element 229 * value = the value of the element 230 * 231 * Returns:a reference to the element 232 */ 233 final Node add (Element element) 234 { 235 if (elements) 236 return Node(elements.element(null, element.name, element.value)); 237 238 Node node = Node(tree.element(null, element.name, element.value)); 239 attribute.node = node.node; 240 241 return node; 242 } 243 244 /** 245 * Adds the given attribute to the root element of the document. 246 * 247 * Params: 248 * element = the element to add 249 * 250 * Returns: a reference to the element 251 */ 252 final Node add (Attribute attribute) 253 { 254 if (elements) 255 return Node(elements.attribute(null, attribute.name, attribute.value)); 256 257 Node node = Node(tree.attribute(null, attribute.name, attribute.value)); 258 SimpleXML.attribute.node = node.node; 259 260 return node; 261 } 262 263 /// 264 alias add opCat; 265 266 /// 267 alias add opCatAssign; 268 269 /** 270 * Returns a string representation of the document. It will return 271 * the whole tree as a string. 272 * 273 * Returns: a string representation of the document 274 */ 275 char[] toString () 276 { 277 return printer.print(this); 278 } 279 280 /** 281 * This struct represents a node in the tree 282 */ 283 static struct Node 284 { 285 private Document!(T).Node node; 286 287 /** 288 * This is an attribute proxy allowing to perform attribute 289 * operations on all the attributes in this element. 290 */ 291 AttributeProxy attribute; 292 293 /** 294 * Creates a new Node. 295 * 296 * Params: 297 * node = the internal node implementation 298 * 299 * Returns: the newly create node 300 */ 301 static Node opCall (Document!(T).Node node) 302 { 303 Node n; 304 n.node = node; 305 n.attribute.node = node; 306 307 return n; 308 } 309 310 /** 311 * Gets the name of the node. 312 * 313 * Returns: the name of the node 314 */ 315 T[] name () 316 { 317 return node.name; 318 } 319 320 /** 321 * Sets the name of the node. 322 * 323 * Params: 324 * name = the name to set 325 * 326 * Returns: the name of the node 327 */ 328 T[] name (T[] name) 329 { 330 node.name = name; 331 return node.name; 332 } 333 334 /** 335 * Gets the value of the node. 336 * 337 * Returns: the value of the node 338 */ 339 T[] value () 340 { 341 return node.value; 342 } 343 344 /** 345 * Sets the value of the node. 346 * 347 * Params: 348 * value = the value to set 349 * 350 * Returns: the value of the noe 351 */ 352 T[] value (T[] value) 353 { 354 node.value = value; 355 return node.value; 356 } 357 358 /** 359 * Returns the first child element of the node, which matches the 360 * given name. 361 * 362 * Params: 363 * name = the name of the element 364 * 365 * Returns: the child element or this 366 */ 367 Node opIndex (T[] name) 368 { 369 auto set = node.query[name]; 370 371 if (set.count > 0) 372 return Node(set.nodes[0]); 373 374 return *this; 375 } 376 377 /** 378 * Iterates over the children of the node 379 * 380 * Params: 381 * dg = the node and the value of the node will be passed to this for every iteration 382 * 383 * Returns: 384 */ 385 int opApply (int delegate(ref Node) dg) 386 { 387 if (!node) 388 return 0; 389 390 int result; 391 392 foreach (n ; node.children) 393 if (n.type == XmlNodeType.Element && (result = dg(Node(n))) != 0) 394 break; 395 396 return result; 397 } 398 399 /** 400 * Adds a new element to this element. 401 * 402 * Params: 403 * name = the name of the element 404 * value = the value of the element 405 * 406 * Returns: 407 */ 408 Node add (T[] name, T[] value = null) 409 { 410 return Node(node.element(null, name, value)); 411 } 412 413 /** 414 * Adds a new element to this element. 415 * 416 * Params: 417 * name = the name of the element 418 * 419 * Returns: a reference to the element 420 */ 421 Node add (SimpleXML.Element element) 422 { 423 return Node(node.element(null, element.name, element.value)); 424 } 425 426 /** 427 * Adds a new attribute to this element. 428 * 429 * Params: 430 * attribute = the attribute 431 * 432 * Returns: a reference to the element 433 */ 434 Node add (Attribute attribute) 435 { 436 return Node(node.attribute(null, attribute.name, attribute.value)); 437 } 438 439 /// 440 alias add opCat; 441 442 /// 443 alias add opCatAssign; 444 445 /** 446 * Finds the first child element of this element with the given 447 * name and sets it's value to the given value. If the element 448 * could not be found it's created and the give value is set. 449 * 450 * Params: 451 * value = the value to set 452 * name = the name of the element 453 */ 454 void opIndexAssign (T2) (T2 value, T[] name) 455 { 456 static if (is(T2 : T[])) 457 { 458 if (auto node = node.attributes.name(null, name)) 459 node.value = value; 460 461 else 462 node.attribute(null, name, value); 463 } 464 465 else static if (is(T2 == bool)) 466 { 467 T[] v = value ? "1" : "0"; 468 469 if (auto n = node.attributes.name(null, name)) 470 n.value = v; 471 472 else 473 node.attribute(null, name, v); 474 } 475 476 else static if (is(T2 : long)) 477 { 478 T[] v = Convert.to!(T[])(value); 479 480 if (auto n = node.attributes.name(null, name)) 481 n.value = v; 482 483 else 484 node.attribute(null, name, v); 485 } 486 } 487 488 /** 489 * Returns a string representation of this object which is the 490 * value of the node 491 * 492 * Returns: a string representation (the value) 493 */ 494 T[] toString () 495 { 496 return value; 497 } 498 499 /** 500 * This struct represents an attribute proxy. This is the type 501 * that is returned when calling Node.attribute and lets you 502 * perform operations on attributes. 503 */ 504 static struct AttributeProxy 505 { 506 private Document!(T).Node node; 507 508 /** 509 * Finds the attribute with the given name in the element of 510 * which this attribute proxy was returned. 511 * 512 * Params: 513 * name = the name of the attribute 514 * 515 * Returns: an attribute proxy 516 */ 517 AttributeProxy opCall (T[] name) 518 { 519 AttributeProxy a; 520 a.node = node.attributes.name(null, name); 521 522 return a; 523 } 524 525 /** 526 * Gets the name of the attribute 527 * 528 * Returns: the name of the attribute 529 */ 530 T[] name () 531 { 532 return node.name; 533 } 534 535 /** 536 * Sets the name of the attribute 537 * 538 * Params: 539 * name = the name of the attribute 540 * 541 * Returns: the name of the attribute 542 */ 543 T[] name (T[] name) 544 { 545 node.name = name; 546 return node.name; 547 } 548 549 /** 550 * Gets the value of the attribute 551 * 552 * Returns: the value of the attribute 553 */ 554 T[] value () 555 { 556 return node.value; 557 } 558 559 /** 560 * Sets the value of the attribute 561 * 562 * Params: 563 * value = the value of the attribute 564 * 565 * Returns: the value of the attribute 566 */ 567 T[] value (T[] value) 568 { 569 node.value = value; 570 return node.value; 571 } 572 573 /** 574 * Returns the value of the attribute with the given name 575 * 576 * Params: 577 * name = the name of the attribute 578 * 579 * Returns: the value of the attribute of null 580 */ 581 T[] opIndex (T[] name) 582 { 583 if (auto node = node.attributes.name(null, name)) 584 return node.value; 585 586 return null; 587 } 588 589 /** 590 * Finds the attribute with the given name in the element of which 591 * this proxy object was received name and sets it's value to the 592 * given value. If the element could not be found it's created and 593 * the give value is set. 594 * 595 * Params: 596 * value = the value to set 597 * name = the name of the element 598 */ 599 void opIndexAssign (T2) (T2 value, T[] name) 600 { 601 static if (is(T2 : T[])) 602 { 603 if (auto node = node.attributes.name(null, name)) 604 node.value = value; 605 else 606 node.attribute(null, name, value); 607 } 608 609 else static if (is(T2 == bool)) 610 { 611 T[] v = value ? "1" : "0"; 612 613 if (auto n = node.attributes.name(null, name)) 614 n.value = v; 615 616 else 617 node.attribute(null, name, v); 618 } 619 620 else static if (is(T2 : long)) 621 { 622 T[] v = Convert.to!(T[])(value); 623 624 if (auto n = node.attributes.name(null, name)) 625 n.value = v; 626 627 else 628 node.attribute(null, name, v); 629 } 630 } 631 632 /** 633 * Returns the value of the first attribute in the element of which 634 * this proxy object was received, which matches the given name. 635 * 636 * Params: 637 * name = the name of the attribute 638 * aDefault = the default return value 639 * 640 * Returns: true if the attribute was found and it's value was 1, otherwise aDefault 641 */ 642 bool getBool (T[] name, bool aDefault = false) 643 { 644 return (*this)[name] == "1" ? true : aDefault; 645 } 646 647 /** 648 * Returns the value of the first attribute in the element of which 649 * this proxy object was received, which matches the given name. 650 * 651 * Params: 652 * name = the name of the attribute 653 * aDefault = the default return value 654 * 655 * Returns: the value of the attribute if it was found and could be 656 * converted to a long, otherwise aDefault. 657 */ 658 long getLong (T[] name, long aDefault = 0) 659 { 660 return Convert.to!(long)((*this)[name], aDefault); 661 } 662 663 /** 664 * Returns the value of the first attribute in the element of which 665 * this proxy object was received, which matches the given name. 666 * 667 * Params: 668 * name = the name of the attribute 669 * aDefault = the default return value 670 * 671 * Returns: the value of the attribute if it was found and could be 672 * converted to an int, otherwise aDefault. 673 */ 674 int getInt (T[] name, int aDefault = 0) 675 { 676 return Convert.to!(int)((*this)[name], aDefault); 677 } 678 679 /** 680 * Adds a new attribute to the element the element of which this 681 * proxy object was received. 682 * 683 * Params: 684 * name = the name of the attribute 685 * value = the value of the attribute 686 * 687 * Returns: the element this attribute was added 688 */ 689 Node add (T[] name, T[] value = null) 690 { 691 return Node(node.attribute(null, name, value)); 692 } 693 694 /** 695 * Adds a new attribute to the element the element of which this 696 * proxy object was received. 697 * 698 * Params: 699 * name = the name of the element 700 * 701 * Returns: the element this attribute was added 702 */ 703 Node add (Attribute attribute) 704 { 705 return Node(node.attribute(null, attribute.name, attribute.value)); 706 } 707 708 709 /** 710 * Adds a new attribute to the element the element of which this 711 * proxy object was received. 712 * 713 * Params: 714 * name = the name of the attribute 715 * value = the value of the attribute 716 * 717 * Returns: the element this attribute was added 718 */ 719 Node add (T[] name, bool value) 720 { 721 return Node(node.attribute(null, name, value ? "1" : "0")); 722 } 723 724 /** 725 * Adds a new attribute to the element the element of which this 726 * proxy object was received. 727 * 728 * Params: 729 * name = the name of the attribute 730 * value = the value of the attribute 731 * 732 * Returns: the element this attribute was added 733 */ 734 Node add (T[] name, int value) 735 { 736 return Node(node.attribute(null, name, Convert.toString(value))); 737 } 738 739 /// 740 //alias add opCat; 741 742 /// 743 //alias add opCatAssign; 744 745 Node opCatAssign (T[] name) 746 { 747 return Node(node.attribute(null, name, null)); 748 } 749 750 /** 751 * Iterates over the children of the root node 752 * 753 * Params: 754 * dg = the attribute proxy be passed to this for every iteration 755 * 756 * Returns: 757 */ 758 int opApply (int delegate(ref AttributeProxy) dg) 759 { 760 if (!node) 761 return 0; 762 763 int result; 764 765 foreach (n ; node.attributes) 766 { 767 AttributeProxy a; 768 a.node = n; 769 770 if (n.type == XmlNodeType.Attribute && (result = dg(a)) != 0) 771 break; 772 } 773 774 return result; 775 } 776 777 /** 778 * Returns a string representation of this object which is the 779 * value of the attribute. 780 * 781 * Returns: a string representation (the value) 782 */ 783 T[] toString () 784 { 785 return value; 786 } 787 } 788 } 789 790 /** 791 * This is an attribute proxy allowing to perform attribute 792 * operations on all the attributes in the root element of this 793 * document. 794 */ 795 Node.AttributeProxy attribute; 796 797 /// This is a name-value pair representing an attribute 798 struct Attribute 799 { 800 /// The name of this attribute 801 T[] name; 802 803 /// The value of this attribute 804 T[] value; 805 } 806 807 /// This is a name-value pair representing an element 808 struct Element 809 { 810 /// The name of this element 811 T[] name; 812 813 /// The value of this element 814 T[] value; 815 } 816 }