File Coverage

blib/lib/XML/DOM/XPath.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             # $Id: XPath.pm,v 1.11 2005/10/18 08:39:04 mrodrigu Exp $
2              
3             package XML::DOM::XPath;
4              
5 33     33   318524 use strict;
  33         86  
  33         1223  
6              
7 33     33   37291 use XML::XPathEngine;
  33         1355188  
  33         1230  
8 33     33   85671 use XML::DOM;
  0            
  0            
9              
10             use vars qw($VERSION);
11             $VERSION="0.14";
12              
13             my $xp_field; # the field in the document that contains the XML::XPathEngine object
14             my $parent_field; # the field in an attribute that contains the parent element
15              
16             BEGIN
17             { # this is probably quite wrong, I have to figure out the internal structure of nodes better
18             $xp_field = 11;
19             $parent_field = 12;
20             }
21              
22             package XML::DOM::Document;
23              
24             sub findnodes { my( $dom, $path)= @_; return $dom->xp->findnodes( $path, $dom); }
25             sub findnodes_as_string { my( $dom, $path)= @_; return $dom->xp->findnodes_as_string( $path, $dom); }
26             sub findnodes_as_strings { my( $dom, $path)= @_; return $dom->xp->findnodes_as_strings( $path, $dom); }
27             sub findvalue { my( $dom, $path)= @_; return $dom->xp->findvalue( $path, $dom); }
28             sub exists { my( $dom, $path)= @_; return $dom->xp->exists( $path, $dom); }
29             sub find { my( $dom, $path)= @_; return $dom->xp->find( $path, $dom); }
30             sub matches { my( $dom, $path)= @_; return $dom->xp->matches( $dom, $path, $dom); }
31             sub set_namespace { my $dom= shift; $dom->xp->set_namespace( @_); }
32              
33             sub cmp { return $_[1]->isa( 'XML::DOM::Document') ? 0 : 1; }
34              
35             sub getRootNode { return $_[0]; }
36             sub xp { return $_[0]->[$xp_field] }
37              
38             { no warnings;
39             # copied from the original DOM package, with the addition of the creation of the XML::XPathEngine object
40             sub new
41             { my ($class) = @_;
42             my $self = bless [], $class;
43              
44             # keep Doc pointer, even though getOwnerDocument returns undef
45             $self->[_Doc] = $self;
46             $self->[_C] = new XML::DOM::NodeList;
47             $self->[$xp_field]= XML::XPathEngine->new();
48             $self;
49             }
50             }
51              
52             package XML::DOM::Node;
53              
54             sub findnodes { my( $node, $path)= @_; return $node->xp->findnodes( $path, $node); }
55             sub findnodes_as_string { my( $node, $path)= @_; return $node->xp->findnodes_as_string( $path, $node); }
56             sub findvalue { my( $node, $path)= @_; return $node->xp->findvalue( $path, $node); }
57             sub exists { my( $node, $path)= @_; return $node->xp->exists( $path, $node); }
58             sub find { my( $node, $path)= @_; return $node->xp->find( $path, $node); }
59             sub matches { my( $node, $path)= @_; return $node->xp->matches( $node->getOwnerDocument, $path, $node); }
60              
61             sub isCommentNode { 0 };
62             sub isPINode { 0 };
63              
64             sub to_number { return XML::XPathEngine::Number->new( shift->string_value); }
65              
66             sub getParent { return $_[0]->getParentNode; }
67             sub getRootNode { return $_[0]->getOwnerDocument; }
68              
69             sub xp { return $_[0]->getOwnerDocument->xp; }
70              
71             # this method exists in XML::DOM but it returns undef, while
72             # XML::XPathEngine needs it, but wants an array... bother!
73             # This method is actually redefined for XML::DOM::Element, but needs
74             # to be here for other types of nodes.
75             { no warnings;
76             sub getAttributes
77             { if( caller(0)!~ m{^XML::XPathEngine}) { return undef; } # XML::DOM
78             else { my @atts= (); return wantarray ? @atts : \@atts; } # XML::XPathEngine
79             }
80             }
81              
82             sub cmp
83             { my( $a, $b)=@_;
84              
85             # easy cases
86             return 0 if( $a == $b);
87             return -1 if( $a->isAncestor($b)); # a starts before b
88             return 1 if( $b->isAncestor($a)); # a starts after b
89              
90             # special case for 2 attributes of the same element
91             # order is dictionary order of the attribute names
92             if( $a->isa( 'XML::DOM::Attr') && $b->isa( 'XML::DOM::Attr'))
93             { if( $a->getParent == $b->getParent)
94             { return $a->getName cmp $b->getName }
95             else
96             { return $a->getParent->cmp( $b->getParent); }
97             }
98              
99             # ancestors does not include the element itself
100             my @a_pile= ($a->ancestors_or_self);
101             my @b_pile= ($b->ancestors_or_self);
102              
103             # the 2 elements are not in the same twig
104             return undef unless( $a_pile[-1] == $b_pile[-1]);
105              
106             # find the first non common ancestors (they are siblings)
107             my $a_anc= pop @a_pile;
108             my $b_anc= pop @b_pile;
109              
110             while( $a_anc == $b_anc)
111             { $a_anc= pop @a_pile;
112             $b_anc= pop @b_pile;
113             }
114              
115             # from there move left and right and figure out the order
116             my( $a_prev, $a_next, $b_prev, $b_next)= ($a_anc, $a_anc, $b_anc, $b_anc);
117             while()
118             { $a_prev= $a_prev->getPreviousSibling || return( -1);
119             return 1 if( $a_prev == $b_next);
120             $a_next= $a_next->getNextSibling || return( 1);
121             return -1 if( $a_next == $b_prev);
122             $b_prev= $b_prev->getPreviousSibling || return( 1);
123             return -1 if( $b_prev == $a_next);
124             $b_next= $b_next->getNextSibling || return( -1);
125             return 1 if( $b_next == $a_prev);
126             }
127             }
128              
129             sub ancestors_or_self
130             { my $node= shift;
131             my @ancestors= ($node);
132             while( $node= $node->getParent)
133             { push @ancestors, $node; }
134             return @ancestors;
135             }
136              
137             sub getNamespace
138             { my $node= shift;
139             my $prefix= shift() || $node->ns_prefix;
140             if( my $expanded= $node->get_namespace( $prefix))
141             { return XML::DOM::Namespace->new( $prefix, $expanded); }
142             else
143             { return XML::DOM::Namespace->new( $prefix, ''); }
144             }
145              
146             sub getLocalName
147             { my $node= shift;
148             (my $local= $node->getName)=~ s{^[^:]*:}{};
149             return $local;
150             }
151              
152             sub ns_prefix
153             { my $node= shift;
154             if( $node->getName=~ m{^([^:]*):})
155             { return $1; }
156             else
157             { return( '#default'); } # should it be '' ?
158             }
159              
160             BEGIN
161             { my %DEFAULT_NS= ( xml => "http://www.w3.org/XML/1998/namespace",
162             xmlns => "http://www.w3.org/2000/xmlns/",
163             );
164            
165             sub get_namespace
166             { my $node= shift;
167             my $prefix= defined $_[0] ? shift() : $node->ns_prefix;
168             if( $prefix eq "#default") { $prefix=''}
169             my $ns_att= $prefix ? "xmlns:$prefix" : "xmlns";
170             my $expanded= $DEFAULT_NS{$prefix} || $node->inherit_att( $ns_att) || '';
171             return $expanded;
172             }
173             }
174              
175             sub inherit_att
176             { my $node= shift;
177             my $att= shift;
178              
179             do
180             { if( ($node->getNodeType == ELEMENT_NODE) && ($node->getAttribute( $att)))
181             { return $node->getAttribute( $att); }
182             } while( $node= $node->getParentNode);
183             return undef;
184             }
185            
186             package XML::DOM::Element;
187              
188             sub getName { return $_[0]->getTagName; }
189              
190             { no warnings;
191              
192             # this method exists in XML::DOM but it returns a NamedNodeMap object
193             # XML::XPathEngine needs it, but wants an array... bother!
194             sub getAttributes
195             { # in any case we need $_[0]->[_A] to be filled
196             $_[0]->[_A] ||= XML::DOM::NamedNodeMap->new (Doc => $_[0]->[_Doc], Parent => $_[0]);
197              
198             if( caller(0)!~ m{^XML::XPathEngine})
199             { # the original XML::DOM value
200             return $_[0]->[_A];
201             }
202             else
203             { # this is what XML::XPathEngine needs
204             my $elt= shift;
205             my @atts= grep { ref $_ eq 'XML::DOM::Attr' } values %{$elt->[1]};
206             $_->[$parent_field]= $elt foreach (@atts);
207             return wantarray ? @atts : \@atts;
208             }
209             }
210              
211             }
212              
213             # nearly straight from XML::XPathEngine
214             sub string_value
215             { my $self = shift;
216             my $string = '';
217             foreach my $kid ($self->getChildNodes)
218             { if ($kid->getNodeType == ELEMENT_NODE || $kid->getNodeType == TEXT_NODE)
219             { $string .= $kid->string_value; }
220             }
221             return $string;
222             }
223              
224              
225            
226             package XML::DOM::Attr;
227              
228             # needed for the sort
229             sub inherit_att { return $_[0]->getParent->inherit_att( @_); }
230              
231             sub getParent { return $_[0]->[$parent_field]; }
232             sub string_value { return $_[0]->getValue; }
233             sub getData { return $_[0]->getValue; }
234              
235              
236             package XML::DOM::Text;
237             sub string_value { return $_[0]->getData; }
238              
239              
240             package XML::DOM::Comment;
241             sub isCommentNode { 1 };
242             sub string_value { return $_[0]->getData; }
243              
244              
245             package XML::DOM::ProcessingInstruction;
246              
247             sub isPINode { 1 };
248             sub isProcessingInstructionNode { 1 };
249             sub string_value { return $_[0]->getData; }
250             sub value { return $_[0]->getData; }
251              
252              
253             package XML::DOM::Namespace;
254              
255             sub new
256             { my( $class, $prefix, $expanded)= @_;
257             bless { prefix => $prefix, expanded => $expanded }, $class;
258             }
259              
260             sub isNamespaceNode { 1; }
261              
262             sub getPrefix { $_[0]->{prefix}; }
263             sub getExpanded { $_[0]->{expanded}; }
264             sub getValue { $_[0]->{expanded}; }
265             sub getData { $_[0]->{expanded}; }
266              
267              
268             1;
269             __END__