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   250 use 5.008;
  12         31  
  12         487  
26 12     12   191 use strict;
  12         20  
  12         431  
27 12     12   54 use warnings;
  12         25  
  12         427  
28 12     12   57 use Carp;
  12         17  
  12         1053  
29 12     12   82 use vars qw( $VERSION );
  12         22  
  12         21036  
30              
31             $VERSION = "1.24";
32              
33             sub new
34             {
35 123     123 0 167 my $proto = shift;
36 123         190 my $self = { };
37              
38 123         429 bless($self,$proto);
39              
40 123         665 $self->{TOKENS} = [ '/','[',']','@','"',"'",'=','!','(',')',':',' ',','];
41 123         233 $self->{QUERY} = shift;
42            
43 123 50 33     717 if (!defined($self->{QUERY}) || ($self->{QUERY} eq ""))
44             {
45 0         0 confess("No query string specified");
46             }
47            
48 123         339 $self->parseQuery();
49            
50 123         289 return $self;
51             }
52              
53              
54             sub getNextToken
55             {
56 1334     1334 0 1138 my $self = shift;
57 1334         1064 my $pos = shift;
58              
59 1334         1052 my @toks = grep{ $_ eq substr($self->{QUERY},$$pos,1)} @{$self->{TOKENS}};
  17342         24137  
  1334         2008  
60 1334         2745 while( $#toks == -1 )
61             {
62 1680         1331 $$pos++;
63 1680 100       2630 if ($$pos > length($self->{QUERY}))
64             {
65 20         28 $$pos = length($self->{QUERY});
66 20         35 return 0;
67             }
68 1660         1230 @toks = grep{ $_ eq substr($self->{QUERY},$$pos,1)} @{$self->{TOKENS}};
  21580         28601  
  1660         1920  
69             }
70              
71 1314         2086 return $toks[0];
72             }
73              
74              
75             sub getNextIdentifier
76             {
77 350     350 0 358 my $self = shift;
78 350         345 my $pos = shift;
79 350         328 my $sp = $$pos;
80 350         505 $self->getNextToken($pos);
81 350         1349 return substr($self->{QUERY},$sp,$$pos-$sp);
82             }
83              
84              
85             sub getOp
86             {
87 556     556 0 562 my $self = shift;
88 556         526 my $pos = shift;
89 556         531 my $in_context = shift;
90 556 100       1097 $in_context = 0 unless defined($in_context);
91              
92 556         425 my $ret_op;
93              
94 556         494 my $loop = 1;
95 556         889 while( $loop )
96             {
97 892         829 my $pos_start = $$pos;
98              
99 892         1375 my $token = $self->getNextToken($pos);
100 892 50 66     1799 if (($token eq "0") && $in_context)
101             {
102 0         0 return;
103             }
104              
105 892         1060 my $token_start = ++$$pos;
106 892         702 my $ident;
107            
108 892 50       1191 if (defined($token))
109             {
110              
111 892 100 66     4710 if ($pos_start != ($token_start-1))
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    50          
112             {
113 61         50 $$pos = $pos_start;
114 61         112 my $temp_ident = $self->getNextIdentifier($pos);
115 61         208 $ret_op = XML::Stream::XPath::NodeOp->new($temp_ident,"0");
116             }
117             elsif ($token eq "/")
118             {
119 49 100       91 if (substr($self->{QUERY},$token_start,1) eq "/")
120             {
121 14         19 $$pos++;
122 14         28 my $temp_ident = $self->getNextIdentifier($pos);
123 14         49 $ret_op = XML::Stream::XPath::AllOp->new($temp_ident);
124             }
125             else
126             {
127 35         65 my $temp_ident = $self->getNextIdentifier($pos);
128 35 100       129 if ($temp_ident ne "")
129             {
130 29 50       175 $ret_op = XML::Stream::XPath::NodeOp->new($temp_ident,($pos_start == 0 ? "1" : "0"));
131             }
132             }
133             }
134             elsif ($token eq "\@")
135             {
136 164         371 $ret_op = XML::Stream::XPath::AttributeOp->new($self->getNextIdentifier($pos));
137             }
138             elsif ($token eq "]")
139             {
140 92 50       184 if ($in_context eq "[")
141             {
142 92         93 $ret_op = pop(@{$self->{OPS}});
  92         163  
143 92         133 $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         352 $$pos = index($self->{QUERY},$token,$token_start);
154 156         831 $ret_op = XML::Stream::XPath::Op->new("LITERAL",substr($self->{QUERY},$token_start,$$pos-$token_start));
155 156         267 $$pos++;
156             }
157             elsif ($token eq " ")
158             {
159 76         157 $ident = $self->getNextIdentifier($pos);
160 76 100       318 if ($ident eq "and")
    100          
161             {
162 4         5 $$pos++;
163 4         22 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         9 $ret_op = XML::Stream::XPath::AndOp->new($self->{OPS}->[$#{$self->{OPS}}],$tmp_op);
  4         21  
170 4         7 $in_context = 0;
171 4         5 pop(@{$self->{OPS}});
  4         6  
172             }
173             elsif ($ident eq "or")
174             {
175 68         101 $$pos++;
176 68         154 my $tmp_op = $self->getOp($pos,$in_context);
177 68 50       156 if (!defined($tmp_op))
178             {
179 0         0 confess("Invalid 'or' operation");
180 0         0 return;
181             }
182 68         125 $ret_op = XML::Stream::XPath::OrOp->new($self->{OPS}->[$#{$self->{OPS}}],$tmp_op);
  68         363  
183 68         117 $in_context = 0;
184 68         73 pop(@{$self->{OPS}});
  68         125  
185             }
186             }
187             elsif ($token eq "[")
188             {
189 92 50       215 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         164 $$pos = $pos_start + 1;
213 92         253 $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         41 my $op = pop(@{$self->{OPS}});
  23         50  
223 23 50       97 if ($op->getType() ne "NODE")
224             {
225 0         0 confess("No function name specified.");
226             }
227 23         80 my $function = $op->getValue();
228 23 50       76 if (!exists($XML::Stream::XPath::FUNCTIONS{$function}))
229             {
230 0         0 confess("Undefined function \"$function\"");
231             }
232 23         96 $ret_op = XML::Stream::XPath::FunctionOp->new($function);
233              
234 23         31 my $op_pos = $#{$self->{OPS}} + 1;
  23         47  
235              
236 23         104 $self->getOp($pos,$token);
237            
238 23         29 foreach my $arg ($op_pos..$#{$self->{OPS}})
  23         72  
239             {
240 6         25 $ret_op->addArg($self->{OPS}->[$arg]);
241             }
242              
243 23         32 splice(@{$self->{OPS}},$op_pos);
  23         64  
244            
245             }
246             elsif ($token eq ")")
247             {
248 23 50       73 if ($in_context eq "(")
249             {
250 23         39 $ret_op = undef;
251 23         42 $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       6 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         175 my $tmp_op;
269 152         299 while(!defined($tmp_op))
270             {
271 152         443 $tmp_op = $self->getOp($pos);
272             }
273 152         274 $ret_op = XML::Stream::XPath::EqualOp->new($self->{OPS}->[$#{$self->{OPS}}],$tmp_op);
  152         650  
274 152         169 pop(@{$self->{OPS}});
  152         389  
275             }
276             elsif ($token eq "!")
277             {
278 2 50       8 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         6 while(!defined($tmp_op))
286             {
287 4         7 $tmp_op = $self->getOp($pos);
288             }
289 2         3 $ret_op = XML::Stream::XPath::NotEqualOp->new($self->{OPS}->[$#{$self->{OPS}}],$tmp_op);
  2         12  
290 2         2 pop(@{$self->{OPS}});
  2         3  
291             }
292             else
293             {
294 0         0 confess("Unhandled \"$token\"");
295             }
296              
297 892 100       1641 if ($in_context)
298             {
299 336 100       600 if (defined($ret_op))
300             {
301 332         282 push(@{$self->{OPS}},$ret_op);
  332         595  
302             }
303 336         379 $ret_op = undef;
304             }
305             }
306             else
307             {
308 0         0 confess("Token undefined");
309             }
310            
311 892 100       2344 $loop = 0 unless $in_context;
312             }
313              
314 556         1207 return $ret_op;
315             }
316              
317              
318             sub parseQuery
319             {
320 123     123 0 147 my $self = shift;
321 123         151 my $query = shift;
322              
323 123         113 my $op;
324 123         146 my $pos = 0;
325 123         356 while($pos < length($self->{QUERY}))
326             {
327 213         434 $op = $self->getOp(\$pos);
328 213 100       466 if (defined($op))
329             {
330 207         199 push(@{$self->{OPS}},$op);
  207         852  
331             }
332             }
333              
334             #foreach my $op (@{$self->{OPS}})
335             #{
336             # $op->display();
337             #}
338              
339 123         162 return 1;
340             }
341              
342              
343             sub execute
344             {
345 123     123 0 169 my $self = shift;
346 123         135 my $root = shift;
347              
348 123         547 my $ctxt = XML::Stream::XPath::Value->new($root);
349              
350 123         143 foreach my $op (@{$self->{OPS}})
  123         286  
351             {
352 192 100       583 if (!$op->isValid(\$ctxt))
353             {
354 75         199 $ctxt->setValid(0);
355 75         189 return $ctxt;
356             }
357             }
358              
359 48         122 $ctxt->setValid(1);
360 48         122 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