diff --git a/source/dom_persist.d b/source/dom_persist.d index ee08f42..f824495 100644 --- a/source/dom_persist.d +++ b/source/dom_persist.d @@ -10,15 +10,26 @@ import d2sqlite3; // https://d2sqlite3.dpldocs.info/v1.0.0/d2sqlite3.database.Database.this.html + // https://dlang-community.github.io/d2sqlite3/d2sqlite3.html version(unittest){ + //stuff only compiled for unittests string sqlite_filename = "dom_persist_test.db"; + + void assertNDRecord( Row row, long id, string e_data, long pid, TreeNodeType tnt ){ + + assert( id == row.peek!long(0) ); + assert( e_data == row.peek!string(1) ); + assert( pid == row.peek!long(2) ); + assert( tnt == getTreeNodeType( row.peek!int(3) ) ); + + } } unittest { - writeln( "Testing tree creation (old code)" ); + writeln( "Testing tree creation" ); db_drop( sqlite_filename ); assert( !db_exists( sqlite_filename ) ); @@ -31,44 +42,59 @@ Tree_Db.db_create_schema( db ); assert(db.tableColumnMetadata("doctree", "ID") == TableColumnMetadata("INTEGER", "BINARY", false, true, true)); - - Tree_Db_Base tdb = new Tree_Db_Base( db ); - - long tree_id = tdb.create_tree("mytree"); - - long nid = tdb.appendChild( tree_id, tree_id, TreeNodeType.docType, "html" ); - long html_nid = tdb.appendChild( tree_id, tree_id, TreeNodeType.element, "html" ); - - long head_id = tdb.appendChild( tree_id, html_nid, TreeNodeType.element, "head" ); - tdb.appendChild( tree_id, head_id, TreeNodeType.comment, "This is my comment" ); - - long body_id = tdb.appendChild( tree_id, html_nid, TreeNodeType.element, "body" ); - tdb.appendChild( tree_id, body_id, TreeNodeType.text, "This is some text" ); - tdb.appendChild( tree_id, body_id, TreeNodeType.text, " with more text" ); - tdb.appendChildElement( tree_id, body_id, "input" ); - - string html_out = tdb.getTreeAsText( tree_id ); - //writeln( html_out ); - assert( html_out == "This is some text with more text"); - db.close(); - + } unittest{ - + auto db = Database( sqlite_filename ); - Tree_Db tree = Tree_Db.createTree( db, "mytree-new" ); + Tree_Db tree = Tree_Db.createTree( db, "mytree" ); - TreeNode* tree_node = tree.getTreeNode(); + TreeNode tree_node = tree.getTreeNode(); NodeData nd = tree_node.node_data; assert( nd.pid == 0); - assert( nd.e_data == "mytree-new"); + assert( nd.e_data == "mytree"); + //bDebug_out = true; + + debug_out("tree_node-1: ", &tree_node); + // flush the empty tree tree.flush(); + ResultRange results = db.execute( "select ID, e_data, p_id, t_id from doctree where id=1" ); + Row row = results.front(); + + assertNDRecord( row, 1, "mytree", 0, TreeNodeType.tree ); + + //bDebug_out = true; + + debug_out("tree_node-2: ", &tree_node); + + tree_node.appendChild( TreeNodeType.docType, "html" ); + auto tn_html = tree_node.appendChild( TreeNodeType.element, "html" ); + + auto tn_head = tn_html.appendChild( TreeNodeType.element, "head" ); + tn_head.appendChild( TreeNodeType.comment, "This is my comment" ); + + auto tn_body = tn_html.appendChild( TreeNodeType.element, "body" ); + + tn_body.appendChild( TreeNodeType.text, "This is some text" ); + tn_body.appendChild( TreeNodeType.text, " with more text" ); + tn_body.appendChild( TreeNodeType.element, "input" ); + + + tree.flush(); + + //bDebug_out = false; + + string html_out = tree.getTreeAsText( ); + //writeln( html_out ); + assert( html_out == "This is some text with more text"); + + } @@ -80,31 +106,31 @@ TreeNameID[] tree_list = Tree_Db.getTreeList( db ); Tree_Db tree = Tree_Db.loadTree( db, tree_list[0].tree_id ); - TreeNode* tree_node = tree.getTreeNode(); + TreeNode tree_node = tree.getTreeNode(); DocOrderIterator it = new DocOrderIterator( tree_node ); int i=0; - TreeNode* nxt; + TreeNode nxt; while( (nxt=it.nextNode) !is null ){ switch(i){ case 0: - assert( (*nxt).node_data.type == TreeNodeType.tree ); + assert( nxt.node_data.type == TreeNodeType.tree ); break; case 1: - assert( (*nxt).node_data.type == TreeNodeType.docType ); + assert( nxt.node_data.type == TreeNodeType.docType ); break; case 2,3,5,8: - assert( (*nxt).node_data.type == TreeNodeType.element ); + assert( nxt.node_data.type == TreeNodeType.element ); break; case 4: - assert( (*nxt).node_data.type == TreeNodeType.comment ); + assert( nxt.node_data.type == TreeNodeType.comment ); break; case 6,7: - assert( (*nxt).node_data.type == TreeNodeType.text ); + assert( nxt.node_data.type == TreeNodeType.text ); break; default: @@ -145,159 +171,6 @@ return TreeNodeType.nulltype; } - - -/** - * This class provides direct database access to all trees in the database table. You can also obtain from it - * an instance of class Tree_Dd for a specific tree given its root node id. - */ -class Tree_Db_Base { - - protected: - Database* db; - static long node_count = 0; - - public: - - this( ref Database db ){ - this.db = &db; - } - - /** - * Create a new tree in the tree table and return the ID of the tree node. Note that the tree node is - * the parent of the root node, doctype and maybe other node data. - * - * The tree node is marked with '0' (zero) since it has no parent. - */ - long create_tree( string tree_name ){ - return appendChild( 0, 0, TreeNodeType.tree, tree_name); - } - - /** - * Read the DOM from this database for the given tree ID (tid) and return as - * an html (or xml) string. - * - * Note that the tree node (type==0) does not have a string representation. - */ - string getTreeAsText( long tid ){ - //NodeData cTree = getChild( tid ); //we don't want to print the 'tree' node - return getTreeAsText_r( tid ); - } - - string getTreeAsText_r( long tid ){ - - string strRtn = ""; - - NodeData[] children = getChildren( tid ); - foreach( child; children){ - strRtn ~= get_openTag_commence( child.type, child.e_data ); - // --> add attributes if required - strRtn ~= get_openTag_end( child.type, child.e_data ); - strRtn ~= getTreeAsText_r( child.ID ); - strRtn ~= get_closeTag( child.type, child.e_data ); - } - - return strRtn; - } - - /** - * Return the (ordered) child node IDs of the given parent_id. - */ - NodeData[] getChildren( long parent_id ){ - - NodeData[] child_nodes; - auto results = db.execute( format("select ID, e_data, p_id, t_id from doctree where p_id=%d", parent_id) ); - foreach (row; results){ - - //assert(row.length == 3); - - child_nodes ~= NodeData( - row.peek!long(0), - row.peek!string(1), - row.peek!long(2), - getTreeNodeType( row.peek!int(3) ) - ); - - } - return child_nodes; - } - - NodeData getChild( long cid ){ - - auto results = db.execute( format("select ID, e_data, p_id, t_id from doctree where id=%d", cid) ); - foreach (row; results){ - - //assert(row.length == 1); - - return NodeData( - row.peek!long(0), - row.peek!string(1), - row.peek!long(2), - getTreeNodeType( row.peek!int(3) ) - ); - - } - throw new Exception( format( "Child with ID(%d) not found", cid) ); - - } - - /** - * Append a new element to the given parent id (pid) - */ - long appendChildElement( long tree_id, long pid, string elem_name ){ - enforce(elem_name!=null && elem_name.length>0 ); - return appendChild( tree_id, pid, TreeNodeType.element, elem_name ); - } - - /** - * Append new text to the given parent id (pid). - * Returns the ID of the text node if appended or -1 otherwise. - */ - long appendChildText( long tree_id, long pid, string text ){ - if(text==null || text.length==0 ) return -1; - return appendChild( tree_id, pid, TreeNodeType.text, text ); - } - - /** - * Append new text to the given parent id (pid). - * Returns the ID of the text node if appended or -1 otherwise. - */ - long appendChildComment( long tree_id, long pid, string text ){ - if(text==null || text.length==0 ) return -1; - return appendChild( tree_id, pid, TreeNodeType.comment, text ); - } - - /** - * Append a new node to the given parent pid. - * node_data is used only for doctype, element and text - * - * The ID of the new node is returned. - */ - long appendChild( long tree_id, long pid, TreeNodeType nt, string node_data = "" ){ - - if( nt == TreeNodeType.docType ){ - //we might store the extra data as an attribute but this will suffice for the moment - node_data = "DOCTYPE "~node_data; - } - db.run( format("insert into doctree(e_data, p_id, t_id, tree_id, c_order ) values( '%s', %d, %d, %d, %d )", node_data, pid, nt, tree_id, node_count ) ); - node_count+=1; - return db.lastInsertRowid; - } - - /** - * Close this object AND the underlying DB connection. - */ - void close(){ - db.close(); - db=null; - } - - long getRootId(){ - return -1; - } - -} - string get_openTag_commence( TreeNodeType nt, string e_data ){ switch( nt ){ @@ -308,6 +181,9 @@ case TreeNodeType.comment: return " add attributes if required strRtn ~= get_openTag_end( nd.type, nd.e_data ); - strRtn ~= getTreeAsText_r( *child ); + strRtn ~= getTreeAsText_r( child ); strRtn ~= get_closeTag( nd.type, nd.e_data ); } @@ -700,29 +629,32 @@ auto db = Database( sqlite_filename ); + auto tree2 = Tree_Db.createTree( db, "AnotherTree" ); + tree2.flush(); + TreeNameID[] tree_list = Tree_Db.getTreeList( db ); assert( tree_list.length==2 ); Tree_Db tree = Tree_Db.loadTree( db, tree_list[0].tree_id ); - TreeNode* tree_node = tree.getTreeNode(); + TreeNode tree_node = tree.getTreeNode(); NodeData nd_t = tree_node.node_data; assert( nd_t.ID == tree_list[0].tree_id ); assert( nd_t.e_data == tree_list[0].name ); assert( nd_t.pid == 0 ); assert( nd_t.type == TreeNodeType.tree ); - TreeNode* html_node; + TreeNode html_node; int i=0; foreach( node_ptr; tree_node.child_nodes ){ - NodeData c_node = (*node_ptr).node_data; + NodeData c_node = node_ptr.node_data; switch(i){ case 0: assert( c_node.ID == 2 ); - assert( c_node.e_data == "DOCTYPE html" ); + assert( c_node.e_data == "html" ); assert( c_node.pid == tree_list[0].tree_id ); assert( c_node.type == TreeNodeType.docType ); break; @@ -747,7 +679,7 @@ writeln( "Testing tree element insertion" ); //get head element - TreeNode* tn_head = html_node.child_nodes[0]; + TreeNode tn_head = html_node.child_nodes[0]; //add an element to the head at position zero tn_head.insertChild( TreeNodeType.element, "script", 0 ); @@ -758,7 +690,7 @@ writeln( "Testing tree editing" ); //edit the comment node (id=5) - TreeNode* tn = tree.getTreeNodeById( 5 ); + TreeNode tn = tree.getTreeNodeById( 5 ); tn.setData( "An edit took place"); html_out = tree.getTreeAsText( ); @@ -774,8 +706,8 @@ tree.flush(); //check db contents using a new tree - Tree_Db tree2 = Tree_Db.loadTree( db, tree_list[0].tree_id ); - html_out = tree2.getTreeAsText( ); + Tree_Db tree3 = Tree_Db.loadTree( db, tree_list[0].tree_id ); + html_out = tree3.getTreeAsText( ); assert( html_out == "This is some text with more text"); } @@ -786,10 +718,10 @@ */ class DocOrderIterator { - TreeNode* start_node; - TreeNode* next_node; + TreeNode start_node; + TreeNode next_node; - this( TreeNode* n ){ + this( TreeNode n ){ start_node = n; next_node = n; } @@ -801,11 +733,11 @@ /** * The initial TreeNode is the first node to be returned. */ - TreeNode* nextNode(){ + TreeNode nextNode(){ //the node we will return this time - TreeNode* rtnNode = next_node; - if(rtnNode==null) return null; + TreeNode rtnNode = next_node; + if(rtnNode is null) return null; //now work out the node for the next call @@ -814,7 +746,7 @@ return rtnNode; } - TreeNode* anc_node = rtnNode; + TreeNode anc_node = rtnNode; while( anc_node !is null && anc_node.nextSibling() is null){ anc_node = anc_node.parentNode(); if( anc_node == start_node ){ @@ -833,3 +765,14 @@ } } + + +bool bDebug_out = false; +void debug_out(){ writeln(); } + +void debug_out(T, A...)(T t, A a){ + if(!bDebug_out) return; + import std.stdio; + write(t); + debug_out(a); +}