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 11     11   325 use 5.008;
  11         37  
  11         471  
26 11     11   65 use strict;
  11         21  
  11         402  
27 11     11   61 use warnings;
  11         18  
  11         414  
28 11     11   70 use Carp;
  11         25  
  11         1278  
29 11     11   68 use vars qw( $VERSION );
  11         36  
  11         25817  
30              
31             $VERSION = "1.23_06";
32              
33             sub new
34             {
35 123     123 0 1030 my $proto = shift;
36 123         233 my $self = { };
37              
38 123         471 bless($self,$proto);
39              
40 123         2713 $self->{TOKENS} = [ '/','[',']','@','"',"'",'=','!','(',')',':',' ',','];
41 123         444 $self->{QUERY} = shift;
42            
43 123 50 33     1699 if (!defined($self->{QUERY}) || ($self->{QUERY} eq ""))
44             {
45 0         0 confess("No query string specified");
46             }
47            
48 123         551 $self->parseQuery();
49            
50 123         348 return $self;
51             }
52              
53              
54             sub getNextToken
55             {
56 1334     1334 0 1684 my $self = shift;
57 1334         1647 my $pos = shift;
58              
59 1334         1729 my @toks = grep{ $_ eq substr($self->{QUERY},$$pos,1)} @{$self->{TOKENS}};
  17342         44710  
  1334         2641  
60 1334         3648 while( $#toks == -1 )
61             {
62 1680         2289 $$pos++;
63 1680 100       3973 if ($$pos > length($self->{QUERY}))
64             {
65 20         42 $$pos = length($self->{QUERY});
66 20         48 return 0;
67             }
68 1660         1742 @toks = grep{ $_ eq substr($self->{QUERY},$$pos,1)} @{$self->{TOKENS}};
  21580         60425  
  1660         3556  
69             }
70              
71 1314         2832 return $toks[0];
72             }
73              
74              
75             sub getNextIdentifier
76             {
77 350     350 0 436 my $self = shift;
78 350         386 my $pos = shift;
79 350         424 my $sp = $$pos;
80 350         608 $self->getNextToken($pos);
81 350         1834 return substr($self->{QUERY},$sp,$$pos-$sp);
82             }
83              
84              
85             sub getOp
86             {
87 556     556 0 693 my $self = shift;
88 556         739 my $pos = shift;
89 556         621 my $in_context = shift;
90 556 100       1193 $in_context = 0 unless defined($in_context);
91              
92 556         520 my $ret_op;
93              
94 556         3052 my $loop = 1;
95 556         1049 while( $loop )
96             {
97 892         1144 my $pos_start = $$pos;
98              
99 892         1696 my $token = $self->getNextToken($pos);
100 892 50 66     2408 if (($token eq "0") && $in_context)
101             {
102 0         0 return;
103             }
104              
105 892         1351 my $token_start = ++$$pos;
106 892         1003 my $ident;
107            
108 892 50       1500 if (defined($token))
109             {
110              
111 892 100 66     5900 if ($pos_start != ($token_start-1))
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    50          
112             {
113 61         90 $$pos = $pos_start;
114 61         1480 my $temp_ident = $self->getNextIdentifier($pos);
115 61         439 $ret_op = XML::Stream::XPath::NodeOp->new($temp_ident,"0");
116             }
117             elsif ($token eq "/")
118             {
119 49 100       130 if (substr($self->{QUERY},$token_start,1) eq "/")
120             {
121 14         22 $$pos++;
122 14         37 my $temp_ident = $self->getNextIdentifier($pos);
123 14         79 $ret_op = XML::Stream::XPath::AllOp->new($temp_ident);
124             }
125             else
126             {
127 35         85 my $temp_ident = $self->getNextIdentifier($pos);
128 35 100       102 if ($temp_ident ne "")
129             {
130 29 50       147 $ret_op = XML::Stream::XPath::NodeOp->new($temp_ident,($pos_start == 0 ? "1" : "0"));
131             }
132             }
133             }
134             elsif ($token eq "\@")
135             {
136 164         408 $ret_op = XML::Stream::XPath::AttributeOp->new($self->getNextIdentifier($pos));
137             }
138             elsif ($token eq "]")
139             {
140 92 50       205 if ($in_context eq "[")
141             {
142 92         114 $ret_op = pop(@{$self->{OPS}});
  92         184  
143 92         154 $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         488 $$pos = index($self->{QUERY},$token,$token_start);
154 156         911 $ret_op = XML::Stream::XPath::Op->new("LITERAL",substr($self->{QUERY},$token_start,$$pos-$token_start));
155 156         325 $$pos++;
156             }
157             elsif ($token eq " ")
158             {
159 76         179 $ident = $self->getNextIdentifier($pos);
160 76 100       299 if ($ident eq "and")
    100          
161             {
162 4         6 $$pos++;
163 4         27 my $tmp_op = $self->getOp($pos,$in_context);
164 4 50       20 if (!defined($tmp_op))
165             {
166 0         0 confess("Invalid 'and' operation");
167 0         0 return;
168             }
169 4         12 $ret_op = XML::Stream::XPath::AndOp->new($self->{OPS}->[$#{$self->{OPS}}],$tmp_op);
  4         25  
170 4         9 $in_context = 0;
171 4         6 pop(@{$self->{OPS}});
  4         11  
172             }
173             elsif ($ident eq "or")
174             {
175 68         84 $$pos++;
176 68         151 my $tmp_op = $self->getOp($pos,$in_context);
177 68 50       158 if (!defined($tmp_op))
178             {
179 0         0 confess("Invalid 'or' operation");
180 0         0 return;
181             }
182 68         134 $ret_op = XML::Stream::XPath::OrOp->new($self->{OPS}->[$#{$self->{OPS}}],$tmp_op);
  68         365  
183 68         107 $in_context = 0;
184 68         85 pop(@{$self->{OPS}});
  68         137  
185             }
186             }
187             elsif ($token eq "[")
188             {
189 92 50       237 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         170 $$pos = $pos_start + 1;
213 92         279 $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         47 my $op = pop(@{$self->{OPS}});
  23         62  
223 23 50       168 if ($op->getType() ne "NODE")
224             {
225 0         0 confess("No function name specified.");
226             }
227 23         109 my $function = $op->getValue();
228 23 50       115 if (!exists($XML::Stream::XPath::FUNCTIONS{$function}))
229             {
230 0         0 confess("Undefined function \"$function\"");
231             }
232 23         137 $ret_op = XML::Stream::XPath::FunctionOp->new($function);
233              
234 23         39 my $op_pos = $#{$self->{OPS}} + 1;
  23         63  
235              
236 23         150 $self->getOp($pos,$token);
237            
238 23         41 foreach my $arg ($op_pos..$#{$self->{OPS}})
  23         118  
239             {
240 6         33 $ret_op->addArg($self->{OPS}->[$arg]);
241             }
242              
243 23         51 splice(@{$self->{OPS}},$op_pos);
  23         292  
244            
245             }
246             elsif ($token eq ")")
247             {
248 23 50       57 if ($in_context eq "(")
249             {
250 23         40 $ret_op = undef;
251 23         79 $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       10 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         190 my $tmp_op;
269 152         311 while(!defined($tmp_op))
270             {
271 152         470 $tmp_op = $self->getOp($pos);
272             }
273 152         264 $ret_op = XML::Stream::XPath::EqualOp->new($self->{OPS}->[$#{$self->{OPS}}],$tmp_op);
  152         705  
274 152         400 pop(@{$self->{OPS}});
  152         289  
275             }
276             elsif ($token eq "!")
277             {
278 2 50       13 if (substr($self->{QUERY},$token_start,1) ne "=")
279             {
280 0         0 confess("Badly formed !=");
281             }
282 2         5 $$pos++;
283            
284 2         4 my $tmp_op;
285 2         8 while(!defined($tmp_op))
286             {
287 4         11 $tmp_op = $self->getOp($pos);
288             }
289 2         5 $ret_op = XML::Stream::XPath::NotEqualOp->new($self->{OPS}->[$#{$self->{OPS}}],$tmp_op);
  2         17  
290 2         3 pop(@{$self->{OPS}});
  2         6  
291             }
292             else
293             {
294 0         0 confess("Unhandled \"$token\"");
295             }
296              
297 892 100       2506 if ($in_context)
298             {
299 336 100       660 if (defined($ret_op))
300             {
301 332         413 push(@{$self->{OPS}},$ret_op);
  332         692  
302             }
303 336         508 $ret_op = undef;
304             }
305             }
306             else
307             {
308 0         0 confess("Token undefined");
309             }
310            
311 892 100       4540 $loop = 0 unless $in_context;
312             }
313              
314 556         1498 return $ret_op;
315             }
316              
317              
318             sub parseQuery
319             {
320 123     123 0 178 my $self = shift;
321 123         170 my $query = shift;
322              
323 123         153 my $op;
324 123         196 my $pos = 0;
325 123         388 while($pos < length($self->{QUERY}))
326             {
327 213         809 $op = $self->getOp(\$pos);
328 213 100       569 if (defined($op))
329             {
330 207         243 push(@{$self->{OPS}},$op);
  207         908  
331             }
332             }
333              
334             #foreach my $op (@{$self->{OPS}})
335             #{
336             # $op->display();
337             #}
338              
339 123         218 return 1;
340             }
341              
342              
343             sub execute
344             {
345 123     123 0 199 my $self = shift;
346 123         152 my $root = shift;
347              
348 123         853 my $ctxt = XML::Stream::XPath::Value->new($root);
349              
350 123         177 foreach my $op (@{$self->{OPS}})
  123         301  
351             {
352 192 100       907 if (!$op->isValid(\$ctxt))
353             {
354 76         203 $ctxt->setValid(0);
355 76         220 return $ctxt;
356             }
357             }
358              
359 47         179 $ctxt->setValid(1);
360 47         153 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