File Coverage

blib/lib/XML/TreePP.pm
Criterion Covered Total %
statement 410 547 74.9
branch 259 406 63.7
condition 64 120 53.3
subroutine 30 41 73.1
pod 8 37 21.6
total 771 1151 66.9


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             XML::TreePP -- Pure Perl implementation for parsing/writing XML documents
4              
5             =head1 SYNOPSIS
6              
7             parse an XML document from file into hash tree:
8              
9             use XML::TreePP;
10             my $tpp = XML::TreePP->new();
11             my $tree = $tpp->parsefile( "index.rdf" );
12             print "Title: ", $tree->{"rdf:RDF"}->{item}->[0]->{title}, "\n";
13             print "URL: ", $tree->{"rdf:RDF"}->{item}->[0]->{link}, "\n";
14              
15             write an XML document as string from hash tree:
16              
17             use XML::TreePP;
18             my $tpp = XML::TreePP->new();
19             my $tree = { rss => { channel => { item => [ {
20             title => "The Perl Directory",
21             link => "http://www.perl.org/",
22             }, {
23             title => "The Comprehensive Perl Archive Network",
24             link => "http://cpan.perl.org/",
25             } ] } } };
26             my $xml = $tpp->write( $tree );
27             print $xml;
28              
29             get a remote XML document by HTTP-GET and parse it into hash tree:
30              
31             use XML::TreePP;
32             my $tpp = XML::TreePP->new();
33             my $tree = $tpp->parsehttp( GET => "http://use.perl.org/index.rss" );
34             print "Title: ", $tree->{"rdf:RDF"}->{channel}->{title}, "\n";
35             print "URL: ", $tree->{"rdf:RDF"}->{channel}->{link}, "\n";
36              
37             get a remote XML document by HTTP-POST and parse it into hash tree:
38              
39             use XML::TreePP;
40             my $tpp = XML::TreePP->new( force_array => [qw( item )] );
41             my $cgiurl = "http://search.hatena.ne.jp/keyword";
42             my $keyword = "ajax";
43             my $cgiquery = "mode=rss2&word=".$keyword;
44             my $tree = $tpp->parsehttp( POST => $cgiurl, $cgiquery );
45             print "Link: ", $tree->{rss}->{channel}->{item}->[0]->{link}, "\n";
46             print "Desc: ", $tree->{rss}->{channel}->{item}->[0]->{description}, "\n";
47              
48             =head1 DESCRIPTION
49              
50             XML::TreePP module parses an XML document and expands it for a hash tree.
51             This generates an XML document from a hash tree as the opposite way around.
52             This is a pure Perl implementation and requires no modules depended.
53             This can also fetch and parse an XML document from remote web server
54             like the XMLHttpRequest object does at JavaScript language.
55              
56             =head1 EXAMPLES
57              
58             =head2 Parse XML file
59              
60             Sample XML document:
61              
62            
63            
64             Yasuhisa
65             Chizuko
66            
67             Shiori
68             Yusuke
69             Kairi
70            
71            
72              
73             Sample program to read a xml file and dump it:
74              
75             use XML::TreePP;
76             use Data::Dumper;
77             my $tpp = XML::TreePP->new();
78             my $tree = $tpp->parsefile( "family.xml" );
79             my $text = Dumper( $tree );
80             print $text;
81              
82             Result dumped:
83              
84             $VAR1 = {
85             'family' => {
86             '-name' => 'Kawasaki',
87             'father' => 'Yasuhisa',
88             'mother' => 'Chizuko',
89             'children' => {
90             'girl' => 'Shiori'
91             'boy' => [
92             'Yusuke',
93             'Kairi'
94             ],
95             }
96             }
97             };
98              
99             Details:
100              
101             print $tree->{family}->{father}; # the father's given name.
102              
103             The prefix '-' is added on every attribute's name.
104              
105             print $tree->{family}->{"-name"}; # the family name of the family
106              
107             The array is used because the family has two boys.
108              
109             print $tree->{family}->{children}->{boy}->[1]; # The second boy's name
110             print $tree->{family}->{children}->{girl}; # The girl's name
111              
112             =head2 Text node and attributes:
113              
114             If a element has both of a text node and attributes
115             or both of a text node and other child nodes,
116             value of a text node is moved to C<#text> like child nodes.
117              
118             use XML::TreePP;
119             use Data::Dumper;
120             my $tpp = XML::TreePP->new();
121             my $source = 'Kawasaki Yusuke';
122             my $tree = $tpp->parse( $source );
123             my $text = Dumper( $tree );
124             print $text;
125              
126             The result dumped is following:
127              
128             $VAR1 = {
129             'span' => {
130             '-class' => 'author',
131             '#text' => 'Kawasaki Yusuke'
132             }
133             };
134              
135             The special node name of C<#text> is used because this elements
136             has attribute(s) in addition to the text node.
137             See also L option.
138              
139             =head1 METHODS
140              
141             =head2 new
142              
143             This constructor method returns a new XML::TreePP object with C<%options>.
144              
145             $tpp = XML::TreePP->new( %options );
146              
147             =head2 set
148              
149             This method sets a option value for C.
150             If C<$option_value> is not defined, its option is deleted.
151              
152             $tpp->set( option_name => $option_value );
153              
154             See OPTIONS section below for details.
155              
156             =head2 get
157              
158             This method returns a current option value for C.
159              
160             $tpp->get( 'option_name' );
161              
162             =head2 parse
163              
164             This method reads an XML document by string and returns a hash tree converted.
165             The first argument is a scalar or a reference to a scalar.
166              
167             $tree = $tpp->parse( $source );
168              
169             =head2 parsefile
170              
171             This method reads an XML document by file and returns a hash tree converted.
172             The first argument is a filename.
173              
174             $tree = $tpp->parsefile( $file );
175              
176             =head2 parsehttp
177              
178             This method receives an XML document from a remote server via HTTP and
179             returns a hash tree converted.
180              
181             $tree = $tpp->parsehttp( $method, $url, $body, $head );
182              
183             C<$method> is a method of HTTP connection: GET/POST/PUT/DELETE
184             C<$url> is an URI of an XML file.
185             C<$body> is a request body when you use POST method.
186             C<$head> is a request headers as a hash ref.
187             L module or L module is required to fetch a file.
188              
189             ( $tree, $xml, $code ) = $tpp->parsehttp( $method, $url, $body, $head );
190              
191             In array context, This method returns also raw XML document received
192             and HTTP response's status code.
193              
194             =head2 write
195              
196             This method parses a hash tree and returns an XML document as a string.
197              
198             $source = $tpp->write( $tree, $encode );
199              
200             C<$tree> is a reference to a hash tree.
201              
202             =head2 writefile
203              
204             This method parses a hash tree and writes an XML document into a file.
205              
206             $tpp->writefile( $file, $tree, $encode );
207              
208             C<$file> is a filename to create.
209             C<$tree> is a reference to a hash tree.
210              
211             =head1 OPTIONS FOR PARSING XML
212              
213             This module accepts option parameters following:
214              
215             =head2 force_array
216              
217             This option allows you to specify a list of element names which
218             should always be forced into an array representation.
219              
220             $tpp->set( force_array => [ 'rdf:li', 'item', '-xmlns' ] );
221              
222             The default value is null, it means that context of the elements
223             will determine to make array or to keep it scalar or hash.
224             Note that the special wildcard name C<'*'> means all elements.
225              
226             =head2 force_hash
227              
228             This option allows you to specify a list of element names which
229             should always be forced into an hash representation.
230              
231             $tpp->set( force_hash => [ 'item', 'image' ] );
232              
233             The default value is null, it means that context of the elements
234             will determine to make hash or to keep it scalar as a text node.
235             See also L option below.
236             Note that the special wildcard name C<'*'> means all elements.
237              
238             =head2 cdata_scalar_ref
239              
240             This option allows you to convert a cdata section into a reference
241             for scalar on parsing an XML document.
242              
243             $tpp->set( cdata_scalar_ref => 1 );
244              
245             The default value is false, it means that each cdata section is converted into a scalar.
246              
247             =head2 user_agent
248              
249             This option allows you to specify a HTTP_USER_AGENT string which
250             is used by parsehttp() method.
251              
252             $tpp->set( user_agent => 'Mozilla/4.0 (compatible; ...)' );
253              
254             The default string is C<'XML-TreePP/#.##'>, where C<'#.##'> is
255             substituted with the version number of this library.
256              
257             =head2 http_lite
258              
259             This option forces pasrsehttp() method to use a L instance.
260              
261             my $http = HTTP::Lite->new();
262             $tpp->set( http_lite => $http );
263              
264             =head2 lwp_useragent
265              
266             This option forces parsehttp() method to use a L instance.
267              
268             my $ua = LWP::UserAgent->new();
269             $ua->timeout( 60 );
270             $ua->env_proxy;
271             $tpp->set( lwp_useragent => $ua );
272              
273             You may use this with L.
274              
275             =head2 base_class
276              
277             This blesses class name for each element's hashref.
278             Each class is named straight as a child class of it parent class.
279              
280             $tpp->set( base_class => 'MyElement' );
281             my $xml = 'text';
282             my $tree = $tpp->parse( $xml );
283             print ref $tree->{root}->{parent}->{child}, "\n";
284              
285             A hash for element above is blessed to C
286             class. You may use this with L.
287              
288             =head2 elem_class
289              
290             This blesses class name for each element's hashref.
291             Each class is named horizontally under the direct child of C.
292              
293             $tpp->set( base_class => 'MyElement' );
294             my $xml = 'text';
295             my $tree = $tpp->parse( $xml );
296             print ref $tree->{root}->{parent}->{child}, "\n";
297              
298             A hash for element above is blessed to C class.
299              
300             =head2 xml_deref
301              
302             This option dereferences the numeric character references, like ë,
303             漢, etc., in an XML document when this value is true.
304              
305             $tpp->set( xml_deref => 1 );
306              
307             Note that, for security reasons and your convenient,
308             this module dereferences the predefined character entity references,
309             &, <, >, ' and ", and the numeric character
310             references up to U+007F without xml_deref per default.
311              
312             =head2 require_xml_decl
313              
314             This option requires XML declaration at the top of XML document to parse.
315              
316             $tpp->set( require_xml_decl => 1 );
317              
318             This will die when declration not found.
319              
320             =head1 OPTIONS FOR WRITING XML
321              
322             =head2 first_out
323              
324             This option allows you to specify a list of element/attribute
325             names which should always appears at first on output XML document.
326              
327             $tpp->set( first_out => [ 'link', 'title', '-type' ] );
328              
329             The default value is null, it means alphabetical order is used.
330              
331             =head2 last_out
332              
333             This option allows you to specify a list of element/attribute
334             names which should always appears at last on output XML document.
335              
336             $tpp->set( last_out => [ 'items', 'item', 'entry' ] );
337              
338             =head2 indent
339              
340             This makes the output more human readable by indenting appropriately.
341              
342             $tpp->set( indent => 2 );
343              
344             This doesn't strictly follow the XML specification but does looks nice.
345              
346             =head2 xml_decl
347              
348             This module inserts an XML declaration on top of the XML document generated
349             per default. This option forces to change it to another or just remove it.
350              
351             $tpp->set( xml_decl => '' );
352              
353             =head2 output_encoding
354              
355             This option allows you to specify a encoding of the XML document generated
356             by write/writefile methods.
357              
358             $tpp->set( output_encoding => 'UTF-8' );
359              
360             On Perl 5.8.0 and later, you can select it from every
361             encodings supported by Encode.pm. On Perl 5.6.x and before with
362             Jcode.pm, you can use C, C, C and
363             C. The default value is C which is recommended encoding.
364              
365             =head2 empty_element_tag_end
366              
367             $tpp->set( empty_element_tag_end => '>' );
368              
369             Set characters which close empty tag. The default value is ' />'.
370              
371             =head1 OPTIONS FOR BOTH
372              
373             =head2 utf8_flag
374              
375             This makes utf8 flag on for every element's value parsed
376             and makes it on for the XML document generated as well.
377              
378             $tpp->set( utf8_flag => 1 );
379              
380             Perl 5.8.1 or later is required to use this.
381              
382             =head2 attr_prefix
383              
384             This option allows you to specify a prefix character(s) which
385             is inserted before each attribute names.
386              
387             $tpp->set( attr_prefix => '@' );
388              
389             The default character is C<'-'>.
390             Or set C<'@'> to access attribute values like E4X, ECMAScript for XML.
391             Zero-length prefix C<''> is available as well, it means no prefix is added.
392              
393             =head2 text_node_key
394              
395             This option allows you to specify a hash key for text nodes.
396              
397             $tpp->set( text_node_key => '#text' );
398              
399             The default key is C<#text>.
400              
401             =head2 ignore_error
402              
403             This module calls Carp::croak function on an error per default.
404             This option makes all errors ignored and just returns.
405              
406             $tpp->set( ignore_error => 1 );
407              
408             =head2 use_ixhash
409              
410             This option keeps the order for each element appeared in XML.
411             L module is required.
412              
413             $tpp->set( use_ixhash => 1 );
414              
415             This makes parsing performance slow.
416             (about 100% slower than default)
417              
418             =head1 AUTHOR
419              
420             Yusuke Kawasaki, http://www.kawa.net/
421              
422             =head1 REPOSITORY
423              
424             https://github.com/kawanet/XML-TreePP
425              
426             =head1 COPYRIGHT
427              
428             The following copyright notice applies to all the files provided in
429             this distribution, including binary files, unless explicitly noted
430             otherwise.
431              
432             Copyright 2006-2010 Yusuke Kawasaki
433              
434             =head1 LICENSE
435              
436             This library is free software; you can redistribute it and/or modify
437             it under the same terms as Perl itself.
438              
439             =cut
440              
441             package XML::TreePP;
442 43     43   478923 use strict;
  43         80  
  43         1502  
