File Coverage

blib/lib/XML/Stream/XPath/Query.pm
Criterion Covered Total %
statement 156 185 84.3
branch 58 76 76.3
condition 5 9 55.5
subroutine 11 12 91.6
pod 0 7 0.0
total 230 289 79.5


line stmt bran cond sub pod time code
1             ##############################################################################
2             #
3             # This library is free software; you can redistribute it and/or
4             # modify it under the terms of the GNU Library General Public
5             # License as published by the Free Software Foundation; either
6             # version 2 of the License, or (at your option) any later version.
7             #
8             # This library is distributed in the hope that it will be useful,
9             # but WITHOUT ANY WARRANTY; without even the implied warranty of
10             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11             # Library General Public License for more details.
12             #
13             # You should have received a copy of the GNU Library General Public
14             # License along with this library; if not, write to the
15             # Free Software Foundation, Inc., 59 Temple Place - Suite 330,
16             # Boston, MA 02111-1307, USA.
17             #
18             # Jabber
19             # Copyright (C) 1998-2004 Jabber Software Foundation http://jabber.org/
20             #
21             ##############################################################################
22              
23             package XML::Stream::XPath::Query;
24              
25 12     12   332 use 5.008;
  12         32  
  12         531  
26 12     12   52 use strict;
  12         18  
  12         381  
27 12     12   54 use warnings;
  12         15  
  12         345  
28 12     12   56 use Carp;
  12         13  
  12         949  
29 12     12   124 use vars qw( $VERSION );
  12         22  
  12         17956  
