File Coverage

blib/lib/XML/Node.pm
Criterion Covered Total %
statement 4 6 66.6
branch n/a
condition n/a
subroutine 2 2 100.0
pod n/a
total 6 8 75.0


line stmt bran cond sub pod time code
1             # Copyright (c) 1999 Chang Liu
2             # All rights reserved.
3             #
4             # This program is free software; you can redistribute it and/or modify
5             # it under the same terms as Perl itself.
6            
7            
8             package XML::Node;
9            
10             #use strict;
11             #use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $VERSION);
12            
13             =head1 NAME
14            
15             XML::Node - Node-based XML parsing: an simplified interface to XML::Parser
16            
17             =head1 SYNOPSIS
18            
19             use XML::Node;
20            
21             $xml_node = new XML::Node;
22             $xml_node->register( $nodetype, $callback_type => \&callback_function );
23             $xml_node->register( $nodetype, $callback_type => \$variable );
24            
25             open(FOO, 'xmlgenerator |');
26             $p3->parse(*FOO);
27             close(FOO);
28            
29             $xml_node->parsefile( $xml_filename );
30            
31             =head1 DESCRIPTION
32            
33             If you are only interested in processing certain nodes in an XML file, this
34             module can help you simplify your Perl scripts significantly.
35            
36             The XML::Node module allows you to register callback functions or variables for
37             any XML node. If you register a call back function, it will be called when
38             the node of the type you specified are encountered. If you register a variable,
39             the content of a XML node will be appended to that variable automatically.
40            
41             Subroutine register accepts both absolute and relative node registrations.
42            
43             Here is an example of absolute path registration:
44            
45             1. register(">TestCase>Name", "start" => \&handle_TestCase_Name_start);
46            
47             Here are examples of single node name registration:
48            
49             2. register( "Name", "start" => \&handle_Name_start);
50             3. register( "Name", "end" => \&handle_Name_end);
51             4. register( "Name", "char" => \&handle_Name_char);
52            
53             Here is an example of attribute registration:
54            
55             5. register(">TestCase:Author", "attr" => \$testcase_author);
56            
57             Abosolute path trigger condition is recommended because a "Name" tage could appear in different
58             places and stands for differe names.
59            
60             Example:
61            
62             1
63             2 Something
64             3
65             4 Something
66             5
67             6
68            
69             Statement 1 causes &handle_TestCase_Name_start to be called when parsing Line 2. Statements 2,3,4 cause the three handler subroutines to be called when parsing both Line 2 and Line 4.
70            
71             This module uses XML::Parser.
72            
73             =head1 EXAMPLE
74            
75             Examples "test.pl" and "parse_orders.pl" come with this perl module.
76            
77             =head1 SEE ALSO
78            
79             XML::Parser
80            
81             =head1 NOTE
82            
83             When you register a variable, XML::Node appends strings found to that variable. So please be sure to clear that variable before it is used again.
84            
85             =head1 AUTHORS
86            
87             Chang Liu
88            
89             =head1 LAST MODIFIED
90            
91             $Date: 2001/12/10 11:38:28 $
92            
93             =cut
94            
95            
96 1     1   749 use Exporter;
  1         2  
  1         85  
97             $VERSION = "0.11";
98             @ISA = ('Exporter');
99             @EXPORT = qw (®ister &parse &parsefile);
100            
101            
102 1     1   1468 use XML::Parser;
  0            
  0            
