@@ -116,20 +116,13 @@ sub find_xpath { my( $elt, $path)= @_; return xp->find(
sub matches { my( $elt, $path)= @_; return xp->matches( $elt, $path, $elt); }
sub set_namespace { my $elt= shift; xp->new->set_namespace( @_); }
-{
my %roots;
- sub DESTROY {
- my $self = shift;
- delete $roots{$self};
- $self->SUPER::DESTROY if $self->can("SUPER::DESTROY");
- }
sub getParentNode
{ my $elt= shift;
return $elt->{_parent} if $elt->{_parent};
return $roots{$elt} //= bless { _root => $elt }, 'HTML::TreeBuilder::XPath::Root';
}
-}
sub getRootNode
{ my $elt= shift;
@@ -207,21 +200,35 @@ sub string_value
# called on a parent, with a child as second argument and its rank as third
# returns the child if it is already an element, or
# a new HTML::TreeBuilder::XPath::Text element if it is a plain string
+ my %text_children;
+
+sub DESTROY {
+ my $self = shift;
+ delete $text_children{$self};
+ delete $roots{$self};
+ $self->SUPER::DESTROY if $self->can("SUPER::DESTROY");
+}
+
sub _child_as_object
{ my( $elt, $elt_or_text, $rank)= @_;
return undef unless( defined $elt_or_text);
if( ! ref $elt_or_text)
{ # $elt_or_text is a string, turn it into a TextNode object
- $elt_or_text= bless { _content => $elt_or_text, _parent => $elt, },
- 'HTML::TreeBuilder::XPath::TextNode'
- ;
- Scalar::Util::weaken($elt_or_text->{_parent});
+ if(!defined $text_children{$elt}{$rank} ||
+ ($elt_or_text= $text_children{$elt}{$rank}, $elt_or_text->{_rank} != $rank || $elt_or_text->{_parent} != $elt)) {
+ $elt_or_text = ($text_children{$elt}{$rank} =
+ bless { _content => $elt_or_text, _parent => $elt, },
+ 'HTML::TreeBuilder::XPath::TextNode')
+ ;
+ Scalar::Util::weaken($elt_or_text->{_parent});
+ }
}
if( ref $rank) { warn "rank is a ", ref( $rank), " elt_or_text is a ", ref( $elt_or_text); }
$elt_or_text->{_rank}= $rank; # used for sorting;
return $elt_or_text;
}
+
sub toString { return shift->as_XML( @_); }
# produces better looking XML
#########################
-use Test::More tests => 31;
+use Test::More tests => 34;
BEGIN { use_ok('HTML::TreeBuilder::XPath') };
#########################
@@ -37,6 +37,10 @@ is( $html->findvalue( '//*[@id="foo"]/@id|//*[@id="foo"]/@class'), 'myspanfoo',
is( $html->findvalue( '//*[@id="foo"]/@class|//*[@id="foo"]/@id'), 'myspanfoo', '2 atts on same element (unsorted)');
is( $html->findvalue( 'count(/|/)' ), 1, 'count roots');
+is( $html->findvalue( 'count(/html/body/h1/text()|/html/body/h1/text())' ), 1, 'count identical text nodes');
+is( $html->findvalue( 'count(id("bq")/@id|id("bq")/@id)' ), 1, 'count identical attribute nodes');
+
+is( $html->findvalue( '//text()[contains(., "folks")]/preceding-sibling::*[1]' ), 'all', 'previous sibling on a text node');
is( $html->findvalue( '//b'), 'boldall', '2 texts');
is( join( '|', $html->findvalues( '//b')), 'bold|all', '2 texts with findvalues');