1 module arrogant; 2 3 import arrogant.c.modest; 4 5 // Public enums & stuffs 6 public import arrogant.c.common; 7 8 import std.traits; 9 import std.conv : to; 10 import std.typecons : Flag, Yes, No; 11 import std..string : toStringz; 12 13 /** Use this enum with `node.byAttribute()` search */ 14 enum AttributeSearchType 15 { 16 exact, /// 17 startsWith, /// 18 endsWith, /// 19 contains, /// 20 spaceSeparated, /// 21 hypenSeparated /// 22 } 23 24 class ArrogantException : Exception 25 { 26 this(uint err) 27 { 28 import std.conv : to; 29 super("Arrogant exception: " ~ to!string(err)); 30 } 31 } 32 33 34 /** An html attribute of a tag */ 35 struct Attribute 36 { 37 /** The attribute key */ 38 @property auto key() { return _key; } 39 40 // The attribute value */ 41 @property auto value() { return _value; } 42 43 private this(myhtml_tree_attr_t* attr) 44 { 45 { 46 size_t length; 47 _key = myhtml_attribute_key(attr, &length)[0..length].to!string; 48 } 49 50 { 51 size_t length; 52 _value = myhtml_attribute_value(attr, &length)[0..length].to!string; 53 } 54 } 55 56 @disable this(); 57 58 private string _key; 59 private string _value; 60 } 61 62 63 64 /** A HTML Node */ 65 struct Node 66 { 67 @disable this(); 68 69 /// Check if node is null / empty 70 bool isNull() { return myhtml_tree_node == null; } 71 72 73 /** 74 * Get the tag id for this node (ex: a, div, body, ...) 75 * Examples: 76 * -------------------- 77 * tree.body.tag.writeln(); // prints "body" 78 * -------------------- 79 */ 80 MyHtmlTagId tagId() { return cast(MyHtmlTagId )myhtml_tree_node.tag_id; } 81 string tag() { return tagId.to!string; } /// Ditto 82 83 /** 84 * "in" operator to check for an attribute inside a node 85 * Examples: 86 * -------------------- 87 * if ("href" in node) writeln("Link: ", node["href"]); 88 * -------------------- 89 */ 90 bool opBinaryRight(string op)(string key) if (op == "in") 91 { 92 auto attr = myhtml_attribute_by_key (myhtml_tree_node, key.toStringz, key.length); 93 return attr !is null; 94 } 95 96 /** Read an attribute from node */ 97 auto opIndex(string attribute) 98 { 99 import std.typecons : Nullable; 100 101 Nullable!string value; 102 auto attr = myhtml_attribute_by_key (myhtml_tree_node, attribute.toStringz, attribute.length); 103 104 if (attr !is null) 105 { 106 size_t length; 107 auto v = myhtml_attribute_value(attr, &length); 108 return Nullable!string(v[0..length].to!string); 109 } 110 111 return value; 112 } 113 114 /** Write an attribute */ 115 auto opIndexAssign(string value, string key) 116 { 117 removeAttribute(key); 118 myhtml_attribute_add (myhtml_tree_node, key.toStringz, key.length, value.toStringz, value.length, MyEncodingList.default_); 119 return value; 120 } 121 122 auto opIndexAssign(typeof(null) value, string key) 123 { 124 removeAttribute(key); 125 myhtml_attribute_add (myhtml_tree_node, key.toStringz, key.length, null, 0, MyEncodingList.default_); 126 return value; 127 } 128 129 /** 130 Remove an attribute 131 Returns: `true` if attribute exists `false` otherwise. 132 */ 133 bool removeAttribute(string key) 134 { 135 auto attr = myhtml_attribute_by_key (myhtml_tree_node, key.toStringz, key.length); 136 if (attr !is null) 137 { 138 myhtml_attribute_delete(myhtml_node_tree(myhtml_tree_node), myhtml_tree_node, attr); 139 return true; 140 } 141 142 return false; 143 } 144 145 /** Remove node from tree and delete it */ 146 void deleteNode() { myhtml_node_delete_recursive(myhtml_tree_node); } 147 148 /// 149 Node firstChild() 150 { 151 return Node(myhtml_node_child(myhtml_tree_node)); 152 } 153 154 /// 155 Node lastChild() 156 { 157 return Node(myhtml_node_last_child(myhtml_tree_node)); 158 } 159 160 /// 161 auto parent() 162 { 163 return Node(myhtml_node_parent(myhtml_tree_node)); 164 } 165 166 /// 167 auto next() 168 { 169 return Node(myhtml_node_next(myhtml_tree_node)); 170 } 171 172 /// 173 auto previous() 174 { 175 return Node(myhtml_node_prev(myhtml_tree_node)); 176 } 177 178 /* 179 Get children of this node. 180 Returns: a lazy `ChildrenRange`. If you want to edit children, convert to array before. 181 */ 182 auto children() 183 { 184 struct ChildrenRange 185 { 186 @disable this(); 187 188 private this(myhtml_tree_node_t *n) { parent = n; current = myhtml_node_child(parent); } 189 190 @property empty() { return current == null; } 191 @property Node front() { return Node(current); } 192 void popFront() { current = myhtml_node_next(current); } 193 194 void opAssign(ChildrenRange rhs) 195 { 196 current = rhs.current; 197 parent = rhs.parent; 198 } 199 200 private: 201 myhtml_tree_node_t *current; 202 myhtml_tree_node_t *parent; 203 } 204 205 return ChildrenRange(myhtml_tree_node); 206 } 207 208 /** All node's attributes 209 Returns: a lazy range of Attributes 210 */ 211 auto attributes() 212 { 213 struct AttributesRange 214 { 215 this(myhtml_tree_node_t *n) { parent = n; current = myhtml_node_attribute_first(parent); } 216 217 @property empty() { return current == null; } 218 auto front() { return Attribute(current); } 219 void popFront() { current = myhtml_attribute_next(current); } 220 221 private: 222 myhtml_tree_attr_t *current; 223 myhtml_tree_node_t *parent; 224 } 225 226 return AttributesRange(myhtml_tree_node); 227 } 228 229 /** Get the text of this node. Only for text nodes! */ 230 @property string text() 231 { 232 return myhtml_node_text(myhtml_tree_node, null).to!string; 233 } 234 235 /** Set the text of this node. Only for text nodes! */ 236 @property void text(string s) 237 { 238 myhtml_node_text_set(myhtml_tree_node, s.toStringz, s.length, MyEncodingList.default_); 239 } 240 241 /** Return node html representation */ 242 string toString() 243 { 244 return innerHTML(); 245 } 246 247 /// Ditto 248 @property string innerHTML() 249 { 250 mycore_string_raw_t str_raw; 251 mycore_string_raw_clean_all(&str_raw); 252 scope(exit) mycore_string_raw_destroy(&str_raw, false); 253 254 if(myhtml_serialization_tree_buffer(myhtml_tree_node, &str_raw)) return ""; 255 return str_raw.data[0..str_raw.length].to!string; 256 } 257 258 /** Set node html. All children will be deleted. */ 259 @property void innerHTML(string s) 260 { 261 // Create a new tree to parse fragment 262 auto tree = Tree(myhtml_tree_get_myhtml(myhtml_node_tree(myhtml_tree_node))); 263 tree.parseFragment(s); 264 265 // Clone fragment and move to current tree 266 auto cloned = tree.first.clone(myhtml_node_tree(myhtml_tree_node)); 267 268 // Delete all children! 269 270 271 myhtml_tree_node_t*[] toDelete; 272 273 for(auto current = myhtml_node_child(myhtml_tree_node); current != null; current = myhtml_node_next(current)) 274 toDelete ~= current; 275 276 foreach(n; toDelete) 277 myhtml_node_delete_recursive(n); 278 279 // Append new child 280 appendChild(cloned); 281 } 282 283 /** Set node innerText. All children will be deleted. */ 284 @property void innerText(string s) 285 { 286 // Create a text node 287 auto text_node = myhtml_node_create ( 288 myhtml_node_tree(myhtml_tree_node), 289 MyHtmlTagId ._text, 290 MyHtmlNamespace.html 291 ); 292 293 Node nodeToAppend = Node(text_node); 294 nodeToAppend.text = s; 295 296 // Delete all children! 297 298 myhtml_tree_node_t*[] toDelete; 299 300 for(auto current = myhtml_node_child(myhtml_tree_node); current != null; current = myhtml_node_next(current)) 301 toDelete ~= current; 302 303 foreach(n; toDelete) 304 myhtml_node_delete_recursive(n); 305 306 // Append new child 307 appendChild(nodeToAppend); 308 } 309 310 @property string innerText() 311 { 312 import std.container.dlist; 313 import std.array : Appender, array; 314 import std.algorithm : map; 315 316 auto appender = Appender!string(); 317 318 auto toExplore = DList!(myhtml_tree_node_t*)(); 319 toExplore.insertBack(myhtml_tree_node); 320 321 while(!toExplore.empty) 322 { 323 auto current = Node(toExplore.front); 324 toExplore.removeFront; 325 if (current.tagId == MyHtmlTagId._text) appender ~= current.text(); 326 else toExplore.insertFront(current.children.map!(x => x.myhtml_tree_node)); 327 } 328 329 return appender.data; 330 } 331 332 /** 333 Create a copy of this node owned by another tree 334 Returns: a `Node` owned by `destination` 335 */ 336 Node clone(Tree destination) { return clone(destination.myhtml_tree); } 337 338 /// Create a copy of this node 339 Node clone() { return clone(myhtml_node_tree(myhtml_tree_node)); } 340 341 /// 342 bool isSelfClosing() { return myhtml_node_is_close_self(myhtml_tree_node); } 343 344 /// 345 bool isVoidElement() { return myhtml_node_is_void_element(myhtml_tree_node); } 346 347 /** Detach node from tree without destroying */ 348 void detach() { myhtml_node_remove(myhtml_tree_node); } 349 350 /// Fast way to append a text node 351 void appendText(string s) 352 { 353 // Create a text node 354 auto text_node = myhtml_node_create ( 355 myhtml_node_tree(myhtml_tree_node), 356 MyHtmlTagId ._text, 357 MyHtmlNamespace.html 358 ); 359 360 Node nodeToAppend = Node(text_node); 361 nodeToAppend.text = s; 362 appendChild(nodeToAppend); 363 } 364 365 /// Fast way to append a comment node 366 void appendComment(string s) 367 { 368 // Create a text node 369 auto text_node = myhtml_node_create ( 370 myhtml_node_tree(myhtml_tree_node), 371 MyHtmlTagId ._comment, 372 MyHtmlNamespace.html 373 ); 374 375 Node nodeToAppend = Node(text_node); 376 nodeToAppend.text = s; 377 appendChild(nodeToAppend); 378 } 379 380 /// 381 void appendChild(Node n) { n.detach(); myhtml_node_append_child(myhtml_tree_node, n.myhtml_tree_node); } 382 383 /// 384 void insertBefore(Node n) { myhtml_node_insert_before(myhtml_tree_node, n.myhtml_tree_node); } 385 386 /// 387 void insertAfter(Node n) { myhtml_node_insert_after(myhtml_tree_node, n.myhtml_tree_node); } 388 389 /// 390 void insertToAppropriatePlace(Node n) { myhtml_node_insert_to_appropriate_place(myhtml_tree_node, n.myhtml_tree_node); } 391 392 /** 393 Search children using a css 3.1 selector 394 Returns: a lazy range of nodes 395 See_Also: byAttribute, byAttributeKey, byTagName, byClass, byId 396 */ 397 auto byCssSelector(string selector) 398 { 399 import std.exception : enforce; 400 401 auto mycss = mycss_create(); 402 mycss_init(mycss); 403 404 auto entry = mycss_entry_create(); 405 mycss_entry_init(mycss, entry); 406 407 auto finder = modest_finder_create_simple(); 408 409 mystatus_t out_status; 410 mycss_selectors_list_t *list = mycss_selectors_parse 411 ( 412 mycss_entry_selectors(entry), 413 MyEncodingList.default_, 414 selector.toStringz, selector.length, 415 &out_status 416 ); 417 418 enforce(list != null && ((list.flags & mycss_selectors_flags.MyCSS_SELECTORS_FLAGS_SELECTOR_BAD) == 0), "Can't compile css selector: " ~ selector); 419 420 myhtml_collection_t* collection = null; 421 422 modest_finder_by_selectors_list(finder, myhtml_tree_node, list, &collection); 423 424 // Free resources! 425 mycss_selectors_list_destroy(mycss_entry_selectors(entry), list, true); 426 modest_finder_destroy(finder, true); 427 mycss_entry_destroy(entry, true); 428 mycss_destroy(mycss, true); 429 430 return NodeRange(collection, myhtml_node_tree(myhtml_tree_node)); 431 } 432 433 /** 434 Search children by tag name 435 Returns: a lazy range of nodes 436 See_Also: byAttribute, byAttributeKey, byClass, byId, byCssSelector 437 */ 438 auto byTagName(MyHtmlTagId name) 439 { 440 mystatus_t status; 441 myhtml_collection_t* myhtml_collection = myhtml_collection_create(0, null); 442 auto collection = NodeRange(myhtml_get_nodes_by_tag_id_in_scope(myhtml_node_tree(myhtml_tree_node), myhtml_collection, myhtml_tree_node, name, &status), myhtml_node_tree(myhtml_tree_node)); 443 444 if (MYHTML_FAILED(status)) throw new ArrogantException(status); 445 446 return collection; 447 } 448 449 /// Ditto 450 auto byTagName(string name) 451 { 452 mystatus_t status; 453 myhtml_collection_t* myhtml_collection = myhtml_collection_create(0, null); 454 auto collection = NodeRange(myhtml_get_nodes_by_name_in_scope(myhtml_node_tree(myhtml_tree_node), myhtml_collection, myhtml_tree_node, name.toStringz, name.length, &status), myhtml_node_tree(myhtml_tree_node)); 455 if (MYHTML_FAILED(status)) throw new ArrogantException(status); 456 457 return collection; 458 } 459 460 /** 461 Search children by class (space separated) 462 Returns: a lazy range of nodes 463 See_Also: byAttribute, byAttributeKey, byTagName, byId, byCssSelector 464 */ 465 auto byClass(string className) { return byAttribute!(AttributeSearchType.spaceSeparated)("class", className);} 466 467 /** 468 Search children by id 469 Returns: a lazy range of nodes 470 See_Also: byAttribute, byAttributeKey, byTagName, byClass, byCssSelector 471 */ 472 auto byId(string id) { return byAttribute("id", id);} 473 474 /** 475 Search children with a specified attribute 476 Returns: a lazy range of nodes 477 See_Also: byAttribute, byTagName, byClass, byId, byCssSelector 478 */ 479 auto byAttributeKey(string name) 480 { 481 mystatus_t status; 482 myhtml_collection_t* myhtml_collection = myhtml_collection_create(0, null); 483 auto collection = NodeRange(myhtml_get_nodes_by_attribute_key(myhtml_node_tree(myhtml_tree_node), myhtml_collection, myhtml_tree_node, name.toStringz, name.length, &status), myhtml_node_tree(myhtml_tree_node)); 484 if (MYHTML_FAILED(status)) throw new ArrogantException(status); 485 486 return collection; 487 } 488 489 /** 490 Search children by tag attribute key/val. 491 Returns: a lazy range of nodes 492 See_Also: byAttributeKey, byTagName, byClass, byId, byCssSelector 493 */ 494 auto byAttribute(AttributeSearchType st = AttributeSearchType.exact, Flag!"caseInsensitive" caseInsensitive = No.caseInsensitive)(string key, string value) 495 { 496 mystatus_t status; 497 typeof(&myhtml_get_nodes_by_attribute_value) callback; 498 499 final switch(st) 500 { 501 case AttributeSearchType.exact: callback = &myhtml_get_nodes_by_attribute_value; break; 502 case AttributeSearchType.startsWith: callback = &myhtml_get_nodes_by_attribute_value_begin; break; 503 case AttributeSearchType.endsWith: callback = &myhtml_get_nodes_by_attribute_value_end; break; 504 case AttributeSearchType.contains: callback = &myhtml_get_nodes_by_attribute_value_contain; break; 505 case AttributeSearchType.spaceSeparated : callback = &myhtml_get_nodes_by_attribute_value_whitespace_separated; break; 506 case AttributeSearchType.hypenSeparated: callback = &myhtml_get_nodes_by_attribute_value_hyphen_separated; break; 507 } 508 509 myhtml_collection_t* myhtml_collection = myhtml_collection_create(0, null); 510 511 auto collection = NodeRange 512 ( 513 callback 514 ( 515 myhtml_node_tree(myhtml_tree_node), 516 myhtml_collection, 517 myhtml_tree_node, 518 caseInsensitive == Yes.caseInsensitive, 519 key.toStringz, key.length, value.toStringz, value.length, 520 &status 521 ), 522 myhtml_node_tree(myhtml_tree_node) 523 ); 524 525 if (MYHTML_FAILED(status)) throw new ArrogantException(status); 526 return collection; 527 } 528 529 /** Create a new html node */ 530 this(ref Tree tree, MyHtmlTagId tag, MyHtmlNamespace ns = MyHtmlNamespace.html) 531 { 532 myhtml_tree_node = myhtml_node_create ( 533 tree.myhtml_tree, 534 tag, 535 ns 536 ); 537 538 Tree.acquire(tree.myhtml_tree); 539 } 540 541 this(this) { Tree.acquire(myhtml_node_tree(myhtml_tree_node)); } 542 543 void opAssign(Node rhs) 544 { 545 Tree.acquire(myhtml_node_tree(rhs.myhtml_tree_node)); 546 Tree.release(myhtml_node_tree(myhtml_tree_node)); 547 myhtml_tree_node = rhs.myhtml_tree_node; 548 } 549 550 ~this() { Tree.release(myhtml_node_tree(myhtml_tree_node)); } 551 552 private: 553 554 this(myhtml_tree_node_t *node) 555 { 556 myhtml_tree_node = node; 557 Tree.acquire(myhtml_node_tree(node)); 558 } 559 560 Node clone(myhtml_tree_t* destination) 561 { 562 563 struct CopyQueueItem 564 { 565 myhtml_tree_node_t* destParent; // Where node will be appended 566 myhtml_tree_node_t* toCopy; // The node to copy 567 } 568 569 import std.container.dlist; 570 auto copyQueue = DList!CopyQueueItem(); 571 572 // Clone a single node without children 573 myhtml_tree_node_t* cloneNode(myhtml_tree_t* _destination, myhtml_tree_node_t* _node) 574 { 575 // Create a new node rooted on destination tree 576 auto ret = myhtml_node_create ( 577 _destination, 578 _node.tag_id, 579 _node.ns 580 ); 581 582 // Copy text if present 583 { 584 size_t textLength; 585 auto text = myhtml_node_text(_node, &textLength); 586 myhtml_node_text_set(ret, text, textLength, MyEncodingList.default_); 587 } 588 589 // Clone attributes 590 for (auto attribute = myhtml_node_attribute_first(_node); attribute != null; attribute = myhtml_attribute_next(attribute)) 591 { 592 size_t keyLength, valueLength; 593 auto k = myhtml_attribute_key(attribute, &keyLength); 594 auto v = myhtml_attribute_value(attribute, &valueLength); 595 myhtml_attribute_add (ret, k, keyLength, v, valueLength, MyEncodingList.default_); 596 } 597 598 // Return the filled node. 599 return ret; 600 } 601 602 // Clone the root 603 auto destinationRoot = cloneNode(destination, myhtml_tree_node); 604 auto currentNode = myhtml_tree_node; 605 auto currentDestNode = destinationRoot; 606 607 while(true) 608 { 609 // Add children of current node to queue 610 for (auto child = myhtml_node_child(currentNode); child != null; child = myhtml_node_next(child)) 611 copyQueue.insertBack(CopyQueueItem(currentDestNode, child)); 612 613 if (copyQueue.empty) break; 614 615 // Get the first item in queue 616 auto destParent = copyQueue.front.destParent; 617 currentNode = copyQueue.front.toCopy; 618 619 // Remove first element of list 620 copyQueue.removeFront(); 621 622 // Clone the children and add to new parent 623 currentDestNode = cloneNode(destination, currentNode); 624 myhtml_node_append_child(destParent, currentDestNode); 625 } 626 627 return Node(destinationRoot); 628 } 629 630 myhtml_tree_node_t* myhtml_tree_node = null; 631 } 632 633 import std.stdio; 634 635 636 637 /** A lazy range of nodes, usually returned by a search */ 638 struct NodeRange 639 { 640 @disable this(); 641 642 Node opIndex(size_t i) 643 { 644 return Node(myhtml_collection.list[i+idx]); 645 } 646 647 size_t length() { if (myhtml_collection) return myhtml_collection.length; return 0; } 648 649 @property Node front() { if (empty) assert(0, "Can't read nodes from an empty collection"); return this[0]; } 650 @property bool empty() { return idx >= length(); } 651 652 void popFront() { idx++; } 653 654 655 ~this() { Tree.release(myhtml_tree); } 656 this(this) { Tree.acquire(myhtml_tree); } 657 658 void opAssign(NodeRange rhs) 659 { 660 Tree.acquire(rhs.myhtml_tree); 661 Tree.release(myhtml_tree); 662 myhtml_tree = rhs.myhtml_tree; 663 myhtml_collection = rhs.myhtml_collection; 664 idx = rhs.idx; 665 } 666 667 private: 668 669 this(myhtml_collection_t* collection, myhtml_tree_t* tree) 670 { 671 myhtml_collection = collection; 672 myhtml_tree = tree; 673 Tree.acquire(myhtml_tree); 674 } 675 676 myhtml_collection_t* myhtml_collection; 677 myhtml_tree_t* myhtml_tree; 678 679 size_t idx = 0; 680 } 681 682 /** A html tree */ 683 struct Tree 684 { 685 /// Create a new node owned by this tree 686 Node createNode(MyHtmlTagId tag, MyHtmlNamespace ns = MyHtmlNamespace.html) { return Node(this, tag, ns); } 687 688 /// Fast way to create a text node 689 Node createTextNode(string text) { auto n = createNode(MyHtmlTagId._text); n.text = text; return n; } 690 691 /// Fast way to create a comment node 692 Node createCommentNode(string text) { auto n = createNode(MyHtmlTagId._comment); n.text = text; return n; } 693 694 /// See: `Node.byXXXX` 695 auto byClass(string className) { return document.byClass(className); } 696 697 698 /// Ditto 699 auto byId(string id) { return document.byId(id);} 700 701 702 /// Ditto 703 auto byCssSelector(string selector) { return document.byCssSelector(selector); } 704 705 /// Ditto 706 auto byTagName(MyHtmlTagId name) { return document.byTagName(name); } 707 708 /// Ditto 709 auto byTagName(string name) { return document.byTagName(name); } 710 711 /// Ditto 712 auto byAttributeKey(string name) { return document.byAttributeKey(name); } 713 714 /// Ditto 715 auto byAttribute(AttributeSearchType st = AttributeSearchType.exact, Flag!"caseInsensitive" caseInsensitive = No.caseInsensitive)(string key, string value) 716 { 717 return document.byAttribute!(st, caseInsensitive)(key, value); 718 } 719 720 /// The document root 721 auto document() 722 { 723 return Node(myhtml_tree_get_document(myhtml_tree)); 724 } 725 726 /// The html node 727 auto html() 728 { 729 return Node(myhtml_tree_get_node_html(myhtml_tree)); 730 } 731 732 /// The head node 733 auto head() 734 { 735 return Node(myhtml_tree_get_node_head(myhtml_tree)); 736 } 737 738 /// The body node 739 auto body() 740 { 741 return Node(myhtml_tree_get_node_body(myhtml_tree)); 742 } 743 744 /// Return the first node 745 auto first() 746 { 747 return Node(myhtml_node_first(myhtml_tree)); 748 } 749 750 string toString() { Node tmp = first(); return tmp.toString(); } 751 752 this(this) 753 { 754 acquire(myhtml_tree); 755 valid = true; 756 } 757 758 void opAssign(Tree rhs) 759 { 760 acquire(rhs.myhtml_tree); 761 762 if (valid) 763 release(myhtml_tree); 764 765 myhtml_tree = rhs.myhtml_tree; 766 valid = true; 767 } 768 769 ~this() { 770 if (valid) 771 release(myhtml_tree); 772 } 773 774 @property isValid() { return valid; } 775 776 private: 777 778 779 void parse(T)(T html, MyEncodingList encoding = MyEncodingList.default_) if (isSomeString!T) 780 { 781 auto status = myhtml_parse(myhtml_tree, encoding, html.toStringz, html.length); 782 if (MYHTML_FAILED(status)) throw new ArrogantException(status); 783 } 784 785 void parseFragment(T)(T html, MyHtmlTagId wrap = MyHtmlTagId .div, MyEncodingList encoding = MyEncodingList.default_, MyHtmlNamespace ns = MyHtmlNamespace.html) if (isSomeString!T) 786 { 787 auto status = myhtml_parse_fragment ( 788 myhtml_tree, 789 encoding, 790 html.toStringz, html.length, 791 wrap, 792 ns 793 ); 794 if (MYHTML_FAILED(status)) throw new ArrogantException(status); 795 } 796 797 this(ref Arrogant parent) { this(parent.myhtml); } 798 799 this(myhtml_t* parent) 800 { 801 myhtml_tree = myhtml_tree_create(); 802 auto status = myhtml_tree_init(myhtml_tree, parent); 803 if (MYHTML_FAILED(status)) throw new ArrogantException(status); 804 acquire(myhtml_tree); 805 valid = true; 806 } 807 808 bool valid = false; 809 myhtml_tree_t* myhtml_tree = null; 810 811 static size_t[myhtml_tree_t*] refCount; 812 813 static void acquire(myhtml_tree_t* ptr) { refCount[ptr]++; Arrogant.acquire(myhtml_tree_get_myhtml(ptr)); } 814 static void release(myhtml_tree_t* ptr) 815 { 816 size_t cnt = refCount[ptr]; 817 assert(cnt > 0); 818 refCount[ptr] = cnt - 1; 819 myhtml_t* myhtml = myhtml_tree_get_myhtml(ptr); 820 821 if (cnt == 1) 822 { 823 myhtml_tree_destroy(ptr); 824 refCount.remove(ptr); 825 } 826 827 Arrogant.release(myhtml); 828 } 829 830 } 831 832 struct Arrogant 833 { 834 /// Parse a html document 835 Tree parse(T)(T html, MyEncodingList encoding = MyEncodingList.default_) if (isSomeString!T) 836 { 837 if (!myhtml) initArrogant(); 838 Tree tree = Tree(myhtml); 839 tree.parse(html, encoding); 840 return tree; 841 } 842 843 /// Parse a html fragment 844 Tree parseFragment(T)(T html, MyHtmlTagId wrap = MyHtmlTagId .div, MyEncodingList encoding = MyEncodingList.default_, MyHtmlNamespace ns = MyHtmlNamespace.html,) if (isSomeString!T) 845 { 846 if (!myhtml) initArrogant(); 847 Tree tree = Tree(myhtml); 848 tree.parseFragment(html, wrap, encoding, ns); 849 return tree; 850 } 851 852 853 void opAssign(Arrogant rhs) 854 { 855 acquire(rhs.myhtml); 856 857 if (valid) 858 release(myhtml); 859 860 myhtml = rhs.myhtml; 861 valid = true; 862 } 863 864 /// 865 this(MyHtmlOptions options, size_t threadCount = 1, size_t queueSize = 0) 866 { 867 initArrogant(options, threadCount, queueSize); 868 } 869 870 this(this) { acquire(myhtml); valid = true; } 871 872 ~this() { if (valid) release(myhtml); } 873 874 @property isValid() { return valid; } 875 876 private: 877 878 void initArrogant(MyHtmlOptions options = MyHtmlOptions.default_, size_t threadCount = 1, size_t queueSize = 0) 879 { 880 if (myhtml) return; 881 882 myhtml = myhtml_create(); 883 auto status = myhtml_init(myhtml, options, threadCount, queueSize); 884 885 acquire(myhtml); 886 887 if (MYHTML_FAILED(status)) 888 throw new ArrogantException(status); 889 890 valid = true; 891 } 892 893 myhtml_t* myhtml = null; 894 bool valid = false; 895 896 static size_t[myhtml_t*] refCount; 897 static void acquire(myhtml_t* ptr) { refCount[ptr]++; } 898 static void release(myhtml_t* ptr) 899 { 900 size_t cnt = refCount[ptr]; 901 902 assert(cnt > 0); 903 refCount[ptr] = cnt - 1; 904 905 if (cnt == 1) 906 { 907 myhtml_destroy(ptr); 908 refCount.remove(ptr); 909 } 910 } 911 912 }