103             use Carp;
104            
105            
106             if ($ENV{DEBUG}) {
107             print "DEBUG:XML::Node.pm VERSION $VERSION\n";
108             }
109            
110             my $instance = 0;
111             my @selves = ();
112             my $myinstance;
113            
114             sub new{
115             my $class = shift;
116            
117             my $self = {
118             INSTANCE => $instance,
119             START_HANDLERS => {},
120             END_HANDLERS => {},
121             CHAR_HANDLERS => {},
122             ATTR_HANDLERS => {},
123             CURRENT_TAG => "",
124             CURRENT_PATH => "",
125             };
126             bless $self, $class;
127             $selves[$instance++] = $self;
128             return $self;
129             }
130            
131             sub register
132             {
133             $self = shift or croak "XML::Node --self is expected as THE first parameter \®ister.\n";
134             my $node = shift or croak "XML::Node --a node path is expected as arg1 in \®ister.\n";
135             my $type = shift or croak "XML::Node --node type is expected as arg2 in \®ister.\n";
136             my $handler = shift or croak "XML::Node --a handler is expected as arg3 in \®ister.\n";
137             if ($type eq "start") {
138             $self->{START_HANDLERS}->{$node} = $handler;
139             } elsif ($type eq "end") {
140             $self->{END_HANDLERS}->{$node} = $handler;
141             } elsif ($type eq "char") {
142             $self->{CHAR_HANDLERS}->{$node} = $handler;
143             } elsif ($type eq "attr") {
144             $self->{ATTR_HANDLERS}->{$node} = $handler;
145             } else {
146             croak "XML::Node --unknown handler type $type for node $node\n";
147             }
148             }
149            
150            
151             sub parsefile
152             {
153             $self = shift or croak "XML::Node --self is expected as THE first parameter \®ister.\n";
154             my $xml_file = shift or croak "XML::Node --an XML filename is expected in \&parse.\n";
155            
156             $myinstance = $self->{INSTANCE};
157             carp "XML::Node - invoking parser [$myinstance]" if $ENV{DEBUG};
158            
159             my $my_handlers = qq {
160             sub handle_start_$myinstance
161             {
162             &handle_start($myinstance, \@_);
163             }
164             sub handle_end_$myinstance
165             {
166             &handle_end($myinstance, \@_);
167             }
168             sub handle_char_$myinstance
169             {
170             &handle_char($myinstance, \@_);
171             }
172             \$XML::Node::parser = new XML::Parser(Handlers => { Start => \\& handle_start_$myinstance,
173             End => \\& handle_end_$myinstance,
174             Char => \\& handle_char_$myinstance } );
175            
176             };
177             #carp "[[[[[[[[[[[[[[[[$my_handlers]]]]]]]]]]]]]]";
178             eval ($my_handlers);
179             $parser->parsefile("$xml_file");
180             }
181            
182             sub parse
183             {
184             $self = shift or croak "XML::Node --self is expected as THE first parameter \®ister.\n";
185            
186             $myinstance = $self->{INSTANCE};
187             carp "XML::Node - invoking parser [$myinstance]" if $ENV{DEBUG};
188            
189             my $my_handlers = qq {
190             sub handle_start_$myinstance
191             {
192             &handle_start($myinstance, \@_);
193             }
194             sub handle_end_$myinstance
195             {
196             &handle_end($myinstance, \@_);
197             }
198             sub handle_char_$myinstance
199             {
200             &handle_char($myinstance, \@_);
201             }
202             \$XML::Node::parser = new XML::Parser(Handlers => { Start => \\& handle_start_$myinstance,
203             End => \\& handle_end_$myinstance,
204             Char => \\& handle_char_$myinstance } );
205            
206             };
207             #carp "[[[[[[[[[[[[[[[[$my_handlers]]]]]]]]]]]]]]";
208             eval ($my_handlers);
209             $parser->parse(shift);
210             }
211            
212             sub handle_start
213             {
214             my $myinstance = shift;
215             my $p = shift;
216             my $element = shift;
217            
218            
219             my $current_path = $selves[$myinstance]->{CURRENT_PATH} =
220             $selves[$myinstance]->{CURRENT_PATH} . ">" . $element;
221             my $current_tag = $selves[$myinstance]->{CURRENT_TAG} = $element;
222            
223             my $attr;
224             my $value;
225            
226             # carp("handle_start called [$myinstance] [$element] [$current_path]\n");
227            
228             while (defined ($attr = shift ) ) {
229             if (! defined ($value = shift)) {
230             croak ("value for attribute [$attr] of element [$element] is not returned by XML::Parser\n");
231             }
232             # carp("Attribute [$attr] of element [$element] found with value [$value] attr_path:[$attr_path]\n");
233             my @array = split(/>/, $current_path);
234             my $current_relative_path = "$current_tag:$attr";
235             my $i;
236             if ($selves[$myinstance]->{ATTR_HANDLERS}->{$current_relative_path}) {
237             handle($p, $value, $selves[$myinstance]->{ATTR_HANDLERS}->{$current_relative_path});
238             }
239             for ($i=$#array-1;$i>=1;$i--)
240             { # call all relative paths
241             $current_relative_path = $array[$i] . ">" . $current_relative_path;
242             if ($selves[$myinstance]->{ATTR_HANDLERS}->{$current_relative_path}) {
243             handle($p, $value, $selves[$myinstance]->{ATTR_HANDLERS}->{$current_relative_path});
244             }
245             }
246             my $attr_path = "$current_path:$attr";
247             if ($selves[$myinstance]->{ATTR_HANDLERS}->{$attr_path}) {
248             handle($p, $value, $selves[$myinstance]->{ATTR_HANDLERS}->{$attr_path});
249             }
250             }
251            
252             my @array = split(/>/, $current_path);
253             my $current_relative_path = $current_tag;
254             my $i;
255            
256             if ($selves[$myinstance]->{START_HANDLERS}->{$current_tag}) {
257             handle($p, $element, $selves[$myinstance]->{START_HANDLERS}->{$current_tag});
258             }
259             #carp("--Begin loop\n");
260             for ($i=$#array-1;$i>=1;$i--)
261             { # call all relative paths
262             $current_relative_path = $array[$i] . ">" . $current_relative_path;
263             #carp("Array size is $#array, \$i is $i, current_relative_path is $current_relative_path\n");
264             if ($selves[$myinstance]->{START_HANDLERS}->{$current_relative_path}) {
265             handle($p, $element, $selves[$myinstance]->{START_HANDLERS}->{$current_relative_path});
266             }
267             }
268             #carp("--End loop\n");
269             if ($selves[$myinstance]->{START_HANDLERS}->{$current_path}) {
270             handle($p, $element, $selves[$myinstance]->{START_HANDLERS}->{$current_path});
271             }
272             }
273            
274             sub handle_end
275             {
276             my $myinstance = shift;
277             my $p = shift;
278             my $element = shift;
279             my $current_path = $selves[$myinstance]->{CURRENT_PATH};
280            
281             # carp("handle_end called [$myinstance] [$element]\n");
282            
283             $selves[$myinstance]->{CURRENT_TAG} = $element;
284            
285             my @array = split(/>/, $current_path);
286             my $current_relative_path = $element;
287             my $i;
288            
289             if ($selves[$myinstance]->{END_HANDLERS}->{$selves[$myinstance]->{CURRENT_TAG}}) {
290             handle($p, $element, $selves[$myinstance]->{END_HANDLERS}->{$selves[$myinstance]->{CURRENT_TAG}});
291             }
292             for ($i=$#array-1;$i>=1;$i--)
293             { # call all relative paths
294             $current_relative_path = $array[$i] . ">" . $current_relative_path;
295             #carp("Array size is $#array, \$i is $i, current_relative_path is $current_relative_path\n");
296             if ($selves[$myinstance]->{END_HANDLERS}->{$current_relative_path}) {
297             handle($p, $element, $selves[$myinstance]->{END_HANDLERS}->{$current_relative_path});
298             }
299             }
300             if ($selves[$myinstance]->{END_HANDLERS}->{$selves[$myinstance]->{CURRENT_PATH}}) {
301             handle($p, $element, $selves[$myinstance]->{END_HANDLERS}->{$selves[$myinstance]->{CURRENT_PATH}});
302             }
303            
304             $selves[$myinstance]->{CURRENT_PATH} =~ /(.*)>/;
305             $selves[$myinstance]->{CURRENT_PATH} = $1;
306             $selves[$myinstance]->{CURRENT_TAG} = $';
307             if ($element ne $selves[$myinstance]->{CURRENT_TAG}) {
308             carp "start-tag <$selves[$myinstance]->{CURRENT_TAG}> doesn't match end-tag <$element>. Is this XML file well-formed?\n";
309             }
310             $selves[$myinstance]->{CURRENT_PATH} =~ /(.*)>/;
311             $selves[$myinstance]->{CURRENT_TAG} = $';
312             }
313            
314             sub handle_char
315             {
316             my $myinstance = shift;
317             my $p = shift;
318             my $element = shift;
319             my $current_path = $selves[$myinstance]->{CURRENT_PATH};
320            
321             # carp("handle_char called [$myinstance] [$element]\n");
322            
323             my @array = split(/>/, $current_path);
324             my $current_relative_path = $element;
325             my $i;
326            
327             if ($selves[$myinstance]->{CHAR_HANDLERS}->{$selves[$myinstance]->{CURRENT_TAG}}) {
328             handle($p, $element, $selves[$myinstance]->{CHAR_HANDLERS}->{$selves[$myinstance]->{CURRENT_TAG}});
329             }
330             for ($i=$#array-1;$i>=1;$i--)
331             { # call all relative paths
332             $current_relative_path = $array[$i] . ">" . $current_relative_path;
333             if ($selves[$myinstance]->{CHAR_HANDLERS}->{$current_relative_path}) {
334             handle($p, $element, $selves[$myinstance]->{CHAR_HANDLERS}->{$current_relative_path});
335             }
336             }
337             if ($selves[$myinstance]->{CHAR_HANDLERS}->{$selves[$myinstance]->{CURRENT_PATH}}) {
338             handle($p, $element, $selves[$myinstance]->{CHAR_HANDLERS}->{$selves[$myinstance]->{CURRENT_PATH}});
339             }
340             }
341            
342             sub handle
343             {
344             my $p = shift;
345             my $element = shift;
346             my $handler = shift;
347            
348             my $handler_type = ref($handler);
349             if ($handler_type eq "CODE") {
350             &$handler($p,$element); # call the handler function
351             } elsif ($handler_type eq "SCALAR") {
352             # chomp($element);
353             # $element =~ /^(\s*)/;
354             # $element = $';
355             # $element =~ /(\s*)$/;
356             # $element = $`;
357             if (! defined $$handler) {
358             $$handler = "";
359             #carp ("XML::Node - SCALAR handler undefined when processing [$element]");
360             }
361             $$handler = $$handler . $element; #append the content to the handler variable
362             } else {
363             carp "XML::Node -unknown handler type [$handler_type]\n";
364             exit;
365             }
366             }
367            
368            
369             1;