30              
31             $VERSION = "1.23_07";
32              
33             sub new
34             {
35 123     123 0 143 my $proto = shift;
36 123         177 my $self = { };
37              
38 123         378 bless($self,$proto);
39              
40 123         577 $self->{TOKENS} = [ '/','[',']','@','"',"'",'=','!','(',')',':',' ',','];
41 123         202 $self->{QUERY} = shift;
42            
43 123 50 33     646 if (!defined($self->{QUERY}) || ($self->{QUERY} eq ""))
44             {
45 0         0 confess("No query string specified");
46             }
47            
48 123         281 $self->parseQuery();
49            
50 123         254 return $self;
51             }
52              
53              
54             sub getNextToken
55             {
56 1334     1334 0 1047 my $self = shift;
57 1334         952 my $pos = shift;
58              
59 1334         959 my @toks = grep{ $_ eq substr($self->{QUERY},$$pos,1)} @{$self->{TOKENS}};
  17342         21109  
  1334         1771  
60 1334         2308 while( $#toks == -1 )
61             {
62 1680         1237 $$pos++;
63 1680 100       2685 if ($$pos > length($self->{QUERY}))
64             {
65 20         25 $$pos = length($self->{QUERY});
66 20         40 return 0;
67             }
68 1660         1167 @toks = grep{ $_ eq substr($self->{QUERY},$$pos,1)} @{$self->{TOKENS}};
  21580         25974  
  1660         1822  
69             }
70              
71 1314         1786 return $toks[0];
72             }
73              
74              
75             sub getNextIdentifier
76             {
77 350     350 0 338 my $self = shift;
78 350         280 my $pos = shift;
79 350         327 my $sp = $$pos;
80 350         472 $self->getNextToken($pos);
81 350         1061 return substr($self->{QUERY},$sp,$$pos-$sp);
82             }
83              
84              
85             sub getOp
86             {
87 556     556 0 490 my $self = shift;
88 556         432 my $pos = shift;
89 556         474 my $in_context = shift;
90 556 100       897 $in_context = 0 unless defined($in_context);
91              
92 556         395 my $ret_op;
93              
94 556         480 my $loop = 1;
95 556         770 while( $loop )
96             {
97 892         714 my $pos_start = $$pos;
98              
99 892         1170 my $token = $self->getNextToken($pos);
100 892 50 66     1667 if (($token eq "0") && $in_context)
101             {
102 0         0 return;
103             }
104              
105 892         841 my $token_start = ++$$pos;
106 892         621 my $ident;
107            
108 892 50       1136 if (defined($token))
109             {
110              
111 892 100 66     4304 if ($pos_start != ($token_start-1))
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    50          
112             {
113 61         70 $$pos = $pos_start;
114 61         119 my $temp_ident = $self->getNextIdentifier($pos);
115 61         223 $ret_op = XML::Stream::XPath::NodeOp->new($temp_ident,"0");
116             }
117             elsif ($token eq "/")
118             {
119 49 100       98 if (substr($self->{QUERY},$token_start,1) eq "/")
120             {
121 14         18 $$pos++;
122 14         30 my $temp_ident = $self->getNextIdentifier($pos);
123 14         61 $ret_op = XML::Stream::XPath::AllOp->new($temp_ident);
124             }
125             else
126             {
127 35         57 my $temp_ident = $self->getNextIdentifier($pos);
128 35 100       72 if ($temp_ident ne "")
129             {
130 29 50       82 $ret_op = XML::Stream::XPath::NodeOp->new($temp_ident,($pos_start == 0 ? "1" : "0"));
131             }
132             }
133             }
134             elsif ($token eq "\@")
135             {
136 164         301 $ret_op = XML::Stream::XPath::AttributeOp->new($self->getNextIdentifier($pos));
137             }
138             elsif ($token eq "]")
139             {
140 92 50       156 if ($in_context eq "[")
141             {
142 92         95 $ret_op = pop(@{$self->{OPS}});
  92         156  
143 92         111 $in_context = 0;
144             }
145             else
146             {
147 0         0 confess("Found ']' but not in context");
148 0         0 return;
149             }
150             }
151             elsif (($token eq "\"") || ($token eq "\'"))
152             {
153 156         300 $$pos = index($self->{QUERY},$token,$token_start);
154 156         642 $ret_op = XML::Stream::XPath::Op->new("LITERAL",substr($self->{QUERY},$token_start,$$pos-$token_start));
155 156         215 $$pos++;
156             }
157             elsif ($token eq " ")
158             {
159 76         130 $ident = $self->getNextIdentifier($pos);
160 76 100       231 if ($ident eq "and")
    100          
161             {
162 4         3 $$pos++;
163 4         20 my $tmp_op = $self->getOp($pos,$in_context);
164 4 50       10 if (!defined($tmp_op))
165             {
166 0         0 confess("Invalid 'and' operation");
167 0         0 return;
168             }
169 4         6 $ret_op = XML::Stream::XPath::AndOp->new($self->{OPS}->[$#{$self->{OPS}}],$tmp_op);
  4         21  
170 4         5 $in_context = 0;
171 4         3 pop(@{$self->{OPS}});
  4         7  
172             }
173             elsif ($ident eq "or")
174             {
175 68         83 $$pos++;
176 68         117 my $tmp_op = $self->getOp($pos,$in_context);
177 68 50       143 if (!defined($tmp_op))
178             {
179 0         0 confess("Invalid 'or' operation");
180 0         0 return;
181             }
182 68         103 $ret_op = XML::Stream::XPath::OrOp->new($self->{OPS}->[$#{$self->{OPS}}],$tmp_op);
  68         283  
183 68         78 $in_context = 0;
184 68         64 pop(@{$self->{OPS}});
  68         109  
185             }
186             }
187             elsif ($token eq "[")
188             {
189 92 50       176 if ($self->getNextToken($pos) eq "]")
190             {
191 0 0       0 if ($$pos == $token_start)
192             {
193 0         0 confess("Nothing in the []");
194 0         0 return;
195             }
196            
197 0         0 $$pos = $token_start;
198 0         0 my $val = $self->getNextIdentifier($pos);
199 0 0       0 if ($val =~ /^\d+$/)
200             {
201 0         0 $ret_op = XML::Stream::XPath::PositionOp->new($val);
202 0         0 $$pos++;
203             }
204             else
205             {
206 0         0 $$pos = $pos_start + 1;
207 0         0 $ret_op = XML::Stream::XPath::ContextOp->new($self->getOp($pos,$token));
208             }
209             }
210             else
211             {
212 92         155 $$pos = $pos_start + 1;
213 92         221 $ret_op = XML::Stream::XPath::ContextOp->new($self->getOp($pos,$token));
214             }
215             }
216             elsif ($token eq "(")
217             {
218             #-------------------------------------------------------------
219             # The function name would have been mistaken for a NodeOp.
220             # Pop it off the back and get the function name.
221             #-------------------------------------------------------------
222 23         23 my $op = pop(@{$self->{OPS}});
  23         48  
223 23 50       83 if ($op->getType() ne "NODE")
224             {
225 0         0 confess("No function name specified.");
226             }
227 23         74 my $function = $op->getValue();
228 23 50       65 if (!exists($XML::Stream::XPath::FUNCTIONS{$function}))
229             {
230 0         0 confess("Undefined function \"$function\"");
231             }
232 23         92 $ret_op = XML::Stream::XPath::FunctionOp->new($function);
233              
234 23         22 my $op_pos = $#{$self->{OPS}} + 1;
  23         45  
235              
236 23         82 $self->getOp($pos,$token);
237            
238 23         19 foreach my $arg ($op_pos..$#{$self->{OPS}})
  23         77  
239             {
240 6         19 $ret_op->addArg($self->{OPS}->[$arg]);
241             }
242              
243 23         28 splice(@{$self->{OPS}},$op_pos);
  23         57  
244            
245             }
246             elsif ($token eq ")")
247             {
248 23 50       40 if ($in_context eq "(")
249             {
250 23         31 $ret_op = undef;
251 23         33 $in_context = 0;
252             }
253             else
254             {
255 0         0 confess("Found ')' but not in context");
256             }
257             }
258             elsif ($token eq ",")
259             {
260 2 50       5 if ($in_context ne "(")
261             {
262 0         0 confess("Found ',' but not in a function");
263             }
264            
265             }
266             elsif ($token eq "=")
267             {
268 152         147 my $tmp_op;
269 152         267 while(!defined($tmp_op))
270             {
271 152         381 $tmp_op = $self->getOp($pos);
272             }
273 152         216 $ret_op = XML::Stream::XPath::EqualOp->new($self->{OPS}->[$#{$self->{OPS}}],$tmp_op);
  152         506  
274 152         182 pop(@{$self->{OPS}});
  152         291  
275             }
276             elsif ($token eq "!")
277             {
278 2 50       9 if (substr($self->{QUERY},$token_start,1) ne "=")
279             {
280 0         0 confess("Badly formed !=");
281             }
282 2         3 $$pos++;
283            
284 2         2 my $tmp_op;
285 2         5 while(!defined($tmp_op))
286             {
287 4         5 $tmp_op = $self->getOp($pos);
288             }
289 2         5 $ret_op = XML::Stream::XPath::NotEqualOp->new($self->{OPS}->[$#{$self->{OPS}}],$tmp_op);
  2         10  
290 2         2 pop(@{$self->{OPS}});
  2         3  
291             }
292             else
293             {
294 0         0 confess("Unhandled \"$token\"");
295             }
296              
297 892 100       1385 if ($in_context)
298             {
299 336 100       508 if (defined($ret_op))
300             {
301 332         255 push(@{$self->{OPS}},$ret_op);
  332         534  
302             }
303 336         334 $ret_op = undef;
304             }
305             }
306             else
307             {
308 0         0 confess("Token undefined");
309             }
310            
311 892 100       2110 $loop = 0 unless $in_context;
312             }
313              
314 556         1066 return $ret_op;
315             }
316              
317              
318             sub parseQuery
319             {
320 123     123 0 149 my $self = shift;
321 123         121 my $query = shift;
322              
323 123         103 my $op;
324 123         125 my $pos = 0;
325 123         317 while($pos < length($self->{QUERY}))
326             {
327 213         399 $op = $self->getOp(\$pos);
328 213 100       451 if (defined($op))
329             {
330 207         176 push(@{$self->{OPS}},$op);
  207         603  
331             }
332             }
333              
334             #foreach my $op (@{$self->{OPS}})
335             #{
336             # $op->display();
337             #}
338              
339 123         155 return 1;
340             }
341              
342              
343             sub execute
344             {
345 123     123 0 141 my $self = shift;
346 123         121 my $root = shift;
347              
348 123         432 my $ctxt = XML::Stream::XPath::Value->new($root);
349              
350 123         140 foreach my $op (@{$self->{OPS}})
  123         271  
351             {
352 192 100       488 if (!$op->isValid(\$ctxt))
353             {
354 75         183 $ctxt->setValid(0);
355 75         177 return $ctxt;
356             }
357             }
358              
359 48         129 $ctxt->setValid(1);
360 48         93 return $ctxt;
361             }
362              
363              
364             sub check
365             {
366 0     0 0   my $self = shift;
367 0           my $root = shift;
368              
369 0           my $ctxt = $self->execute($root);
370 0           return $ctxt->check();
371             }
372              
373              
374             1;
375