443 43     43   171 use Carp;
  43         107  
  43         2917  
444 43     43   19202 use Symbol;
  43         29767  
  43         2684  
445              
446 43     43   221 use vars qw( $VERSION );
  43         53  
  43         242376  
447             $VERSION = '0.43';
448              
449             my $XML_ENCODING = 'UTF-8';
450             my $INTERNAL_ENCODING = 'UTF-8';
451             my $USER_AGENT = 'XML-TreePP/'.$VERSION.' ';
452             my $ATTR_PREFIX = '-';
453             my $TEXT_NODE_KEY = '#text';
454             my $USE_ENCODE_PM = ( $] >= 5.008 );
455             my $ALLOW_UTF8_FLAG = ( $] >= 5.008001 );
456              
457             my $EMPTY_ELEMENT_TAG_END = ' />';
458              
459             sub new {
460 93     93 1 115381 my $package = shift;
461 93         231 my $self = {@_};
462 93         212 bless $self, $package;
463 93         194 $self;
464             }
465              
466             sub die {
467 17     17 0 21 my $self = shift;
468 17         18 my $mess = shift;
469 17 100       50 return if $self->{ignore_error};
470 13         1606 Carp::croak $mess;
471             }
472              
473             sub warn {
474 2     2 0 3 my $self = shift;
475 2         3 my $mess = shift;
476 2 50       4 return if $self->{ignore_error};
477 2         245 Carp::carp $mess;
478             }
479              
480             sub set {
481 34     34 1 9218 my $self = shift;
482 34         42 my $key = shift;
483 34         34 my $val = shift;
484 34 100       72 if ( defined $val ) {
485 31         137 $self->{$key} = $val;
486             }
487             else {
488 3         11 delete $self->{$key};
489             }
490             }
491              
492             sub get {
493 0     0 1 0 my $self = shift;
494 0         0 my $key = shift;
495 0 0       0 $self->{$key} if exists $self->{$key};
496             }
497              
498             sub writefile {
499 3     3 1 10 my $self = shift;
500 3         4 my $file = shift;
501 3 50       8 my $tree = shift or return $self->die( 'Invalid tree' );
502 3         4 my $encode = shift;
503 3 50       6 return $self->die( 'Invalid filename' ) unless defined $file;
504 3         4 my $text = $self->write( $tree, $encode );
505 3 100 66     12 if ( $ALLOW_UTF8_FLAG && utf8::is_utf8( $text ) ) {
506 1         3 utf8::encode( $text );
507             }
508 3         8 $self->write_raw_xml( $file, $text );
509             }
510              
511             sub write {
512 168     168 1 113542 my $self = shift;
513 168 100       439 my $tree = shift or return $self->die( 'Invalid tree' );
514 165   33     659 my $from = $self->{internal_encoding} || $INTERNAL_ENCODING;
515 165   66     820 my $to = shift || $self->{output_encoding} || $XML_ENCODING;
516 165         219 my $decl = $self->{xml_decl};
517 165 100       498 $decl = '' unless defined $decl;
518              
519 165         292 local $self->{__first_out};
520 165 100       360 if ( exists $self->{first_out} ) {
521 6         9 my $keys = $self->{first_out};
522 6 50       16 $keys = [$keys] unless ref $keys;
523 6         23 $self->{__first_out} = { map { $keys->[$_] => $_ } 0 .. $#$keys };
  42         83  
524             }
525              
526 165         230 local $self->{__last_out};
527 165 100       356 if ( exists $self->{last_out} ) {
528 2         2 my $keys = $self->{last_out};
529 2 50       5 $keys = [$keys] unless ref $keys;
530 2         3 $self->{__last_out} = { map { $keys->[$_] => $_ } 0 .. $#$keys };
  6         9  
531             }
532              
533 165 100       308 my $tnk = $self->{text_node_key} if exists $self->{text_node_key};
534 165 100       292 $tnk = $TEXT_NODE_KEY unless defined $tnk;
535 165         273 local $self->{text_node_key} = $tnk;
536              
537 165 100       334 my $apre = $self->{attr_prefix} if exists $self->{attr_prefix};
538 165 100       328 $apre = $ATTR_PREFIX unless defined $apre;
539 165         302 local $self->{__attr_prefix_len} = length($apre);
540 165         209 local $self->{__attr_prefix_rex} = $apre;
541              
542 165         250 local $self->{__indent};
543 165 100 100     447 if ( exists $self->{indent} && $self->{indent} ) {
544 2         7 $self->{__indent} = ' ' x $self->{indent};
545             }
546              
547 165 100       569 if ( ! UNIVERSAL::isa( $tree, 'HASH' )) {
548 3         6 return $self->die( 'Invalid tree' );
549             }
550              
551 162         351 my $text = $self->hash_to_xml( undef, $tree );
552 162 50 33     618 if ( $from && $to ) {
553 162         363 my $stat = $self->encode_from_to( \$text, $from, $to );
554 160 50       337 return $self->die( "Unsupported encoding: $to" ) unless $stat;
555             }
556              
557 160 100       308 return $text if ( $decl eq '' );
558 159         786 join( "\n", $decl, $text );
559             }
560              
561             sub parsehttp {
562 0     0 1 0 my $self = shift;
563              
564 0         0 local $self->{__user_agent};
565 0 0       0 if ( exists $self->{user_agent} ) {
566 0         0 my $agent = $self->{user_agent};
567 0 0       0 $agent .= $USER_AGENT if ( $agent =~ /\s$/s );
568 0 0       0 $self->{__user_agent} = $agent if ( $agent ne '' );
569             } else {
570 0         0 $self->{__user_agent} = $USER_AGENT;
571             }
572              
573 0         0 my $http = $self->{__http_module};
574 0 0       0 unless ( $http ) {
575 0         0 $http = $self->find_http_module(@_);
576 0         0 $self->{__http_module} = $http;
577             }
578 0 0       0 if ( $http eq 'LWP::UserAgent' ) {
    0          
579 0         0 return $self->parsehttp_lwp(@_);
580             }
581             elsif ( $http eq 'HTTP::Lite' ) {
582 0         0 return $self->parsehttp_lite(@_);
583             }
584             else {
585 0         0 return $self->die( "LWP::UserAgent or HTTP::Lite is required: $_[1]" );
586             }
587             }
588              
589             sub find_http_module {
590 0   0 0 0 0 my $self = shift || {};
591              
592 0 0 0     0 if ( exists $self->{lwp_useragent} && ref $self->{lwp_useragent} ) {
593 0 0       0 return 'LWP::UserAgent' if defined $LWP::UserAgent::VERSION;
594 0 0       0 return 'LWP::UserAgent' if &load_lwp_useragent();
595 0         0 return $self->die( "LWP::UserAgent is required: $_[1]" );
596             }
597              
598 0 0 0     0 if ( exists $self->{http_lite} && ref $self->{http_lite} ) {
599 0 0       0 return 'HTTP::Lite' if defined $HTTP::Lite::VERSION;
600 0 0       0 return 'HTTP::Lite' if &load_http_lite();
601 0         0 return $self->die( "HTTP::Lite is required: $_[1]" );
602             }
603              
604 0 0       0 return 'LWP::UserAgent' if defined $LWP::UserAgent::VERSION;
605 0 0       0 return 'HTTP::Lite' if defined $HTTP::Lite::VERSION;
606 0 0       0 return 'LWP::UserAgent' if &load_lwp_useragent();
607 0 0       0 return 'HTTP::Lite' if &load_http_lite();
608 0         0 return $self->die( "LWP::UserAgent or HTTP::Lite is required: $_[1]" );
609             }
610              
611             sub load_lwp_useragent {
612 0 0   0 0 0 return $LWP::UserAgent::VERSION if defined $LWP::UserAgent::VERSION;
613 0         0 local $@;
614 0         0 eval { require LWP::UserAgent; };
  0         0  
615 0         0 $LWP::UserAgent::VERSION;
616             }
617              
618             sub load_http_lite {
619 0 0   0 0 0 return $HTTP::Lite::VERSION if defined $HTTP::Lite::VERSION;
620 0         0 local $@;
621 0         0 eval { require HTTP::Lite; };
  0         0  
622 0         0 $HTTP::Lite::VERSION;
623             }
624              
625             sub load_tie_ixhash {
626 0 0   0 0 0 return $Tie::IxHash::VERSION if defined $Tie::IxHash::VERSION;
627 0         0 local $@;
628 0         0 eval { require Tie::IxHash; };
  0         0  
629 0         0 $Tie::IxHash::VERSION;
630             }
631              
632             sub parsehttp_lwp {
633 0     0 0 0 my $self = shift;
634 0 0       0 my $method = shift or return $self->die( 'Invalid HTTP method' );
635 0 0       0 my $url = shift or return $self->die( 'Invalid URL' );
636 0         0 my $body = shift;
637 0         0 my $header = shift;
638              
639 0 0       0 my $ua = $self->{lwp_useragent} if exists $self->{lwp_useragent};
640 0 0       0 if ( ! ref $ua ) {
641 0         0 $ua = LWP::UserAgent->new();
642 0         0 $ua->env_proxy();
643 0 0       0 $ua->agent( $self->{__user_agent} ) if defined $self->{__user_agent};
644             } else {
645 0 0       0 $ua->agent( $self->{__user_agent} ) if exists $self->{user_agent};
646             }
647              
648 0         0 my $req = HTTP::Request->new( $method, $url );
649 0         0 my $ct = 0;
650 0 0       0 if ( ref $header ) {
651 0         0 foreach my $field ( sort keys %$header ) {
652 0         0 my $value = $header->{$field};
653 0         0 $req->header( $field => $value );
654 0 0       0 $ct ++ if ( $field =~ /^Content-Type$/i );
655             }
656             }
657 0 0 0     0 if ( defined $body && ! $ct ) {
658 0         0 $req->header( 'Content-Type' => 'application/x-www-form-urlencoded' );
659             }
660 0 0       0 $req->add_content_utf8($body) if defined $body;
661 0         0 my $res = $ua->request($req);
662 0         0 my $code = $res->code();
663 0         0 my $text;
664 0 0       0 if ( $res->can( 'decoded_content' )) {
665 0         0 $text = $res->decoded_content( charset => 'none' );
666             } else {
667 0         0 $text = $res->content(); # less than LWP 5.802
668             }
669 0 0       0 my $tree = $self->parse( \$text ) if $res->is_success();
670 0 0       0 wantarray ? ( $tree, $text, $code ) : $tree;
671             }
672              
673             sub parsehttp_lite {
674 0     0 0 0 my $self = shift;
675 0 0       0 my $method = shift or return $self->die( 'Invalid HTTP method' );
676 0 0       0 my $url = shift or return $self->die( 'Invalid URL' );
677 0         0 my $body = shift;
678 0         0 my $header = shift;
679              
680 0         0 my $http = HTTP::Lite->new();
681 0         0 $http->method($method);
682 0         0 my $ua = 0;
683 0 0       0 if ( ref $header ) {
684 0         0 foreach my $field ( sort keys %$header ) {
685 0         0 my $value = $header->{$field};
686 0         0 $http->add_req_header( $field, $value );
687 0 0       0 $ua ++ if ( $field =~ /^User-Agent$/i );
688             }
689             }
690 0 0 0     0 if ( defined $self->{__user_agent} && ! $ua ) {
691 0         0 $http->add_req_header( 'User-Agent', $self->{__user_agent} );
692             }
693 0 0       0 $http->{content} = $body if defined $body;
694 0 0       0 my $code = $http->request($url) or return;
695 0         0 my $text = $http->body();
696 0         0 my $tree = $self->parse( \$text );
697 0 0       0 wantarray ? ( $tree, $text, $code ) : $tree;
698             }
699              
700             sub parsefile {
701 33     33 1 12345 my $self = shift;
702 33         42 my $file = shift;
703 33 50       73 return $self->die( 'Invalid filename' ) unless defined $file;
704 33         63 my $text = $self->read_raw_xml($file);
705 32         89 $self->parse( \$text );
706             }
707              
708             sub parse {
709 274     274 1 44400 my $self = shift;
710 274 100       593 my $text = ref $_[0] ? ${$_[0]} : $_[0];
  32         46  
711 274 100       586 return $self->die( 'Null XML source' ) unless defined $text;
712              
713 273   66     522 my $from = &xml_decl_encoding(\$text) || $XML_ENCODING;
714 273   33     1030 my $to = $self->{internal_encoding} || $INTERNAL_ENCODING;
715 273 50 33     929 if ( $from && $to ) {
716 273         540 my $stat = $self->encode_from_to( \$text, $from, $to );
717 271 50       548 return $self->die( "Unsupported encoding: $from" ) unless $stat;
718             }
719              
720 271         439 local $self->{__force_array};
721 271         348 local $self->{__force_array_all};
722 271 100       518 if ( exists $self->{force_array} ) {
723 7         9 my $force = $self->{force_array};
724 7 100       19 $force = [$force] unless ref $force;
725 7         10 $self->{__force_array} = { map { $_ => 1 } @$force };
  15         33  
726 7         14 $self->{__force_array_all} = $self->{__force_array}->{'*'};
727             }
728              
729 271         314 local $self->{__force_hash};
730 271         304 local $self->{__force_hash_all};
731 271 100       527 if ( exists $self->{force_hash} ) {
732 2         3 my $force = $self->{force_hash};
733 2 100       6 $force = [$force] unless ref $force;
734 2         3 $self->{__force_hash} = { map { $_ => 1 } @$force };
  4         10  
735 2         5 $self->{__force_hash_all} = $self->{__force_hash}->{'*'};
736             }
737              
738 271 100       471 my $tnk = $self->{text_node_key} if exists $self->{text_node_key};
739 271 100       534 $tnk = $TEXT_NODE_KEY unless defined $tnk;
740 271         390 local $self->{text_node_key} = $tnk;
741              
742 271 100       437 my $apre = $self->{attr_prefix} if exists $self->{attr_prefix};
743 271 100       453 $apre = $ATTR_PREFIX unless defined $apre;
744 271         373 local $self->{attr_prefix} = $apre;
745              
746 271 50 33     577 if ( exists $self->{use_ixhash} && $self->{use_ixhash} ) {
747 0 0       0 return $self->die( "Tie::IxHash is required." ) unless &load_tie_ixhash();
748             }
749              
750             # Avoid segfaults when receving random input (RT #42441)
751 271 100 66     603 if ( exists $self->{require_xml_decl} && $self->{require_xml_decl} ) {
752 3 100       8 return $self->die( "XML declaration not found" ) unless looks_like_xml(\$text);
753             }
754              
755 269         574 my $flat = $self->xml_to_flat(\$text);
756 269 100       574 my $class = $self->{base_class} if exists $self->{base_class};
757 269         563 my $tree = $self->flat_to_tree( $flat, '', $class );
758 268 50       499 if ( ref $tree ) {
759 268 100 66     977 if ( defined $class ) {
    100          
760 1         22 bless( $tree, $class );
761             }
762             elsif ( exists $self->{elem_class} && $self->{elem_class} ) {
763 1         25 bless( $tree, $self->{elem_class} );
764             }
765             }
766 268 50       1449 wantarray ? ( $tree, $text ) : $tree;
767             }
768              
769             sub xml_to_flat {
770 269     269 0 264 my $self = shift;
771 269         266 my $textref = shift; # reference
772 269         320 my $flat = [];
773 269         322 my $prefix = $self->{attr_prefix};
774 269   33     553 my $ixhash = ( exists $self->{use_ixhash} && $self->{use_ixhash} );
775              
776 269         325 my $deref = \&xml_unescape;
777 269   100     546 my $xml_deref = ( exists $self->{xml_deref} && $self->{xml_deref} );
778 269 100       425 if ( $xml_deref ) {
779 13 100 100     108 if (( exists $self->{utf8_flag} && $self->{utf8_flag} ) ||
      66        
      66        
780             ( $ALLOW_UTF8_FLAG && utf8::is_utf8( $$textref ))) {
781 7         13 $deref = \&xml_deref_string;
782             } else {
783 6         13 $deref = \&xml_deref_octet;
784             }
785             }
786              
787 269         2106 while ( $$textref =~ m{
788             ([^<]*) <
789             ((
790             \? ([^<>]*) \?
791             )|(
792             \!\[CDATA\[(.*?)\]\]
793             )|(
794             \!DOCTYPE\s+([^\[\]<>]*(?:\[.*?\]\s*)?)
795             )|(
796             \!--(.*?)--
797             )|(
798             ([^\!\?\s<>](?:"[^"]*"|'[^']*'|[^"'<>])*)
799             ))
800             > ([^<]*)
801             }sxg ) {
802             my (
803 2358         7711 $ahead, $match, $typePI, $contPI, $typeCDATA,
804             $contCDATA, $typeDocT, $contDocT, $typeCmnt, $contCmnt,
805             $typeElem, $contElem, $follow
806             )
807             = ( $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13 );
808 2358 50 33     7358 if ( defined $ahead && $ahead =~ /\S/ ) {
809 0         0 $ahead =~ s/([^\040-\076])/sprintf("\\x%02X",ord($1))/eg;
  0         0  
810 0         0 $self->warn( "Invalid string: [$ahead] before <$match>" );
811             }
812              
813 2358 100       3145 if ($typeElem) { # Element
    100          
    100          
    50          
    50          
814 2043         1860 my $node = {};
815 2043 100       4882 if ( $contElem =~ s#^/## ) {
    100          
816 960         1367 $node->{endTag}++;
817             }
818             elsif ( $contElem =~ s#/$## ) {
819 123         179 $node->{emptyTag}++;
820             }
821             else {
822 960         1382 $node->{startTag}++;
823             }
824 2043 50       7200 $node->{tagName} = $1 if ( $contElem =~ s#^(\S+)\s*## );
825 2043 100       3336 unless ( $node->{endTag} ) {
826 1083         750 my $attr;
827 1083         2209 while ( $contElem =~ m{
828             ([^\s\=\"\']+)\s*=\s*(?:(")(.*?)"|'(.*?)')
829             }sxg ) {
830 353         383 my $key = $1;
831 353 100       763 my $val = &$deref( $2 ? $3 : $4 );
832 353 100       571 if ( ! ref $attr ) {
833 235         206 $attr = {};
834 235 50       397 tie( %$attr, 'Tie::IxHash' ) if $ixhash;
835             }
836 353         1083 $attr->{$prefix.$key} = $val;
837             }
838 1083 100       1752 $node->{attributes} = $attr if ref $attr;
839             }
840 2043         2439 push( @$flat, $node );
841             }
842             elsif ($typeCDATA) { ## CDATASection
843 177 100 100     558 if ( exists $self->{cdata_scalar_ref} && $self->{cdata_scalar_ref} ) {
844 114         153 push( @$flat, \$contCDATA ); # as reference for scalar
845             }
846             else {
847 63         84 push( @$flat, $contCDATA ); # as scalar like text node
848             }
849             }
850             elsif ($typeCmnt) { # Comment (ignore)
851             }
852             elsif ($typeDocT) { # DocumentType (ignore)
853             }
854             elsif ($typePI) { # ProcessingInstruction (ignore)
855             }
856             else {
857 0         0 $self->warn( "Invalid Tag: <$match>" );
858             }
859 2358 100       12368 if ( $follow =~ /\S/ ) { # text node
860 698         913 my $val = &$deref($follow);
861 698         3823 push( @$flat, $val );
862             }
863             }
864 269         409 $flat;
865             }
866              
867             sub flat_to_tree {
868 1229     1229 0 1017 my $self = shift;
869 1229         890 my $source = shift;
870 1229         995 my $parent = shift;
871 1229         882 my $class = shift;
872 1229         1142 my $tree = {};
873 1229         1282 my $text = [];
874              
875 1229 50 33     2308 if ( exists $self->{use_ixhash} && $self->{use_ixhash} ) {
876 0         0 tie( %$tree, 'Tie::IxHash' );
877             }
878              
879 1229         1831 while ( scalar @$source ) {
880 2917         2509 my $node = shift @$source;
881 2917 100 100     8966 if ( !ref $node || UNIVERSAL::isa( $node, "SCALAR" ) ) {
882 875         966 push( @$text, $node ); # cdata or text node
883 875         1447 next;
884             }
885 2042         2078 my $name = $node->{tagName};
886 2042 100       2915 if ( $node->{endTag} ) {
887 959 100       2093 last if ( $parent eq $name );
888 2         11 return $self->die( "Invalid tag sequence: <$parent>" );
889             }
890 1083         860 my $elem = $node->{attributes};
891 1083   100     2914 my $forcehash = $self->{__force_hash_all} || $self->{__force_hash}->{$name};
892 1083         734 my $subclass;
893 1083 100       1465 if ( defined $class ) {
894 15         10 my $escname = $name;
895 15         16 $escname =~ s/\W/_/sg;
896 15         23 $subclass = $class.'::'.$escname;
897             }
898 1083 100 66     1509 if ( $node->{startTag} ) { # recursive call
    100          
899 960         1563 my $child = $self->flat_to_tree( $source, $name, $subclass );
900 958 100       1454 next unless defined $child;
901 957 100       1399 my $hasattr = scalar keys %$elem if ref $elem;
902 957 100       2447 if ( UNIVERSAL::isa( $child, "HASH" ) ) {
903 189 100       250 if ( $hasattr ) {
904             # some attributes and some child nodes
905 92         583 %$elem = ( %$elem, %$child );
906             }
907             else {
908             # some child nodes without attributes
909 97         118 $elem = $child;
910             }
911             }
912             else {
913 768 100       1224 if ( $hasattr ) {
    100          
914             # some attributes and text node
915 31         70 $elem->{$self->{text_node_key}} = $child;
916             }
917             elsif ( $forcehash ) {
918             # only text node without attributes
919 7         15 $elem = { $self->{text_node_key} => $child };
920             }
921             else {
922             # text node without attributes
923 730         758 $elem = $child;
924             }
925             }
926             }
927             elsif ( $forcehash && ! ref $elem ) {
928 2         3 $elem = {};
929             }
930             # bless to a class by base_class or elem_class
931 1080 100 100     2717 if ( ref $elem && UNIVERSAL::isa( $elem, "HASH" ) ) {
932 341 100 66     1159 if ( defined $subclass ) {
    100          
933 10         29 bless( $elem, $subclass );
934             } elsif ( exists $self->{elem_class} && $self->{elem_class} ) {
935 10         10 my $escname = $name;
936 10         13 $escname =~ s/\W/_/sg;
937 10         14 my $elmclass = $self->{elem_class}.'::'.$escname;
938 10         38 bless( $elem, $elmclass );
939             }
940             }
941             # next unless defined $elem;
942 1080   100     3143 $tree->{$name} ||= [];
943 1080         827 push( @{ $tree->{$name} }, $elem );
  1080         2869  
944             }
945 1225 100       1847 if ( ! $self->{__force_array_all} ) {
946 1220         2387 foreach my $key ( keys %$tree ) {
947 928 100       1532 next if $self->{__force_array}->{$key};
948 920 100       683 next if ( 1 < scalar @{ $tree->{$key} } );
  920         1505  
949 899         650 $tree->{$key} = shift @{ $tree->{$key} };
  899         1609  
950             }
951             }
952 1225         1327 my $haschild = scalar keys %$tree;
953 1225 100       1976 if ( scalar @$text ) {
    100          
954 752 100       1018 if ( scalar @$text == 1 ) {
  198 100       287  
955             # one text node (normal)
956 677         667 $text = shift @$text;
957             }
958             elsif ( ! scalar grep {ref $_} @$text ) {
959             # some text node splitted
960 36         76 $text = join( '', @$text );
961             }
962             else {
963             # some cdata node
964 39 100       49 my $join = join( '', map {ref $_ ? $$_ : $_} @$text );
  103         208  
965 39         109 $text = \$join;
966             }
967 752 100       995 if ( $haschild ) {
968             # some child nodes and also text node
969 12         20 $tree->{$self->{text_node_key}} = $text;
970             }
971             else {
972             # only text node without child nodes
973 740         670 $tree = $text;
974             }
975             }
976             elsif ( ! $haschild ) {
977             # no child and no text
978 28         245 $tree = "";
979             }
980 1225         1912 $tree;
981             }
982              
983             sub hash_to_xml {
984 306     306 0 325 my $self = shift;
985 306         337 my $name = shift;
986 306         238 my $hash = shift;
987 306         341 my $out = [];
988 306         330 my $attr = [];
989 306         687 my $allkeys = [ keys %$hash ];
990 306 100       646 my $fo = $self->{__first_out} if ref $self->{__first_out};
991 306 100       524 my $lo = $self->{__last_out} if ref $self->{__last_out};
992 306 100       457 my $firstkeys = [ sort { $fo->{$a} <=> $fo->{$b} } grep { exists $fo->{$_} } @$allkeys ] if ref $fo;
  33         51  
  69         130  
993 306 100       494 my $lastkeys = [ sort { $lo->{$a} <=> $lo->{$b} } grep { exists $lo->{$_} } @$allkeys ] if ref $lo;
  6         7  
  20         23  
994 306 100       449 $allkeys = [ grep { ! exists $fo->{$_} } @$allkeys ] if ref $fo;
  69         97  
995 306 100       501 $allkeys = [ grep { ! exists $lo->{$_} } @$allkeys ] if ref $lo;
  14         16  
996 306 50 33     721 unless ( exists $self->{use_ixhash} && $self->{use_ixhash} ) {
997 306         695 $allkeys = [ sort @$allkeys ];
998             }
999 306         423 my $prelen = $self->{__attr_prefix_len};
1000 306         310 my $pregex = $self->{__attr_prefix_rex};
1001 306         270 my $textnk = $self->{text_node_key};
1002 306   66     907 my $tagend = $self->{empty_element_tag_end} || $EMPTY_ELEMENT_TAG_END;
1003              
1004 306         397 foreach my $keys ( $firstkeys, $allkeys, $lastkeys ) {
1005 918 100       1625 next unless ref $keys;
1006 339 100       585 my $elemkey = $prelen ? [ grep { substr($_,0,$prelen) ne $pregex } @$keys ] : $keys;
  451         965  
1007 339 100       567 my $attrkey = $prelen ? [ grep { substr($_,0,$prelen) eq $pregex } @$keys ] : [];
  451         671  
1008              
1009 339         404 foreach my $key ( @$elemkey ) {
1010 406         458 my $val = $hash->{$key};
1011 406 100       1933 if ( !defined $val ) {
    100          
    100          
    100          
1012 7 100       17 next if ($key eq $textnk);
1013 6         22 push( @$out, "<$key$tagend" );
1014             }
1015             elsif ( UNIVERSAL::isa( $val, 'HASH' ) ) {
1016 132         532 my $child = $self->hash_to_xml( $key, $val );
1017 132         256 push( @$out, $child );
1018             }
1019             elsif ( UNIVERSAL::isa( $val, 'ARRAY' ) ) {
1020 8         27 my $child = $self->array_to_xml( $key, $val );
1021 8         14 push( @$out, $child );
1022             }
1023             elsif ( UNIVERSAL::isa( $val, 'SCALAR' ) ) {
1024 50         119 my $child = $self->scalaref_to_cdata( $key, $val );
1025 50         125 push( @$out, $child );
1026             }
1027             else {
1028 209         222 my $ref = ref $val;
1029 209 100       336 $self->warn( "Unsupported reference type: $ref in $key" ) if $ref;
1030 209         471 my $child = $self->scalar_to_xml( $key, $val );
1031 209         423 push( @$out, $child );
1032             }
1033             }
1034              
1035 339         606 foreach my $key ( @$attrkey ) {
1036 53         72 my $name = substr( $key, $prelen );
1037 53         100 my $val = &xml_escape( $hash->{$key} );
1038 53         256 push( @$attr, ' ' . $name . '="' . $val . '"' );
1039             }
1040             }
1041 306         438 my $jattr = join( '', @$attr );
1042              
1043 306 100 100     782 if ( defined $name && scalar @$out && ! grep { ! /^
  243   100     819  
1044             # Use human-friendly white spacing
1045 103 100       232 if ( defined $self->{__indent} ) {
1046 6         79 s/^(\s*<)/$self->{__indent}$1/mg foreach @$out;
1047             }
1048 103         179 unshift( @$out, "\n" );
1049             }
1050              
1051 306         449 my $text = join( '', @$out );
1052 306 100       466 if ( defined $name ) {
1053 144 100       274 if ( scalar @$out ) {
1054 126         322 $text = "<$name$jattr>$text\n";
1055             }
1056             else {
1057 18         37 $text = "<$name$jattr$tagend\n";
1058             }
1059             }
1060 306         712 $text;
1061             }
1062              
1063             sub array_to_xml {
1064 8     8 0 14 my $self = shift;
1065 8         8 my $name = shift;
1066 8         10 my $array = shift;
1067 8         14 my $out = [];
1068 8   33     42 my $tagend = $self->{empty_element_tag_end} || $EMPTY_ELEMENT_TAG_END;
1069              
1070 8         19 foreach my $val (@$array) {
1071 16 100       76 if ( !defined $val ) {
    100          
    50          
    50          
1072 1         3 push( @$out, "<$name$tagend\n" );
1073             }
1074             elsif ( UNIVERSAL::isa( $val, 'HASH' ) ) {
1075 12         22 my $child = $self->hash_to_xml( $name, $val );
1076 12         21 push( @$out, $child );
1077             }
1078             elsif ( UNIVERSAL::isa( $val, 'ARRAY' ) ) {
1079 0         0 my $child = $self->array_to_xml( $name, $val );
1080 0         0 push( @$out, $child );
1081             }
1082             elsif ( UNIVERSAL::isa( $val, 'SCALAR' ) ) {
1083 0         0 my $child = $self->scalaref_to_cdata( $name, $val );
1084 0         0 push( @$out, $child );
1085             }
1086             else {
1087 3         5 my $ref = ref $val;
1088 3 50       7 $self->warn( "Unsupported reference type: $ref in $name" ) if $ref;
1089 3         7 my $child = $self->scalar_to_xml( $name, $val );
1090 3         6 push( @$out, $child );
1091             }
1092             }
1093              
1094 8         35 my $text = join( '', @$out );
1095 8         21 $text;
1096             }
1097              
1098             sub scalaref_to_cdata {
1099 50     50 0 54 my $self = shift;
1100 50         51 my $name = shift;
1101 50         49 my $ref = shift;
1102 50 100       101 my $data = defined $$ref ? $$ref : '';
1103 50         174 $data =~ s#(]])(>)#$1]]>
1104 50         81 my $text = '';
1105 50 100       174 $text = "<$name>$text\n" if ( $name ne $self->{text_node_key} );
1106 50         81 $text;
1107             }
1108              
1109             sub scalar_to_xml {
1110 212     212 0 190 my $self = shift;
1111 212         186 my $name = shift;
1112 212         188 my $scalar = shift;
1113 212         171 my $copy = $scalar;
1114 212         328 my $text = &xml_escape($copy);
1115 212 100       663 $text = "<$name>$text\n" if ( $name ne $self->{text_node_key} );
1116 212         296 $text;
1117             }
1118              
1119             sub write_raw_xml {
1120 3     3 0 3 my $self = shift;
1121 3         3 my $file = shift;
1122 3         10 my $fh = Symbol::gensym();
1123 3 50       257 open( $fh, ">$file" ) or return $self->die( "$! - $file" );
1124 3         22 print $fh @_;
1125 3         93 close($fh);
1126             }
1127              
1128             sub read_raw_xml {
1129 33     33 0 33 my $self = shift;
1130 33         32 my $file = shift;
1131 33         75 my $fh = Symbol::gensym();
1132 33 100       1293 open( $fh, $file ) or return $self->die( "$! - $file" );
1133 31         111 local $/ = undef;
1134 31         530 my $text = <$fh>;
1135 31         177 close($fh);
1136 31         134 $text;
1137             }
1138              
1139             sub looks_like_xml {
1140 276     276 0 252 my $textref = shift;
1141 276         2060 my $args = ( $$textref =~ /^(?:\s*\xEF\xBB\xBF)?\s*<\?xml(\s+\S.*)\?>/s )[0];
1142 276 100       521 if ( ! $args ) {
1143 163         869 return;
1144             }
1145 113         262 return $args;
1146             }
1147              
1148             sub xml_decl_encoding {
1149 273     273 0 274 my $textref = shift;
1150 273 50       524 return unless defined $$textref;
1151 273 100       419 my $args = looks_like_xml($textref) or return;
1152 112 100       574 my $getcode = ( $args =~ /\s+encoding=(".*?"|'.*?')/ )[0] or return;
1153 106         254 $getcode =~ s/^['"]//;
1154 106         211 $getcode =~ s/['"]$//;
1155 106         248 $getcode;
1156             }
1157              
1158             sub encode_from_to {
1159 435     435 0 521 my $self = shift;
1160 435 50       810 my $txtref = shift or return;
1161 435 50       767 my $from = shift or return;
1162 435 50       725 my $to = shift or return;
1163              
1164 435 50       711 unless ( defined $Encode::EUCJPMS::VERSION ) {
1165 435 50       731 $from = 'EUC-JP' if ( $from =~ /\beuc-?jp-?(win|ms)$/i );
1166 435 50       755 $to = 'EUC-JP' if ( $to =~ /\beuc-?jp-?(win|ms)$/i );
1167             }
1168              
1169 435         1242 my $RE_IS_UTF8 = qr/^utf-?8$/i;
1170 435 100       2650 if ( $from =~ $RE_IS_UTF8 ) {
1171 425         650 $$txtref =~ s/^\xEF\xBB\xBF//s; # UTF-8 BOM (Byte Order Mark)
1172             }
1173              
1174 435 100       886 my $setflag = $self->{utf8_flag} if exists $self->{utf8_flag};
1175 435 50 33     958 if ( ! $ALLOW_UTF8_FLAG && $setflag ) {
1176 0         0 return $self->die( "Perl 5.8.1 is required for utf8_flag: $]" );
1177             }
1178              
1179 435 50 0     674 if ( $USE_ENCODE_PM ) {
    0 0        
1180 435         715 &load_encode();
1181 435         301165 my $encver = ( $Encode::VERSION =~ /^([\d\.]+)/ )[0];
1182 435 50       1816 my $check = ( $encver < 2.13 ) ? 0x400 : Encode::FB_XMLCREF();
1183              
1184 435 50       1393 my $encfrom = Encode::find_encoding($from) if $from;
1185 435 100       47510 return $self->die( "Unknown encoding: $from" ) unless ref $encfrom;
1186 433 50       1176 my $encto = Encode::find_encoding($to) if $to;
1187 433 100       25274 return $self->die( "Unknown encoding: $to" ) unless ref $encto;
1188              
1189 431 100 66     1947 if ( $ALLOW_UTF8_FLAG && utf8::is_utf8( $$txtref ) ) {
1190 13 50       71 if ( $to =~ $RE_IS_UTF8 ) {
1191             # skip
1192             } else {
1193 0         0 $$txtref = $encto->encode( $$txtref, $check );
1194             }
1195             } else {
1196 418         2007 $$txtref = $encfrom->decode( $$txtref );
1197 418 100 100     9732 if ( $to =~ $RE_IS_UTF8 && $setflag ) {
1198             # skip
1199             } else {
1200 403         6534 $$txtref = $encto->encode( $$txtref, $check );
1201             }
1202             }
1203             }
1204             elsif ( ( uc($from) eq 'ISO-8859-1'
1205             || uc($from) eq 'US-ASCII'
1206             || uc($from) eq 'LATIN-1' ) && uc($to) eq 'UTF-8' ) {
1207 0         0 &latin1_to_utf8($txtref);
1208             }
1209             else {
1210 0         0 my $jfrom = &get_jcode_name($from);
1211 0         0 my $jto = &get_jcode_name($to);
1212 0 0       0 return $to if ( uc($jfrom) eq uc($jto) );
1213 0 0 0     0 if ( $jfrom && $jto ) {
1214 0         0 &load_jcode();
1215 0 0       0 if ( defined $Jcode::VERSION ) {
1216 0         0 Jcode::convert( $txtref, $jto, $jfrom );
1217             }
1218             else {
1219 0         0 return $self->die( "Jcode.pm is required: $from to $to" );
1220             }
1221             }
1222             else {
1223 0         0 return $self->die( "Encode.pm is required: $from to $to" );
1224             }
1225             }
1226 431         977 $to;
1227             }
1228              
1229             sub load_jcode {
1230 0 0   0 0 0 return if defined $Jcode::VERSION;
1231 0         0 local $@;
1232 0         0 eval { require Jcode; };
  0         0  
1233             }
1234              
1235             sub load_encode {
1236 435 100   435 0 952 return if defined $Encode::VERSION;
1237 40         62 local $@;
1238 40         67 eval { require Encode; };
  40         23373  
1239             }
1240              
1241             sub latin1_to_utf8 {
1242 0     0 0 0 my $strref = shift;
1243 0         0 $$strref =~ s{
1244             ([\x80-\xFF])
1245             }{
1246 0         0 pack( 'C2' => 0xC0|(ord($1)>>6),0x80|(ord($1)&0x3F) )
1247             }exg;
1248             }
1249              
1250             sub get_jcode_name {
1251 0     0 0 0 my $src = shift;
1252 0         0 my $dst;
1253 0 0       0 if ( $src =~ /^utf-?8$/i ) {
    0          
    0          
    0          
1254 0         0 $dst = 'utf8';
1255             }
1256             elsif ( $src =~ /^euc.*jp(-?(win|ms))?$/i ) {
1257 0         0 $dst = 'euc';
1258             }
1259             elsif ( $src =~ /^(shift.*jis|cp932|windows-31j)$/i ) {
1260 0         0 $dst = 'sjis';
1261             }
1262             elsif ( $src =~ /^iso-2022-jp/ ) {
1263 0         0 $dst = 'jis';
1264             }
1265 0         0 $dst;
1266             }
1267              
1268             sub xml_escape {
1269 265     265 0 268 my $str = shift;
1270 265 100       416 return '' unless defined $str;
1271             # except for TAB(\x09),CR(\x0D),LF(\x0A)
1272 264         392 $str =~ s{
1273             ([\x00-\x08\x0B\x0C\x0E-\x1F\x7F])
1274             }{
1275 8         26 sprintf( '&#%d;', ord($1) );
1276             }gex;
1277 264         449 $str =~ s/&(?!#(\d+;|x[\dA-Fa-f]+;))/&/g;
1278 264         245 $str =~ s/
1279 264         278 $str =~ s/>/>/g;
1280 264         243 $str =~ s/'/'/g;
1281 264         249 $str =~ s/"/"/g;
1282 264         352 $str;
1283             }
1284              
1285             sub xml_unescape {
1286 973     973 0 983 my $str = shift;
1287 973         2430 my $map = {qw( quot " lt < gt > apos ' amp & )};
1288 973         1498 $str =~ s{
1289             (&(?:\#(\d{1,3})|\#x([0-9a-fA-F]{1,2})|(quot|lt|gt|apos|amp));)
1290             }{
1291 6325 100       14860 $4 ? $map->{$4} : &code_to_ascii( $3 ? hex($3) : $2, $1 );
    100          
1292             }gex;
1293 973         2301 $str;
1294             }
1295              
1296             sub xml_deref_octet {
1297 36     36 0 48 my $str = shift;
1298 36         158 my $map = {qw( quot " lt < gt > apos ' amp & )};
1299 36         123 $str =~ s{
1300             (&(?:\#(\d{1,7})|\#x([0-9a-fA-F]{1,6})|(quot|lt|gt|apos|amp));)
1301             }{
1302 36 100       119 $4 ? $map->{$4} : &code_to_utf8( $3 ? hex($3) : $2, $1 );
    50          
1303             }gex;
1304 36         112 $str;
1305             }
1306              
1307             sub xml_deref_string {
1308 42     42 0 40 my $str = shift;
1309 42         146 my $map = {qw( quot " lt < gt > apos ' amp & )};
1310 42         102 $str =~ s{
1311             (&(?:\#(\d{1,7})|\#x([0-9a-fA-F]{1,6})|(quot|lt|gt|apos|amp));)
1312             }{
1313 30 100       116 $4 ? $map->{$4} : pack( U => $3 ? hex($3) : $2 );
    50          
1314             }gex;
1315 42         93 $str;
1316             }
1317              
1318             sub code_to_ascii {
1319 185     185 0 166 my $code = shift;
1320 185 100       278 if ( $code <= 0x007F ) {
1321 158         507 return pack( C => $code );
1322             }
1323 27 50       101 return shift if scalar @_; # default value
1324 0         0 sprintf( '&#%d;', $code );
1325             }
1326              
1327             sub code_to_utf8 {
1328 36     36 0 41 my $code = shift;
1329 36 100       88 if ( $code <= 0x007F ) {
    100          
    50          
    0          
1330 12         45 return pack( C => $code );
1331             }
1332             elsif ( $code <= 0x07FF ) {
1333 12         51 return pack( C2 => 0xC0|($code>>6), 0x80|($code&0x3F));
1334             }
1335             elsif ( $code <= 0xFFFF ) {
1336 12         61 return pack( C3 => 0xE0|($code>>12), 0x80|(($code>>6)&0x3F), 0x80|($code&0x3F));
1337             }
1338             elsif ( $code <= 0x10FFFF ) {
1339 0           return pack( C4 => 0xF0|($code>>18), 0x80|(($code>>12)&0x3F), 0x80|(($code>>6)&0x3F), 0x80|($code&0x3F));
1340             }
1341 0 0         return shift if scalar @_; # default value
1342 0           sprintf( '&#x%04X;', $code );
1343             }
1344              
1345             1;