diff --git a/source/attributeHandler.d b/source/attributeHandler.d index 9bb88ff..fb37e6b 100644 --- a/source/attributeHandler.d +++ b/source/attributeHandler.d @@ -13,18 +13,49 @@ * * Remember to escape the " and/or ' with " or ' if these values are required. */ -class AttribParser { +class AttributeMap { - string strAtts; - string[string] mapAtts; + string[string] attrib_map; - this( string strAtts ){ - this.strAtts = strAtts; + this( string strAtts="" ){ + if(strAtts!=""){ + attrib_map = parse( strAtts ); + } } - ref string[string] getAsMap(){ + string[string] getAttribMap( ){ + return attrib_map; + } + + int getAttribCount( ){ + return cast(int)(attrib_map.length); + } + + string getAttribute( string name ){ + if( name in attrib_map ) return attrib_map[name]; + return null; + } + + string getAttsAsString(){ + if(attrib_map.length==0) return ""; + string rtn = ""; + foreach( key; attrib_map.keys() ){ + rtn ~= key ~ "=\"" ~ attrib_map[key] ~ "\" "; + } + return rtn; + } + + void setAttribute( string name, string value){ + attrib_map[name] = value; + } + + void removeAttribute( string name ){ + if( name in attrib_map ) attrib_map.remove(name); + } - if(mapAtts !is null ) return mapAtts; + static string[string] parse( string strAtts ){ + + string[string] mapAtts; int state = 0; string nxtKey = ""; @@ -122,9 +153,9 @@ writeln( "Testing attribute parsing" ); string strAtts = " color=\"red\" font='big font' nowrap v-align='top' border=\"\" "; - AttribParser atts = new AttribParser( strAtts ); + AttributeMap atts = new AttributeMap( strAtts ); - auto attMap = atts.getAsMap(); + auto attMap = atts.getAttribMap(); foreach( key; attMap.keys() ){ string value = attMap[key]; @@ -155,12 +186,12 @@ } - atts = new AttribParser( "" ); - attMap = atts.getAsMap(); + atts = new AttributeMap( "" ); + attMap = atts.getAttribMap(); assert( attMap.length==0 ); - atts = new AttribParser( "color='pink' > other garbage" ); - attMap = atts.getAsMap(); + atts = new AttributeMap( "color='pink' > other garbage" ); + attMap = atts.getAttribMap(); assert( attMap.length==1 ); assert( attMap["color"]=="pink" ); diff --git a/source/dom_persist.d b/source/dom_persist.d index 7cdde89..d6be98a 100644 --- a/source/dom_persist.d +++ b/source/dom_persist.d @@ -109,7 +109,7 @@ //also order by the parent_id so that we know all siblings are grouped together //and then by child order - auto results = db.execute( format("select ID, e_data, p_id, t_id from doctree where tree_id=%d or id=%d order by p_id,c_order", tid, tid) ); + auto results = db.execute( format("select ID, e_data, p_id, t_id, att_data from doctree where tree_id=%d or id=%d order by p_id,c_order", tid, tid) ); foreach (row; results){ long id = row.peek!long(0); @@ -119,7 +119,7 @@ row.peek!string(1), p_id, getTreeNodeType( row.peek!int(3) ) - )); + ), row.peek!string(4) ); all_nodes[id] = tn; if(p_id==0) continue; all_nodes[p_id].child_nodes ~= all_nodes[id]; @@ -135,7 +135,7 @@ * Create the Database schema for the trees. */ static void db_create_schema( ref Database db ){ - db.run("CREATE TABLE IF NOT EXISTS doctree (ID INTEGER, e_data TEXT,p_id INTEGER,t_id INTEGER NOT NULL,tree_id INTEGER NOT NULL, c_order INTEGER, PRIMARY KEY( ID AUTOINCREMENT))"); + db.run("CREATE TABLE IF NOT EXISTS doctree (ID INTEGER, e_data TEXT,p_id INTEGER,t_id INTEGER NOT NULL,tree_id INTEGER NOT NULL, c_order INTEGER, att_data TEXT, PRIMARY KEY( ID AUTOINCREMENT))"); } /** @@ -296,6 +296,10 @@ debug_out("flush():"); + Statement stmt_insert_with_atts = db.prepare("insert into doctree(e_data, p_id, t_id, tree_id, att_data ) values( ?,?,?,?,? )"); + Statement stmt_insert = db.prepare("insert into doctree(e_data, p_id, t_id, tree_id ) values( ?,?,?,? )"); + Statement stmt_update_with_atts = db.prepare("update doctree set e_data=?, p_id=?, att_data=? where id=?"); + long new_tree = tree_id<0; DocOrderIterator it = new DocOrderIterator( all_nodes[tree_id] ); @@ -310,7 +314,25 @@ if(new_tree) tree_id=0; - db.run( format("insert into doctree(e_data, p_id, t_id, tree_id ) values( '%s', %d, %d, %d )", nd.e_data, nd.pid, nd.type, tree_id ) ); + if(tnode.hasAttributes()){ + writeln("saving atts: ", tnode.getAttsAsString() ); + stmt_insert_with_atts.reset(); + stmt_insert_with_atts.bind( 1, nd.e_data); + stmt_insert_with_atts.bind( 2, nd.pid); + stmt_insert_with_atts.bind( 3, nd.type); + stmt_insert_with_atts.bind( 4, tree_id); + stmt_insert_with_atts.bind( 5, tnode.getAttsAsString() ); + stmt_insert_with_atts.execute(); + + }else{ + stmt_insert.reset(); + stmt_insert.bind( 1, nd.e_data); + stmt_insert.bind( 2, nd.pid); + stmt_insert.bind( 3, nd.type); + stmt_insert.bind( 4, tree_id); + stmt_insert.execute(); + + } //update the node id long oldid = tnode.node_data.ID; @@ -323,8 +345,13 @@ if(new_tree) tree_id = newID; }else if( nd.dirty ){ - //dirty data and possibly pid too - db.run( format("update doctree set e_data='%s', p_id=%d where id=%d", nd.e_data, nd.pid, nd.ID ) ); + //dirty data and possibly pid and atributes + stmt_update_with_atts.reset(); + stmt_update_with_atts.bind( 1, nd.e_data ); + stmt_update_with_atts.bind( 2, nd.pid ); + stmt_update_with_atts.bind( 3, tnode.getAttsAsString() ); + stmt_update_with_atts.bind( 4, nd.ID ); + stmt_update_with_atts.execute(); nd.dirty = false; } } @@ -384,7 +411,7 @@ foreach( child; children){ NodeData nd = child.node_data; strRtn ~= get_openTag_commence( nd.type, nd.e_data ); - // --> add attributes if required + if(nd.type==TreeNodeType.element && child.hasAttributes() ) strRtn ~= " "~child.getAttsAsString(); strRtn ~= get_openTag_end( nd.type, nd.e_data ); strRtn ~= getTreeAsText_r( child ); strRtn ~= get_closeTag( nd.type, nd.e_data ); diff --git a/source/dom_persist_tests.d b/source/dom_persist_tests.d index 04d84a3..3077c25 100644 --- a/source/dom_persist_tests.d +++ b/source/dom_persist_tests.d @@ -249,6 +249,7 @@ //check db contents using a new tree Tree_Db tree3 = Tree_Db.loadTree( db, tree_list[0].tree_id ); html_out = tree3.getTreeAsText( ); + //writeln( html_out ); assert( html_out == "This is some text with more text"); writeln("Testing test element move"); @@ -302,6 +303,51 @@ } +unittest{ + + writeln( "Testing attributes" ); + + + auto db = Database( sqlite_filename ); + + TreeNameID[] tree_list = Tree_Db.getTreeList( db ); + assert( tree_list.length==2 ); + + Tree_Db tree = Tree_Db.loadTree( db, tree_list[0].tree_id ); + string html_out = tree.getTreeAsText( ); + //writeln( html_out ); + assert( html_out == "This is some text with more text"); + + TreeNode tn_body = tree.getTreeRoot().getChildAt(1).getChildAt(1); + tn_body.setAttribute( "border", "solid red 1px"); + + assert( tn_body.hasAttributes ); + tree.flush(); + + html_out = tree.getTreeAsText( ); + //writeln( html_out ); + assert( html_out == "This is some text with more text"); + + tn_body.setAttribute( "border", "solid red 2px"); + tn_body.setAttribute( "v-align", "top"); + html_out = tree.getTreeAsText( ); + //writeln( html_out ); + if( html_out != "This is some text with more text" && + html_out != "This is some text with more text" ){ + // the order of the attributes does not matter, either one will do. + assert(false); + } + + tn_body.removeAttribute("border"); + tree.flush(); + + html_out = tree.getTreeAsText( ); + assert( html_out == "This is some text with more text"); + +} + + + /*unittest{ diff --git a/source/nodecode.d b/source/nodecode.d index 79c54e7..83e36a3 100644 --- a/source/nodecode.d +++ b/source/nodecode.d @@ -10,6 +10,7 @@ import std.format; import dom_persist; +import attributeHandler; enum TreeNodeType { @@ -54,6 +55,7 @@ private: + AttributeMap attMap; Tree_Db owner_tree; // end private @@ -64,11 +66,41 @@ TreeNode[] child_nodes; bool dirty; // true indicates a change in child_nodes ordering - this( Tree_Db owner_tree, NodeData node_data){ + this( Tree_Db owner_tree, NodeData node_data, string atts = null){ this.owner_tree = owner_tree; this.node_data = node_data; + if(atts !is null ){ + attMap = new AttributeMap(atts); + } } + bool hasAttributes(){ + if(attMap is null) return false; + return attMap.getAttribCount()>0; + } + + void setAttribute( string name, string value ){ + if(attMap is null) attMap = new AttributeMap(); + attMap.setAttribute( name, value); + node_data.dirty = true; + } + + string getAttribute( string name ){ + if(attMap is null) return ""; + return attMap.getAttribute( name ); + } + + string getAttsAsString(){ + if(attMap is null) return ""; + return attMap.getAttsAsString(); + } + + void removeAttribute( string name ){ + if(attMap is null) return; + attMap.removeAttribute( name ); + node_data.dirty = true; + } + /** * Returns the parent node of this node or null if no parent exists (i.e. tree-root) */