File Coverage

blib/lib/CGI/XMLForm.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             package CGI::XMLForm;
2              
3 1     1   785 use strict;
  1         2  
  1         42  
4 1     1   6 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
  1         2  
  1         89  
5              
6 1     1   9774 use CGI;
  1         26147  
  1         6  
7 1     1   595 use CGI::XMLForm::Path;
  1         3  
  1         58  
8 1     1   2174 use XML::Parser;
  0            
  0            
9              
10             @ISA = qw(CGI);
11             # Items to export into callers namespace by default. Note: do not export
12             # names by default without a very good reason. Use EXPORT_OK instead.
13             # Do not simply export all your public functions/methods/constants.
14             @EXPORT = qw(
15              
16             );
17             $VERSION = '0.10';
18              
19             sub new {
20             my $proto = shift;
21             my $class = ref($proto) || $proto;
22             my $self = $class->SUPER::new(@_);
23             bless ($self, $class); # reconsecrate
24             return $self;
25             }
26              
27             sub readXML {
28             my $self = shift;
29             my $xml = shift;
30              
31             my @queries = @_;
32              
33             my @Requests;
34              
35             my $req = new CGI::XMLForm::Path();
36             do {
37             $req = new CGI::XMLForm::Path(shift @queries, $req);
38             push @Requests, $req;
39             } while @queries;
40              
41             my $currenttree = new CGI::XMLForm::Path();
42              
43             my $p = new XML::Parser(Style => 'Stream',
44             _parseresults => [],
45             _currenttree => $currenttree,
46             _requests => \@Requests,
47             );
48              
49             my $results;
50             eval {
51             $results = $p->parse($xml);
52             # warn "Parse returned ", @{$results}, "\n";
53             };
54             if ($@) {
55             return $@;
56             }
57             else {
58             return @{$results};
59             }
60             }
61              
62             sub StartTag {
63             my $expat = shift;
64             return $expat->finish() if $expat->{_done};
65             my $element = shift;
66             # my %attribs = %_;
67              
68             #warn "Start: $element\n";
69             $expat->{_currenttree}->Append($element, %_);
70             my $current = $expat->{_currenttree};
71              
72             #warn "Path now: ", $expat->{_currenttree}->Path, "\n";
73              
74             foreach (0..$#{$expat->{_requests}}) {
75             next unless defined $expat->{_requests}->[$_]->Attrib;
76             # warn "Looking for attrib: ", $expat->{_requests}->[$_]->Attrib, "\n";
77             if (defined $_{$expat->{_requests}->[$_]->Attrib}) {
78             # Looking for attrib
79             if ($expat->{_requests}->[$_]->isEqual($current)) {
80             # We have equality!
81             found($expat, $expat->{_requests}->[$_], $_{$expat->{_requests}->[$_]->Attrib});
82             splice(@{$expat->{_requests}}, $_, 1) unless $expat->{_requests}->[$_]->isRepeat;
83             $expat->{_done} = 1 if (@{$expat->{_requests}} == 0);
84             return;
85             }
86             }
87             }
88             }
89              
90             sub EndTag {
91             my $expat = shift;
92             return $expat->finish() if $expat->{_done};
93             # warn "End: $_\n";
94              
95             $expat->{_currenttree}->Pop();
96             }
97              
98             sub Text {
99             my $expat = shift;
100             my $text = $_;
101              
102             return $expat->finish() if $expat->{_done};
103              
104             my @Requests = @{$expat->{_requests}};
105             my $current = $expat->{_currenttree};
106              
107             foreach (0..$#Requests) {
108             if (!$Requests[$_]->Attrib) {
109             # Not looking for an attrib
110             # warn "Comparing : ", $Requests[$_]->Path, " : ", $expat->{_currenttree}->Path, "\n";
111             if ($Requests[$_]->isEqual($current)) {
112             found($expat, $Requests[$_], $text);
113             splice(@{$expat->{_requests}}, $_, 1) unless $Requests[$_]->isRepeat;
114             $expat->{_done} = 1 if (@Requests == 0);
115             return;
116             }
117             }
118             }
119             }
120              
121             sub found {
122             my $expat = shift;
123             my ($request, $found) = @_;
124              
125             #warn "Found: ", $request->Path, " : $found\n";
126              
127             if ($request->Path =~ /\.\*/) {
128             # Request path contains a regexp
129             my $match = $request->Path;
130             $match =~ s/\[(.*?)\]/\\\[$1\\\]/g;
131              
132             # warn "Regexp: ", $expat->{_currenttree}->Path, " =~ |$match|\n";
133             $expat->{_currenttree}->Path =~ /$match/;
134             push @{$expat->{_parseresults}}, $&, $found;
135             }
136             else {
137             push @{$expat->{_parseresults}}, $request->Path, $found;
138             }
139              
140             }
141              
142             sub EndDocument {
143             my $expat = shift;
144             delete $expat->{_done};
145             delete $expat->{_currenttree};
146             delete $expat->{_requests};
147             return $expat->{_parseresults};
148             }
149              
150             sub formatElement($$) {
151             # Properly formats elements whether opening or closing.
152              
153             my $cgi = shift;
154             my $open = shift;
155             my $element = shift;
156             my $level = shift;
157              
158             $element =~ s/&slash;/\//g;
159              
160             $element =~ /^(.*?)(\[(.*)\])?$/;
161             my $output = $1;
162             my $attribs = $3 || "";
163              
164             if (!$open) {
165             if (!$cgi->{'.closetags'}) {
166             $cgi->{'.closetags'} = $level;
167             return "\n";
168             }
169             else {
170             return ("\t" x --$cgi->{'.closetags'}) . "\n";
171             }
172             }
173              
174             # If we have attributes
175             while ($attribs =~ /\@(\w+?)=([\"\'])(.*?)\2(\s+and\s+)?/g) {
176             $output .= " $1=\"$3\"";
177             }
178             my $save = $cgi->{'.closetags'};
179             $cgi->{'.closetags'} = 0;
180             return ($save ? '' : "\n") . ("\t" x $level) . "<$output>";
181             }
182              
183             sub ToXML {
184             shift()->toXML(@_);
185             }
186              
187             sub toXML {
188             my $self = shift;
189             my $filename = shift;
190              
191             if (defined $filename) {
192             local *OUTPUT;
193             open(OUTPUT, ">$filename") or die "Can't open $filename for output: $!";
194             print OUTPUT $self->{".xml"};
195             close OUTPUT;
196             }
197              
198             defined wantarray && return $self->{".xml"};
199             }
200              
201             sub parse_params {
202             my($self,$tosplit) = @_;
203             my(@pairs) = split('&',$tosplit);
204             my($param,$value);
205             my $output = "";
206              
207             my @prevStack;
208             my @stack;
209             my @rawParams;
210             my $relative;
211             $self->{'.closetags'} = 0;
212              
213             foreach (@pairs) {
214             ($param,$value) = split('=',$_,2);
215             $param = $self->unescape($param);
216             $value = $self->unescape($value);
217              
218             $self->add_parameter($param);
219             push (@{$self->{$param}},$value);
220              
221             next if $param =~ /^xmlcgi:ignore/;
222             next if $param =~ /^\.\w/; # Skip CGI.pm ".submit" and other buttons
223              
224             push @rawParams, $param, $value;
225              
226             # Encode values
227             $value =~ s/&/&/g;
228             $value =~ s/
229             $value =~ s/>/>/g;
230             $value =~ s/'/'/g;
231             $value =~ s/"/"/g;
232              
233             $value =~ s/\//\&slash;/g; # We decode this later...
234             $param =~ s/\[(.*?)\/(.*?)\]/\[$1\&slash;$2\]/g;
235              
236             # Here we make the attribute into an internal attrib
237             # so that tree compares work properly
238             my $attrib = 0;
239             if($param =~ s/(\])?\/(\@\w+)$/(($1 && " and ")||"[").qq($2="$value"])/e) {
240             $attrib = 1;
241             }
242              
243             # Do work here
244             if ($param =~ s/^\///) {
245             # If starts with a slash it's a root element
246             @stack = split /\//, $param;
247             $relative = 0;
248             }
249             else {
250             # Otherwise it's a relative path
251              
252             # - We don't need to do this, but it's here commented out
253             # to show what we're implying.
254             # @stack = @prevStack;
255              
256              
257             # We don't want the last element if the previous param
258             # was also a relative param.
259             my $top = pop @stack if ($relative);
260              
261             foreach ( split(/\//, $param)) {
262             if ($_ eq "..") {
263             if ($top) {
264             $output .= $self->formatElement(0, $top, scalar @stack);
265             $top = '';
266             pop @prevStack;
267             }
268             $output .= $self->formatElement(0, pop(@stack), scalar @stack);
269             pop @prevStack;
270             }
271             else {
272             push @stack, $_;
273             }
274             }
275             $relative++;
276             }
277              
278             # print STDERR "Prev Stack: ", join(", ", @prevStack), "\n";
279             # print STDERR "New Stack: ", join(", ", @stack), "\n----------\n";
280              
281             foreach my $i (0..$#stack) {
282              
283             if (defined $prevStack[$i]) {
284              
285             # We've travelled along this branch of the tree before.
286             if (($i == $#stack) || ($prevStack[$i] ne $stack[$i])) {
287              
288             # If we've reached the end of the branch, or the branch has changed...
289             while ($i <= $#prevStack) {
290             # Close the previous branch
291             $output .= $self->formatElement(0, pop(@prevStack),
292             scalar @prevStack);
293             }
294              
295             # And add this new branch
296             $output .= $self->formatElement(1, $stack[$i], scalar
297             @prevStack);
298             push @prevStack, $stack[$i];
299             }
300             }
301              
302             else {
303             # here we're traversing out into the tree where we've not travelled before.
304             $output .= $self->formatElement(1, $stack[$i], scalar @prevStack);
305             push @prevStack, $stack[$i];
306             }
307             }
308              
309             # Finally, we output the contents of the form field, unless it's an attribute form field
310             if (!$attrib) {
311             $output .= $value;
312             }
313              
314             # Store the previous stack.
315             @prevStack = @stack;
316             }
317              
318             # Finish by completely popping the stack off.
319             while (@prevStack) {
320             $output .= $self->formatElement(0, pop(@prevStack), scalar @prevStack);
321             }
322              
323             $self->{".xml"} = $output;
324             $self->{rawParams} = \@rawParams;
325              
326             1;
327             }
328              
329             1;
330             __END__