Skip Menu |

This queue is for tickets about the XML-Twig CPAN distribution.

Report information
The Basics
Id: 390
Status: resolved
Priority: 0/
Queue: XML-Twig

People
Owner: Nobody in particular
Requestors: Julian.Arnold [...] hsbcgroup.com
Cc:
AdminCc:

Bug Information
Severity: Critical
Broken in: 3.02
Fixed in: 3.03



Subject: Issue with erase method against an Elt
I suspect there is a bug with the erase method in the following code snippet (lines 3814-3818): else { # elt was the last child $elt->{parent}->set_last_child( $elt->{next_sibling}); } } The elt being erased is the last child, therefore elt->{next_sibling} is set to undef. This is causing the following problems (those I have seen): 1) Use of uninitialized value in numeric eq (==) at line 3901 (paste_after method): $parent->set_last_child( $elt) if( $parent->{last_child}== $ref); Obviously occurs because $parent->{last_child} has been set to undef during erase against the previous last element; 2) A new element not being pasted during paste_last_element method: sub paste_last_child { my( $elt, $ref)= @_; my( $parent, $prev_sibling, $next_sibling ); $parent= $ref; $prev_sibling= $ref->{last_child}; delete $ref->{empty} if( $ref->is_empty); $elt->set_parent( $parent); $parent->set_last_child( $elt); $parent->{first_child}= $elt unless( $parent->{first_child}); $elt->set_prev_sibling( $prev_sibling); $prev_sibling->{next_sibling}= $elt if( $prev_sibling); $elt->{next_sibling}= undef; } As you can see, $prev_sibling is being assigned $ref->{last_child}, which is undef. Which means the following is not functioning as expected, resulting in the element being orphaned: $elt->set_prev_sibling( $prev_sibling); $prev_sibling->{next_sibling}= $elt if( $prev_sibling); To reproduce this problem, create an XML document with a parent that has multiple children. Delete the last child and then try to add it back as the last child. Possible solution: I suspect that if you change the $elt line in the erase method: { # elt was the last child $elt->{parent}->set_last_child( $elt->{next_sibling}); } to: { # elt was the last child $elt->{parent}->set_last_child( $elt->{prev_sibling}); } this will correct it
You are right. I have to clean up a bit of totally unrelated code and I will put the new version up on my website, then on CPAN. I was wondering why you labelled this as critical though, as erase is not such an often-used method. Especially as the bug only shows when you erase an empty element, which doesn't make a lot of sense unless... maybe you are using erase to delete elements. cut and delete are used to delete elements, not erase. erase should probably have been called unwrap (it will be aliased to unwrap in the next version): it removes an element while leaving its children in place. In any case, i am quite ashamed of the bug, and even more so that I let it slip through the tests. Here is a test case that shows how the last_child of elt gets improperly set to undef: #!/bin/perl -w use strict; use XML::Twig; my $t= XML::Twig->new( pretty_print => 'indented'); $t->parse( \*DATA); my $elt= $t->first_elt( 'empty'); print "elt: gi is ", $elt->gi, " id is ", $elt->att( 'id'), "\n"; $elt->erase; print "doc:\n"; $t->print; print "\n"; my $last_child= $t->root->last_child; print "last child: gi is ", $last_child->gi, " id is ", $last_child->att( 'id'), "\n"; __DATA__ <doc> <elt id="elt1"/> <empty/> </doc>