File Coverage

blib/lib/XML/Parser/Nodes.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 XML::Parser::Nodes ;
2              
3 1     1   29552 use 5.008009 ;
  1         4  
  1         39  
4 1     1   6 use strict ;
  1         2  
  1         36  
5 1     1   6 use warnings ;
  1         121  
  1         47  
6 1     1   6 use Carp ;
  1         2  
  1         102  
7              
8 1     1   705 use XML::Parser ;
  0            
  0            
9             use XML::Dumper ;
10             use XML::Parser::Style::Tree ;
11              
12             require Exporter ;
13              
14             our @ISA = qw( Exporter XML::Parser::Style::Tree ) ;
15              
16             # Items to export into callers namespace by default. Note: do not export
17             # names by default without a very good reason. Use EXPORT_OK instead.
18             # Do not simply export all your public functions/methods/constants.
19              
20             # This allows declaration use XML::Parser::Nodes ':all';
21             # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
22             # will save memory.
23              
24             our %EXPORT_TAGS = ( 'all' => [ qw( ) ] ) ;
25              
26             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ) ;
27              
28             our @EXPORT = qw( ) ;
29              
30             our $VERSION = '0.08' ;
31              
32             $XML::Parser::Built_In_Styles{Nodes} = 1;
33              
34             sub Init {
35             return XML::Parser::Style::Tree::Init( @_ ) ;
36             } ;
37              
38             sub Start {
39             return XML::Parser::Style::Tree::Start( @_ ) ;
40             } ;
41              
42             sub End {
43             return XML::Parser::Style::Tree::End( @_ ) ;
44             } ;
45              
46             sub Char {
47             return XML::Parser::Style::Tree::Char( @_ ) ;
48             } ;
49              
50             sub Final {
51             my $tree = XML::Parser::Style::Tree::Final( @_ ) ;
52             return XML::Parser::Nodes->conform( $tree ) ;
53             } ;
54              
55             # Preloaded methods go here.
56              
57             sub new {
58             my $self = shift ;
59             my $xmlarg = shift ;
60             return length $xmlarg < 255 && $xmlarg !~ /^
61             $self->parsefile( $xmlarg ):
62             $self->parse( $xmlarg ) ;
63             }
64              
65             ################################################################################
66             ##
67             ## In XML::Parser's output, a node is an Array whose first element is a
68             ## Hash of NVP's that represent that tag's attributes. Subsequent
69             ## elements alternate between text and child nodes.
70             ##
71             ## conform() prepends an empty hash to the output structure to ensure it
72             ## conforms to this node definition.
73             ##
74             ################################################################################
75              
76             sub conform {
77             my $package = shift ;
78             my $parsed = shift ;
79             $package = ref $package if ref $package ;
80             unshift @$parsed, {} ;
81             return bless $parsed, $package ;
82             }
83              
84             sub parse {
85             my $self = shift ;
86             my $xmlbuff = shift ;
87              
88             my $package = ref $self? ref $self: $self ;
89             my $parser = ref $self? $self:
90             XML::Parser->new( Style => 'Tree' ) ;
91             return $package->conform( XML::Parser::parse(
92             $parser, $xmlbuff ) ) ;
93             }
94              
95             sub parsefile {
96             my $self = shift ;
97             my $xmlfn = shift ;
98              
99             my $package = ref $self? ref $self: $self ;
100             return ref $self? XML::Parser::parsefile( $self, $xmlfn ):
101             $package->conform(
102             XML::Parser->new( Style => 'Tree' )->parsefile( $xmlfn )
103             ) ;
104             }
105              
106             sub byfile {
107             return parsefile( @_ ) ;
108             }
109              
110             sub readfile {
111             return parsefile( @_ ) ;
112             }
113              
114             sub childlist {
115             my $self = shift ;
116             my $i = 0 ;
117             my @a = () ;
118             push @a, [ $self->[ $i ], $self->[ ++$i ] ] while $i++ < $#$self ;
119             return map { $_->[0] } grep ref $_->[1], @a ;
120             }
121              
122             sub taglist {
123             return childlist( @_ ) ;
124             }
125              
126             sub tree {
127             my $self = shift ;
128             my $tag = '' ;
129             my @rv = () ;
130              
131             if ( @_ ) {
132             push @rv, shift @_ ;
133             $tag = $rv[0] .'/' ;
134             }
135              
136             my $i = 0 ;
137             my @a = () ;
138             push @a, [ $self->[ $i ], $self->[ ++$i ] ] while $i++ < $#$self ;
139             push @rv, map { tree( $_->[1], $tag .$_->[0] ) } grep ref $_->[1], @a ;
140             return @rv ;
141             }
142              
143             sub childnodes {
144             my $self = shift ;
145              
146             my $i = 0 ;
147             my @a = () ;
148             push @a, [ $self->[ $i ], $self->[ ++$i ] ] while $i++ < $#$self ;
149             my @aa = grep ref $_->[1], @a ;
150             map { bless $_->[1], ref $self } @aa ;
151             return @aa ;
152             }
153              
154             sub getkids {
155             return childnodes( @_ ) ;
156             }
157              
158             sub childnode {
159             my $self = shift ;
160             return $self->childnodes unless @_ ;
161              
162             my $key = shift ;
163              
164             my $i = 0 ;
165             my @a = () ;
166             push @a, [ $self->[ $i ], $self->[ ++$i ] ] while $i++ < $#$self ;
167              
168             my @aa = map { bless $_->[1], ref $self } grep $_->[0] eq $key, @a ;
169             return $aa[0] || bless( [], ref $self ) unless wantarray ;
170             return @aa ;
171             }
172              
173             sub getdata {
174             return childnode( @_ ) ;
175             }
176              
177             sub nodebykey {
178             my $self = shift ;
179             my $key = shift ;
180             my @key = split( m|/|, $key, 2 ) ;
181             my $next = $self->childnode( $key[0] ) ;
182              
183             return $next if @key == 1 ;
184             return $next->nodebykey( $key[1] ) ;
185             }
186              
187             sub recordbykey {
188             return nodebykey( @_ ) ;
189             }
190              
191             sub getattributes {
192             my $self = shift ;
193             return $self->[0] ;
194             }
195              
196             sub gettext {
197             my $self = shift ;
198              
199             my $i = 0 ;
200             my @a = () ;
201             push @a, [ $self->[ $i ], $self->[ ++$i ] ] while $i++ < $#$self ;
202              
203             my @results = grep defined $_,
204             map { $_->[1] } grep $_->[0] eq '0', @a ;
205             return @results if wantarray && @results != 0 ;
206             return join '', @results ;
207             }
208              
209             sub cells {
210             my $self = shift ;
211             my @rv = map { $self->childnode( $_ )->gettext || '' } @_ ;
212             return \@rv ;
213             }
214              
215             sub wrapper {
216             my $self = shift ;
217             my $name = shift ;
218             return bless [ {}, $name, $self ], ref $self ;
219             }
220              
221             sub name {
222             return wrapper( @_ ) ;
223             }
224              
225             sub dump {
226             my $self = shift ;
227             return xmlout( @$self[ 1, 2 ] ) ."\n" ;
228             }
229              
230             sub xmlout {
231             my $name = shift ;
232             my $properties = shift ;
233             my $singletag = undef ; # optionally set true
234              
235             return retext( $properties ) if $name eq '0' ;
236              
237             my @properties = () ;
238             push @properties, @$properties ;
239             my $attribs = shift @properties ;
240             $attribs ||= {} ;
241             my $atstring .= join ' ', '', map { sprintf '%s="%s"',
242             $_, charfix( $attribs->{$_} ) } keys %$attribs ;
243              
244             return "<$name$atstring />" unless scalar @properties ;
245              
246             my $out = "<$name$atstring>" ;
247             $out .= xmlout( splice @properties, 0, 2 ) while @properties ;
248             return "$out" ;
249             }
250              
251             sub retext {
252             my $s = shift ;
253             return '' unless defined $s ;
254             $s =~ s/&/&/g ;
255             $s =~ s/>/>/g ;
256             $s =~ s/
257             return $s ;
258             }
259              
260             sub charfix {
261             my $value = shift ;
262             return '' unless defined $value ;
263             $value =~ s/&/&/g ;
264             $value =~ s/"/"/g ;
265             $value =~ s/'/'/g ;
266             return $value ;
267             }
268              
269             no warnings ;
270              
271             sub pl2xml {
272             my $o = pop ;
273             my $self = shift if @_ && ( ref $_[0] || $_[0] eq __PACKAGE__ ) ;
274             $self = ! $self? __PACKAGE__: ref $self? ref $self: $self ;
275             my $toplabel = shift ;
276             $toplabel ||= 'perldata' ;
277              
278             my $top = $self->newelement() ;
279             $top->addelement( "\n " ) ;
280             $top->addelement( nextpl2xml( bless( {}, $self ), $o, 2 ) ) ;
281             $top->addelement( "\n" ) ;
282              
283             my $out = $self->newelement() ;
284             $out->addelement( $toplabel => $top ) ;
285             return $out ;
286             }
287              
288             use warnings ;
289              
290             sub nextpl2xml {
291             my $self = shift;
292             my $ref = shift;
293             my $indent = shift;
294              
295             my $out = $self->newelement() ;
296              
297             if ( ref $ref ) {
298             local $_ = ref $ref ;
299             my $class = '' ;
300             my $address = '' ;
301            
302             if ( /^(?:SCALAR|HASH|ARRAY)$/ ) {
303             ( $_, $address) = overload::StrVal( $ref )
304             =~ /([^(]+)\(([x0-9A-Fa-f]+)\)/ ;
305             }
306             else {
307             $class = XML::Dumper::xml_escape( ref $ref );
308             ( $_, $address ) = overload::StrVal( $ref )
309             =~ /$class=([^(]+)\(([x0-9A-Fa-f]+)\)/ ;
310             }
311              
312             my $reused = $address && $self->{xml}{ $address }++ ;
313             my $indentstr = "\n" . " " x$indent ;
314              
315             $out->[0]->{blessed_package} = $class if $class ;
316             $out->[0]->{memory_address} = $address if $address ;
317              
318             if ( /^SCALAR$/ && ! $reused ) {
319             $out->[0]->{defined} = 'false' unless defined $$ref ;
320             $out->addelement( 0 => $$ref ) ;
321             }
322             elsif ( /^HASH$/ && ! $reused ) {
323             foreach my $k ( keys %$ref ) {
324             $out->addelement( $indentstr ) ;
325             $out->addelement( newitem( $self,
326             { key => $k },
327             $ref->{ $k },
328             $indent +1 ) ) ;
329             }
330              
331             $out->addelement( "\n" . " " x( $indent -1 ) ) ;
332             }
333             elsif ( /^ARRAY$/ && ! $reused ) {
334             for ( my $ct = 0 ; $ct < @$ref ; $ct++ ) {
335             $out->addelement( $indentstr ) ;
336             $out->addelement( newitem( $self,
337             { key => $ct },
338             $ref->[ $ct ],
339             $indent +1 ) ) ;
340             }
341              
342             $out->addelement( "\n" . " " x( $indent -1 ) ) ;
343             }
344              
345             my $key = /^SCALAR$/? 'scalarref':
346             /^HASH$/? 'hashref':
347             /^ARRAY$/? 'arrayref': '' ;
348              
349             return $key => $out ;
350             }
351             else {
352             $out->[0]->{defined} = 'false' unless defined $ref ;
353             $out->addelement( 0 => $ref ) ;
354             return ( scalar => $out ) ;
355             }
356             }
357              
358              
359             sub newitem {
360             my $self = shift ;
361             my $attribs = shift ;
362             my $value = shift ;
363             my $indent = shift ;
364              
365             $attribs->{defined} = 'false' unless defined $value ;
366             my $out = $self->newelement( %$attribs ) ;
367              
368             if ( ref $value ) {
369             $out->addelement( "\n" . " " x$indent ) ;
370             $out->addelement( nextpl2xml( $self, $value, $indent +1 ) ) ;
371             $out->addelement( "\n" . " " x( $indent -1 ) ) ;
372             }
373             else {
374             $out->addelement( $value ) ;
375             }
376              
377             return ( item => $out ) ;
378             }
379              
380             sub newelement {
381             my $self = shift if @_ && ( ref $_[0] || $_[0] eq __PACKAGE__ ) ;
382             $self = ! $self? __PACKAGE__: ref $self? ref $self: $self ;
383            
384             return bless [ { @_ } ], $self ;
385             }
386              
387             sub addelement {
388             my $self = shift ;
389             my $scalar = pop ;
390             my $key = @_? shift @_: 0 ;
391              
392             push @$self, $key, $scalar ;
393             }
394              
395             sub nvpdump {
396             my $key = @_ > 3? pop( @_ ): '' ;
397             my $space = @_ > 2? pop( @_ ): -1 ;
398             my $self = pop @_ ;
399             my @kids = $self->childnodes ;
400              
401             if ( @_ == 0 ) {
402             @kids = $self->childnode('perldata')->childnodes ;
403             carp( "Data source not from pl2xml" ) && return ""
404             unless @kids == 1 ;
405             return nvpdump( @{ $kids[0] } ) ;
406             }
407              
408             my $name = shift ;
409             my $pad = ' 'x$space ;
410              
411             return join "\n", map { nvpdump( @$_, $space +1 ) } @kids
412             if $name eq 'hashref' ;
413             return join "\n", map { nvpdump( @$_, $space, $key ) } @kids
414             if $name eq 'arrayref' ;
415              
416             $key = $key && @kids? $key: $self->getattributes->{key} ;
417              
418             my $value = join "\n$pad",
419             map { nvpdump( @$_, $space, $key ) } @kids ;
420             return $value if @kids && $kids[0][0] eq 'arrayref' ;
421              
422             $value = join "\n", '', $value, $pad if $value ;
423             $value ||= $self->gettext() ;
424             return sprintf "$pad<$key>%s", $value ;
425             }
426              
427             1 ;
428             __END__