File Coverage

blib/lib/XML/TreePP.pm
Criterion Covered Total %
statement 410 548 74.8
branch 259 406 63.7
condition 64 120 53.3
subroutine 30 41 73.1
pod 8 37 21.6
total 771 1152 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 COPYRIGHT
423              
424             The following copyright notice applies to all the files provided in
425             this distribution, including binary files, unless explicitly noted
426             otherwise.
427              
428             Copyright 2006-2010 Yusuke Kawasaki
429              
430             =head1 LICENSE
431              
432             This library is free software; you can redistribute it and/or modify
433             it under the same terms as Perl itself.
434              
435             =cut
436              
437             package XML::TreePP;
438 43     43   1024829 use strict;
  43         105  
  43         1936  
439 43     43   243 use Carp;
  43         87  
  43         4240  
440 43     43   41638 use Symbol;
  43         64934  
  43         3397  
441              
442 43     43   287 use vars qw( $VERSION );
  43         90  
  43         495112  
443             $VERSION = '0.42';
444              
445             my $XML_ENCODING = 'UTF-8';
446             my $INTERNAL_ENCODING = 'UTF-8';
447             my $USER_AGENT = 'XML-TreePP/'.$VERSION.' ';
448             my $ATTR_PREFIX = '-';
449             my $TEXT_NODE_KEY = '#text';
450             my $USE_ENCODE_PM = ( $] >= 5.008 );
451             my $ALLOW_UTF8_FLAG = ( $] >= 5.008001 );
452              
453             my $EMPTY_ELEMENT_TAG_END = ' />';
454              
455             sub new {
456 93     93 1 235401 my $package = shift;
457 93         304 my $self = {@_};
458 93         387 bless $self, $package;
459 93         290 $self;
460             }
461              
462             sub die {
463 17     17 0 29 my $self = shift;
464 17         25 my $mess = shift;
465 17 100       70 return if $self->{ignore_error};
466 13         2094 Carp::croak $mess;
467             }
468              
469             sub warn {
470 2     2 0 4 my $self = shift;
471 2         4 my $mess = shift;
472 2 50       7 return if $self->{ignore_error};
473 2         355 Carp::carp $mess;
474             }
475              
476             sub set {
477 34     34 1 22703 my $self = shift;
478 34         66 my $key = shift;
479 34         61 my $val = shift;
480 34 100       4150 if ( defined $val ) {
481 31         158 $self->{$key} = $val;
482             }
483             else {
484 3         35 delete $self->{$key};
485             }
486             }
487              
488             sub get {
489 0     0 1 0 my $self = shift;
490 0         0 my $key = shift;
491 0 0       0 $self->{$key} if exists $self->{$key};
492             }
493              
494             sub writefile {
495 3     3 1 18 my $self = shift;
496 3         6 my $file = shift;
497 3 50       16 my $tree = shift or return $self->die( 'Invalid tree' );
498 3         6 my $encode = shift;
499 3 50       11 return $self->die( 'Invalid filename' ) unless defined $file;
500 3         11 my $text = $self->write( $tree, $encode );
501 3 100 66     23 if ( $ALLOW_UTF8_FLAG && utf8::is_utf8( $text ) ) {
502 1         4 utf8::encode( $text );
503             }
504 3         13 $self->write_raw_xml( $file, $text );
505             }
506              
507             sub write {
508 168     168 1 251907 my $self = shift;
509 168 100       670 my $tree = shift or return $self->die( 'Invalid tree' );
510 165   33     1034 my $from = $self->{internal_encoding} || $INTERNAL_ENCODING;
511 165   66     1249 my $to = shift || $self->{output_encoding} || $XML_ENCODING;
512 165         342 my $decl = $self->{xml_decl};
513 165 100       647 $decl = '' unless defined $decl;
514              
515 165         453 local $self->{__first_out};
516 165 100       513 if ( exists $self->{first_out} ) {
517 6         16 my $keys = $self->{first_out};
518 6 50       21 $keys = [$keys] unless ref $keys;
519 6         30 $self->{__first_out} = { map { $keys->[$_] => $_ } 0 .. $#$keys };
  42         119  
520             }
521              
522 165         370 local $self->{__last_out};
523 165 100       565 if ( exists $self->{last_out} ) {
524 2         4 my $keys = $self->{last_out};
525 2 50       5 $keys = [$keys] unless ref $keys;
526 2         5 $self->{__last_out} = { map { $keys->[$_] => $_ } 0 .. $#$keys };
  6         16  
527             }
528              
529 165 100       470 my $tnk = $self->{text_node_key} if exists $self->{text_node_key};
530 165 100       443 $tnk = $TEXT_NODE_KEY unless defined $tnk;
531 165         398 local $self->{text_node_key} = $tnk;
532              
533 165 100       559 my $apre = $self->{attr_prefix} if exists $self->{attr_prefix};
534 165 100       539 $apre = $ATTR_PREFIX unless defined $apre;
535 165         653 local $self->{__attr_prefix_len} = length($apre);
536 165         348 local $self->{__attr_prefix_rex} = $apre;
537              
538 165         403 local $self->{__indent};
539 165 100 100     750 if ( exists $self->{indent} && $self->{indent} ) {
540 2         9 $self->{__indent} = ' ' x $self->{indent};
541             }
542              
543 165 100       836 if ( ! UNIVERSAL::isa( $tree, 'HASH' )) {
544 3         8 return $self->die( 'Invalid tree' );
545             }
546              
547 162         623 my $text = $self->hash_to_xml( undef, $tree );
548 162 50 33     961 if ( $from && $to ) {
549 162         662 my $stat = $self->encode_from_to( \$text, $from, $to );
550 160 50       486 return $self->die( "Unsupported encoding: $to" ) unless $stat;
551             }
552              
553 160 100       439 return $text if ( $decl eq '' );
554 159         1184 join( "\n", $decl, $text );
555             }
556              
557             sub parsehttp {
558 0     0 1 0 my $self = shift;
559              
560 0         0 local $self->{__user_agent};
561 0 0       0 if ( exists $self->{user_agent} ) {
562 0         0 my $agent = $self->{user_agent};
563 0 0       0 $agent .= $USER_AGENT if ( $agent =~ /\s$/s );
564 0 0       0 $self->{__user_agent} = $agent if ( $agent ne '' );
565             } else {
566 0         0 $self->{__user_agent} = $USER_AGENT;
567             }
568              
569 0         0 my $http = $self->{__http_module};
570 0 0       0 unless ( $http ) {
571 0         0 $http = $self->find_http_module(@_);
572 0         0 $self->{__http_module} = $http;
573             }
574 0 0       0 if ( $http eq 'LWP::UserAgent' ) {
    0          
575 0         0 return $self->parsehttp_lwp(@_);
576             }
577             elsif ( $http eq 'HTTP::Lite' ) {
578 0         0 return $self->parsehttp_lite(@_);
579             }
580             else {
581 0         0 return $self->die( "LWP::UserAgent or HTTP::Lite is required: $_[1]" );
582             }
583             }
584              
585             sub find_http_module {
586 0   0 0 0 0 my $self = shift || {};
587              
588 0 0 0     0 if ( exists $self->{lwp_useragent} && ref $self->{lwp_useragent} ) {
589 0 0       0 return 'LWP::UserAgent' if defined $LWP::UserAgent::VERSION;
590 0 0       0 return 'LWP::UserAgent' if &load_lwp_useragent();
591 0         0 return $self->die( "LWP::UserAgent is required: $_[1]" );
592             }
593              
594 0 0 0     0 if ( exists $self->{http_lite} && ref $self->{http_lite} ) {
595 0 0       0 return 'HTTP::Lite' if defined $HTTP::Lite::VERSION;
596 0 0       0 return 'HTTP::Lite' if &load_http_lite();
597 0         0 return $self->die( "HTTP::Lite is required: $_[1]" );
598             }
599              
600 0 0       0 return 'LWP::UserAgent' if defined $LWP::UserAgent::VERSION;
601 0 0       0 return 'HTTP::Lite' if defined $HTTP::Lite::VERSION;
602 0 0       0 return 'LWP::UserAgent' if &load_lwp_useragent();
603 0 0       0 return 'HTTP::Lite' if &load_http_lite();
604 0         0 return $self->die( "LWP::UserAgent or HTTP::Lite is required: $_[1]" );
605             }
606              
607             sub load_lwp_useragent {
608 0 0   0 0 0 return $LWP::UserAgent::VERSION if defined $LWP::UserAgent::VERSION;
609 0         0 local $@;
610 0         0 eval { require LWP::UserAgent; };
  0         0  
611 0         0 $LWP::UserAgent::VERSION;
612             }
613              
614             sub load_http_lite {
615 0 0   0 0 0 return $HTTP::Lite::VERSION if defined $HTTP::Lite::VERSION;
616 0         0 local $@;
617 0         0 eval { require HTTP::Lite; };
  0         0  
618 0         0 $HTTP::Lite::VERSION;
619             }
620              
621             sub load_tie_ixhash {
622 0 0   0 0 0 return $Tie::IxHash::VERSION if defined $Tie::IxHash::VERSION;
623 0         0 local $@;
624 0         0 eval { require Tie::IxHash; };
  0         0  
625 0         0 $Tie::IxHash::VERSION;
626             }
627              
628             sub parsehttp_lwp {
629 0     0 0 0 my $self = shift;
630 0 0       0 my $method = shift or return $self->die( 'Invalid HTTP method' );
631 0 0       0 my $url = shift or return $self->die( 'Invalid URL' );
632 0         0 my $body = shift;
633 0         0 my $header = shift;
634              
635 0 0       0 my $ua = $self->{lwp_useragent} if exists $self->{lwp_useragent};
636 0 0       0 if ( ! ref $ua ) {
637 0         0 $ua = LWP::UserAgent->new();
638 0         0 $ua->timeout(10);
639 0         0 $ua->env_proxy();
640 0 0       0 $ua->agent( $self->{__user_agent} ) if defined $self->{__user_agent};
641             } else {
642 0 0       0 $ua->agent( $self->{__user_agent} ) if exists $self->{user_agent};
643             }
644              
645 0         0 my $req = HTTP::Request->new( $method, $url );
646 0         0 my $ct = 0;
647 0 0       0 if ( ref $header ) {
648 0         0 foreach my $field ( sort keys %$header ) {
649 0         0 my $value = $header->{$field};
650 0         0 $req->header( $field => $value );
651 0 0       0 $ct ++ if ( $field =~ /^Content-Type$/i );
652             }
653             }
654 0 0 0     0 if ( defined $body && ! $ct ) {
655 0         0 $req->header( 'Content-Type' => 'application/x-www-form-urlencoded' );
656             }
657 0 0       0 $req->add_content_utf8($body) if defined $body;
658 0         0 my $res = $ua->request($req);
659 0         0 my $code = $res->code();
660 0         0 my $text;
661 0 0       0 if ( $res->can( 'decoded_content' )) {
662 0         0 $text = $res->decoded_content( charset => 'none' );
663             } else {
664 0         0 $text = $res->content(); # less than LWP 5.802
665             }
666 0 0       0 my $tree = $self->parse( \$text ) if $res->is_success();
667 0 0       0 wantarray ? ( $tree, $text, $code ) : $tree;
668             }
669              
670             sub parsehttp_lite {
671 0     0 0 0 my $self = shift;
672 0 0       0 my $method = shift or return $self->die( 'Invalid HTTP method' );
673 0 0       0 my $url = shift or return $self->die( 'Invalid URL' );
674 0         0 my $body = shift;
675 0         0 my $header = shift;
676              
677 0         0 my $http = HTTP::Lite->new();
678 0         0 $http->method($method);
679 0         0 my $ua = 0;
680 0 0       0 if ( ref $header ) {
681 0         0 foreach my $field ( sort keys %$header ) {
682 0         0 my $value = $header->{$field};
683 0         0 $http->add_req_header( $field, $value );
684 0 0       0 $ua ++ if ( $field =~ /^User-Agent$/i );
685             }
686             }
687 0 0 0     0 if ( defined $self->{__user_agent} && ! $ua ) {
688 0         0 $http->add_req_header( 'User-Agent', $self->{__user_agent} );
689             }
690 0 0       0 $http->{content} = $body if defined $body;
691 0 0       0 my $code = $http->request($url) or return;
692 0         0 my $text = $http->body();
693 0         0 my $tree = $self->parse( \$text );
694 0 0       0 wantarray ? ( $tree, $text, $code ) : $tree;
695             }
696              
697             sub parsefile {
698 33     33 1 18401 my $self = shift;
699 33         64 my $file = shift;
700 33 50       115 return $self->die( 'Invalid filename' ) unless defined $file;
701 33         110 my $text = $self->read_raw_xml($file);
702 32         132 $self->parse( \$text );
703             }
704              
705             sub parse {
706 274     274 1 72087 my $self = shift;
707 274 100       808 my $text = ref $_[0] ? ${$_[0]} : $_[0];
  32         70  
708 274 100       953 return $self->die( 'Null XML source' ) unless defined $text;
709              
710 273   66     1205 my $from = &xml_decl_encoding(\$text) || $XML_ENCODING;
711 273   33     1406 my $to = $self->{internal_encoding} || $INTERNAL_ENCODING;
712 273 50 33     2923 if ( $from && $to ) {
713 273         843 my $stat = $self->encode_from_to( \$text, $from, $to );
714 271 50       840 return $self->die( "Unsupported encoding: $from" ) unless $stat;
715             }
716              
717 271         753 local $self->{__force_array};
718 271         561 local $self->{__force_array_all};
719 271 100       737 if ( exists $self->{force_array} ) {
720 7         16 my $force = $self->{force_array};
721 7 100       28 $force = [$force] unless ref $force;
722 7         18 $self->{__force_array} = { map { $_ => 1 } @$force };
  15         56  
723 7         29 $self->{__force_array_all} = $self->{__force_array}->{'*'};
724             }
725              
726 271         533 local $self->{__force_hash};
727 271         483 local $self->{__force_hash_all};
728 271 100       713 if ( exists $self->{force_hash} ) {
729 2         4 my $force = $self->{force_hash};
730 2 100       8 $force = [$force] unless ref $force;
731 2         5 $self->{__force_hash} = { map { $_ => 1 } @$force };
  4         15  
732 2         6 $self->{__force_hash_all} = $self->{__force_hash}->{'*'};
733             }
734              
735 271 100       646 my $tnk = $self->{text_node_key} if exists $self->{text_node_key};
736 271 100       642 $tnk = $TEXT_NODE_KEY unless defined $tnk;
737 271         685 local $self->{text_node_key} = $tnk;
738              
739 271 100       760 my $apre = $self->{attr_prefix} if exists $self->{attr_prefix};
740 271 100       567 $apre = $ATTR_PREFIX unless defined $apre;
741 271         610 local $self->{attr_prefix} = $apre;
742              
743 271 50 33     831 if ( exists $self->{use_ixhash} && $self->{use_ixhash} ) {
744 0 0       0 return $self->die( "Tie::IxHash is required." ) unless &load_tie_ixhash();
745             }
746              
747             # Avoid segfaults when receving random input (RT #42441)
748 271 100 66     810 if ( exists $self->{require_xml_decl} && $self->{require_xml_decl} ) {
749 3 100       9 return $self->die( "XML declaration not found" ) unless looks_like_xml(\$text);
750             }
751              
752 269         839 my $flat = $self->xml_to_flat(\$text);
753 269 100       937 my $class = $self->{base_class} if exists $self->{base_class};
754 269         861 my $tree = $self->flat_to_tree( $flat, '', $class );
755 268 50       706 if ( ref $tree ) {
756 268 100 66     1301 if ( defined $class ) {
    100          
757 1         114 bless( $tree, $class );
758             }
759             elsif ( exists $self->{elem_class} && $self->{elem_class} ) {
760 1         45 bless( $tree, $self->{elem_class} );
761             }
762             }
763 268 50       3085 wantarray ? ( $tree, $text ) : $tree;
764             }
765              
766             sub xml_to_flat {
767 269     269 0 466 my $self = shift;
768 269         336 my $textref = shift; # reference
769 269         479 my $flat = [];
770 269         492 my $prefix = $self->{attr_prefix};
771 269   33     788 my $ixhash = ( exists $self->{use_ixhash} && $self->{use_ixhash} );
772              
773 269         487 my $deref = \&xml_unescape;
774 269   100     783 my $xml_deref = ( exists $self->{xml_deref} && $self->{xml_deref} );
775 269 100       543 if ( $xml_deref ) {
776 13 100 100     119 if (( exists $self->{utf8_flag} && $self->{utf8_flag} ) ||
      66        
      66        
777             ( $ALLOW_UTF8_FLAG && utf8::is_utf8( $$textref ))) {
778 7         19 $deref = \&xml_deref_string;
779             } else {
780 6         15 $deref = \&xml_deref_octet;
781             }
782             }
783              
784 269         3012 while ( $$textref =~ m{
785             ([^<]*) <
786             ((
787             \? ([^<>]*) \?
788             )|(
789             \!\[CDATA\[(.*?)\]\]
790             )|(
791             \!DOCTYPE\s+([^\[\]<>]*(?:\[.*?\]\s*)?)
792             )|(
793             \!--(.*?)--
794             )|(
795             ([^\!\?\s<>](?:"[^"]*"|'[^']*'|[^"'<>])*)
796             ))
797             > ([^<]*)
798             }sxg ) {
799             my (
800 2358         14686 $ahead, $match, $typePI, $contPI, $typeCDATA,
801             $contCDATA, $typeDocT, $contDocT, $typeCmnt, $contCmnt,
802             $typeElem, $contElem, $follow
803             )
804             = ( $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13 );
805 2358 50 33     10585 if ( defined $ahead && $ahead =~ /\S/ ) {
806 0         0 $ahead =~ s/([^\040-\076])/sprintf("\\x%02X",ord($1))/eg;
  0         0  
807 0         0 $self->warn( "Invalid string: [$ahead] before <$match>" );
808             }
809              
810 2358 100       4675 if ($typeElem) { # Element
    100          
    100          
    50          
    50          
811 2043         3164 my $node = {};
812 2043 100       7976 if ( $contElem =~ s#^/## ) {
    100          
813 960         2301 $node->{endTag}++;
814             }
815             elsif ( $contElem =~ s#/$## ) {
816 123         275 $node->{emptyTag}++;
817             }
818             else {
819 960         2643 $node->{startTag}++;
820             }
821 2043 50       15670 $node->{tagName} = $1 if ( $contElem =~ s#^(\S+)\s*## );
822 2043 100       5614 unless ( $node->{endTag} ) {
823 1083         1361 my $attr;
824 1083         3236 while ( $contElem =~ m{
825             ([^\s\=\"\']+)\s*=\s*(?:(")(.*?)"|'(.*?)')
826             }sxg ) {
827 353         580 my $key = $1;
828 353 100       1191 my $val = &$deref( $2 ? $3 : $4 );
829 353 100       1005 if ( ! ref $attr ) {
830 235         411 $attr = {};
831 235 50       583 tie( %$attr, 'Tie::IxHash' ) if $ixhash;
832             }
833 353         1769 $attr->{$prefix.$key} = $val;
834             }
835 1083 100       2946 $node->{attributes} = $attr if ref $attr;
836             }
837 2043         9115 push( @$flat, $node );
838             }
839             elsif ($typeCDATA) { ## CDATASection
840 177 100 100     701 if ( exists $self->{cdata_scalar_ref} && $self->{cdata_scalar_ref} ) {
841 114         195 push( @$flat, \$contCDATA ); # as reference for scalar
842             }
843             else {
844 63         133 push( @$flat, $contCDATA ); # as scalar like text node
845             }
846             }
847             elsif ($typeCmnt) { # Comment (ignore)
848             }
849             elsif ($typeDocT) { # DocumentType (ignore)
850             }
851             elsif ($typePI) { # ProcessingInstruction (ignore)
852             }
853             else {
854 0         0 $self->warn( "Invalid Tag: <$match>" );
855             }
856 2358 100       19718 if ( $follow =~ /\S/ ) { # text node
857 698         1360 my $val = &$deref($follow);
858 698         5534 push( @$flat, $val );
859             }
860             }
861 269         941 $flat;
862             }
863              
864             sub flat_to_tree {
865 1229     1229 0 1644 my $self = shift;
866 1229         1458 my $source = shift;
867 1229         1750 my $parent = shift;
868 1229         1324 my $class = shift;
869 1229         3006 my $tree = {};
870 1229         1835 my $text = [];
871              
872 1229 50 33     3463 if ( exists $self->{use_ixhash} && $self->{use_ixhash} ) {
873 0         0 tie( %$tree, 'Tie::IxHash' );
874             }
875              
876 1229         2737 while ( scalar @$source ) {
877 2917         4264 my $node = shift @$source;
878 2917 100 100     13481 if ( !ref $node || UNIVERSAL::isa( $node, "SCALAR" ) ) {
879 875         1629 push( @$text, $node ); # cdata or text node
880 875         2641 next;
881             }
882 2042         3477 my $name = $node->{tagName};
883 2042 100       7375 if ( $node->{endTag} ) {
884 959 100       3434 last if ( $parent eq $name );
885 2         15 return $self->die( "Invalid tag sequence: <$parent>" );
886             }
887 1083         1374 my $elem = $node->{attributes};
888 1083   100     4429 my $forcehash = $self->{__force_hash_all} || $self->{__force_hash}->{$name};
889 1083         1185 my $subclass;
890 1083 100       2098 if ( defined $class ) {
891 15         17 my $escname = $name;
892 15         23 $escname =~ s/\W/_/sg;
893 15         32 $subclass = $class.'::'.$escname;
894             }
895 1083 100 66     2223 if ( $node->{startTag} ) { # recursive call
    100          
896 960         2874 my $child = $self->flat_to_tree( $source, $name, $subclass );
897 958 100       2248 next unless defined $child;
898 957 100       1972 my $hasattr = scalar keys %$elem if ref $elem;
899 957 100       4134 if ( UNIVERSAL::isa( $child, "HASH" ) ) {
900 189 100       370 if ( $hasattr ) {
901             # some attributes and some child nodes
902 92         1421 %$elem = ( %$elem, %$child );
903             }
904             else {
905             # some child nodes without attributes
906 97         164 $elem = $child;
907             }
908             }
909             else {
910 768 100       1697 if ( $hasattr ) {
    100          
911             # some attributes and text node
912 31         110 $elem->{$self->{text_node_key}} = $child;
913             }
914             elsif ( $forcehash ) {
915             # only text node without attributes
916 7         21 $elem = { $self->{text_node_key} => $child };
917             }
918             else {
919             # text node without attributes
920 730         1334 $elem = $child;
921             }
922             }
923             }
924             elsif ( $forcehash && ! ref $elem ) {
925 2         3 $elem = {};
926             }
927             # bless to a class by base_class or elem_class
928 1080 100 100     4150 if ( ref $elem && UNIVERSAL::isa( $elem, "HASH" ) ) {
929 341 100 66     1863 if ( defined $subclass ) {
    100          
930 10         43 bless( $elem, $subclass );
931             } elsif ( exists $self->{elem_class} && $self->{elem_class} ) {
932 10         15 my $escname = $name;
933 10         52 $escname =~ s/\W/_/sg;
934 10         24 my $elmclass = $self->{elem_class}.'::'.$escname;
935 10         68 bless( $elem, $elmclass );
936             }
937             }
938             # next unless defined $elem;
939 1080   100     5106 $tree->{$name} ||= [];
940 1080         1640 push( @{ $tree->{$name} }, $elem );
  1080         5119  
941             }
942 1225 100       2842 if ( ! $self->{__force_array_all} ) {
943 1220         16343 foreach my $key ( keys %$tree ) {
944 928 100       2399 next if $self->{__force_array}->{$key};
945 920 100       1012 next if ( 1 < scalar @{ $tree->{$key} } );
  920         3033  
946 899         1088 $tree->{$key} = shift @{ $tree->{$key} };
  899         2741  
947             }
948             }
949 1225         3246 my $haschild = scalar keys %$tree;
950 1225 100       2965 if ( scalar @$text ) {
    100          
951 752 100       1964 if ( scalar @$text == 1 ) {
  198 100       422  
952             # one text node (normal)
953 677         1003 $text = shift @$text;
954             }
955             elsif ( ! scalar grep {ref $_} @$text ) {
956             # some text node splitted
957 36         102 $text = join( '', @$text );
958             }
959             else {
960             # some cdata node
961 39 100       69 my $join = join( '', map {ref $_ ? $$_ : $_} @$text );
  103         288  
962 39         81 $text = \$join;
963             }
964 752 100       1618 if ( $haschild ) {
965             # some child nodes and also text node
966 12         33 $tree->{$self->{text_node_key}} = $text;
967             }
968             else {
969             # only text node without child nodes
970 740         997 $tree = $text;
971             }
972             }
973             elsif ( ! $haschild ) {
974             # no child and no text
975 28         47 $tree = "";
976             }
977 1225         3242 $tree;
978             }
979              
980             sub hash_to_xml {
981 306     306 0 463 my $self = shift;
982 306         420 my $name = shift;
983 306         352 my $hash = shift;
984 306         469 my $out = [];
985 306         459 my $attr = [];
986 306         1332 my $allkeys = [ keys %$hash ];
987 306 100       1000 my $fo = $self->{__first_out} if ref $self->{__first_out};
988 306 100       759 my $lo = $self->{__last_out} if ref $self->{__last_out};
989 306 100       655 my $firstkeys = [ sort { $fo->{$a} <=> $fo->{$b} } grep { exists $fo->{$_} } @$allkeys ] if ref $fo;
  33         76  
  69         187  
990 306 100       592 my $lastkeys = [ sort { $lo->{$a} <=> $lo->{$b} } grep { exists $lo->{$_} } @$allkeys ] if ref $lo;
  6         12  
  20         35  
991 306 100       629 $allkeys = [ grep { ! exists $fo->{$_} } @$allkeys ] if ref $fo;
  69         157  
992 306 100       720 $allkeys = [ grep { ! exists $lo->{$_} } @$allkeys ] if ref $lo;
  14         27  
993 306 50 33     990 unless ( exists $self->{use_ixhash} && $self->{use_ixhash} ) {
994 306         1063 $allkeys = [ sort @$allkeys ];
995             }
996 306         735 my $prelen = $self->{__attr_prefix_len};
997 306         448 my $pregex = $self->{__attr_prefix_rex};
998 306         470 my $textnk = $self->{text_node_key};
999 306   66     1244 my $tagend = $self->{empty_element_tag_end} || $EMPTY_ELEMENT_TAG_END;
1000              
1001 306         647 foreach my $keys ( $firstkeys, $allkeys, $lastkeys ) {
1002 918 100       2580 next unless ref $keys;
1003 339 100       922 my $elemkey = $prelen ? [ grep { substr($_,0,$prelen) ne $pregex } @$keys ] : $keys;
  451         1630  
1004 339 100       771 my $attrkey = $prelen ? [ grep { substr($_,0,$prelen) eq $pregex } @$keys ] : [];
  451         1437  
1005              
1006 339         697 foreach my $key ( @$elemkey ) {
1007 406         721 my $val = $hash->{$key};
1008 406 100       3037 if ( !defined $val ) {
    100          
    100          
    100          
1009 7 100       26 next if ($key eq $textnk);
1010 6         31 push( @$out, "<$key$tagend" );
1011             }
1012             elsif ( UNIVERSAL::isa( $val, 'HASH' ) ) {
1013 132         933 my $child = $self->hash_to_xml( $key, $val );
1014 132         490 push( @$out, $child );
1015             }
1016             elsif ( UNIVERSAL::isa( $val, 'ARRAY' ) ) {
1017 8         29 my $child = $self->array_to_xml( $key, $val );
1018 8         21 push( @$out, $child );
1019             }
1020             elsif ( UNIVERSAL::isa( $val, 'SCALAR' ) ) {
1021 50         180 my $child = $self->scalaref_to_cdata( $key, $val );
1022 50         165 push( @$out, $child );
1023             }
1024             else {
1025 209         358 my $ref = ref $val;
1026 209 100       439 $self->warn( "Unsupported reference type: $ref in $key" ) if $ref;
1027 209         745 my $child = $self->scalar_to_xml( $key, $val );
1028 209         1129 push( @$out, $child );
1029             }
1030             }
1031              
1032 339         959 foreach my $key ( @$attrkey ) {
1033 53         114 my $name = substr( $key, $prelen );
1034 53         142 my $val = &xml_escape( $hash->{$key} );
1035 53         316 push( @$attr, ' ' . $name . '="' . $val . '"' );
1036             }
1037             }
1038 306         750 my $jattr = join( '', @$attr );
1039              
1040 306 100 100     1312 if ( defined $name && scalar @$out && ! grep { ! /^
  243   100     1114  
1041             # Use human-friendly white spacing
1042 103 100       545 if ( defined $self->{__indent} ) {
1043 6         120 s/^(\s*<)/$self->{__indent}$1/mg foreach @$out;
1044             }
1045 103         261 unshift( @$out, "\n" );
1046             }
1047              
1048 306         693 my $text = join( '', @$out );
1049 306 100       739 if ( defined $name ) {
1050 144 100       329 if ( scalar @$out ) {
1051 126         548 $text = "<$name$jattr>$text\n";
1052             }
1053             else {
1054 18         52 $text = "<$name$jattr$tagend\n";
1055             }
1056             }
1057 306         1106 $text;
1058             }
1059              
1060             sub array_to_xml {
1061 8     8 0 18 my $self = shift;
1062 8         14 my $name = shift;
1063 8         11 my $array = shift;
1064 8         14 my $out = [];
1065 8   33     59 my $tagend = $self->{empty_element_tag_end} || $EMPTY_ELEMENT_TAG_END;
1066              
1067 8         17 foreach my $val (@$array) {
1068 16 100       86 if ( !defined $val ) {
    100          
    50          
    50          
1069 1         5 push( @$out, "<$name$tagend\n" );
1070             }
1071             elsif ( UNIVERSAL::isa( $val, 'HASH' ) ) {
1072 12         40 my $child = $self->hash_to_xml( $name, $val );
1073 12         34 push( @$out, $child );
1074             }
1075             elsif ( UNIVERSAL::isa( $val, 'ARRAY' ) ) {
1076 0         0 my $child = $self->array_to_xml( $name, $val );
1077 0         0 push( @$out, $child );
1078             }
1079             elsif ( UNIVERSAL::isa( $val, 'SCALAR' ) ) {
1080 0         0 my $child = $self->scalaref_to_cdata( $name, $val );
1081 0         0 push( @$out, $child );
1082             }
1083             else {
1084 3         14 my $ref = ref $val;
1085 3 50       8 $self->warn( "Unsupported reference type: $ref in $name" ) if $ref;
1086 3         10 my $child = $self->scalar_to_xml( $name, $val );
1087 3         10 push( @$out, $child );
1088             }
1089             }
1090              
1091 8         40 my $text = join( '', @$out );
1092 8         23 $text;
1093             }
1094              
1095             sub scalaref_to_cdata {
1096 50     50 0 71 my $self = shift;
1097 50         78 my $name = shift;
1098 50         70 my $ref = shift;
1099 50 100       157 my $data = defined $$ref ? $$ref : '';
1100 50         215 $data =~ s#(]])(>)#$1]]>
1101 50         127 my $text = '';
1102 50 100       227 $text = "<$name>$text\n" if ( $name ne $self->{text_node_key} );
1103 50         130 $text;
1104             }
1105              
1106             sub scalar_to_xml {
1107 212     212 0 296 my $self = shift;
1108 212         262 my $name = shift;
1109 212         336 my $scalar = shift;
1110 212         263 my $copy = $scalar;
1111 212         509 my $text = &xml_escape($copy);
1112 212 100       976 $text = "<$name>$text\n" if ( $name ne $self->{text_node_key} );
1113 212         470 $text;
1114             }
1115              
1116             sub write_raw_xml {
1117 3     3 0 5 my $self = shift;
1118 3         5 my $file = shift;
1119 3         15 my $fh = Symbol::gensym();
1120 3 50       362 open( $fh, ">$file" ) or return $self->die( "$! - $file" );
1121 3         37 print $fh @_;
1122 3         158 close($fh);
1123             }
1124              
1125             sub read_raw_xml {
1126 33     33 0 51 my $self = shift;
1127 33         47 my $file = shift;
1128 33         118 my $fh = Symbol::gensym();
1129 33 100       2700 open( $fh, $file ) or return $self->die( "$! - $file" );
1130 31         151 local $/ = undef;
1131 31         1260 my $text = <$fh>;
1132 31         381 close($fh);
1133 31         194 $text;
1134             }
1135              
1136             sub looks_like_xml {
1137 276     276 0 388 my $textref = shift;
1138 276         2748 my $args = ( $$textref =~ /^(?:\s*\xEF\xBB\xBF)?\s*<\?xml(\s+\S.*)\?>/s )[0];
1139 276 100       766 if ( ! $args ) {
1140 163         1141 return;
1141             }
1142 113         405 return $args;
1143             }
1144              
1145             sub xml_decl_encoding {
1146 273     273 0 369 my $textref = shift;
1147 273 50       651 return unless defined $$textref;
1148 273 100       625 my $args = looks_like_xml($textref) or return;
1149 112 100       857 my $getcode = ( $args =~ /\s+encoding=(".*?"|'.*?')/ )[0] or return;
1150 106         367 $getcode =~ s/^['"]//;
1151 106         334 $getcode =~ s/['"]$//;
1152 106         557 $getcode;
1153             }
1154              
1155             sub encode_from_to {
1156 435     435 0 1154 my $self = shift;
1157 435 50       1295 my $txtref = shift or return;
1158 435 50       1058 my $from = shift or return;
1159 435 50       994 my $to = shift or return;
1160              
1161 435 50       1049 unless ( defined $Encode::EUCJPMS::VERSION ) {
1162 435 50       977 $from = 'EUC-JP' if ( $from =~ /\beuc-?jp-?(win|ms)$/i );
1163 435 50       3424 $to = 'EUC-JP' if ( $to =~ /\beuc-?jp-?(win|ms)$/i );
1164             }
1165              
1166 435         2046 my $RE_IS_UTF8 = qr/^utf-?8$/i;
1167 435 100       3304 if ( $from =~ $RE_IS_UTF8 ) {
1168 425         1134 $$txtref =~ s/^\xEF\xBB\xBF//s; # UTF-8 BOM (Byte Order Mark)
1169             }
1170              
1171 435 100       1321 my $setflag = $self->{utf8_flag} if exists $self->{utf8_flag};
1172 435 50 33     1293 if ( ! $ALLOW_UTF8_FLAG && $setflag ) {
1173 0         0 return $self->die( "Perl 5.8.1 is required for utf8_flag: $]" );
1174             }
1175              
1176 435 50 0     1129 if ( $USE_ENCODE_PM ) {
    0 0        
1177 435         1073 &load_encode();
1178 435         609435 my $encver = ( $Encode::VERSION =~ /^([\d\.]+)/ )[0];
1179 435 50       3479 my $check = ( $encver < 2.13 ) ? 0x400 : Encode::FB_XMLCREF();
1180              
1181 435 50       2099 my $encfrom = Encode::find_encoding($from) if $from;
1182 435 100       91674 return $self->die( "Unknown encoding: $from" ) unless ref $encfrom;
1183 433 50       1633 my $encto = Encode::find_encoding($to) if $to;
1184 433 100       71341 return $self->die( "Unknown encoding: $to" ) unless ref $encto;
1185              
1186 431 100 66     2856 if ( $ALLOW_UTF8_FLAG && utf8::is_utf8( $$txtref ) ) {
1187 13 50       91 if ( $to =~ $RE_IS_UTF8 ) {
1188             # skip
1189             } else {
1190 0         0 $$txtref = $encto->encode( $$txtref, $check );
1191             }
1192             } else {
1193 418         11127 $$txtref = $encfrom->decode( $$txtref );
1194 418 100 100     12971 if ( $to =~ $RE_IS_UTF8 && $setflag ) {
1195             # skip
1196             } else {
1197 403         8997 $$txtref = $encto->encode( $$txtref, $check );
1198             }
1199             }
1200             }
1201             elsif ( ( uc($from) eq 'ISO-8859-1'
1202             || uc($from) eq 'US-ASCII'
1203             || uc($from) eq 'LATIN-1' ) && uc($to) eq 'UTF-8' ) {
1204 0         0 &latin1_to_utf8($txtref);
1205             }
1206             else {
1207 0         0 my $jfrom = &get_jcode_name($from);
1208 0         0 my $jto = &get_jcode_name($to);
1209 0 0       0 return $to if ( uc($jfrom) eq uc($jto) );
1210 0 0 0     0 if ( $jfrom && $jto ) {
1211 0         0 &load_jcode();
1212 0 0       0 if ( defined $Jcode::VERSION ) {
1213 0         0 Jcode::convert( $txtref, $jto, $jfrom );
1214             }
1215             else {
1216 0         0 return $self->die( "Jcode.pm is required: $from to $to" );
1217             }
1218             }
1219             else {
1220 0         0 return $self->die( "Encode.pm is required: $from to $to" );
1221             }
1222             }
1223 431         1612 $to;
1224             }
1225              
1226             sub load_jcode {
1227 0 0   0 0 0 return if defined $Jcode::VERSION;
1228 0         0 local $@;
1229 0         0 eval { require Jcode; };
  0         0  
1230             }
1231              
1232             sub load_encode {
1233 435 100   435 0 1177 return if defined $Encode::VERSION;
1234 40         88 local $@;
1235 40         98 eval { require Encode; };
  40         56682  
1236             }
1237              
1238             sub latin1_to_utf8 {
1239 0     0 0 0 my $strref = shift;
1240 0         0 $$strref =~ s{
1241             ([\x80-\xFF])
1242             }{
1243 0         0 pack( 'C2' => 0xC0|(ord($1)>>6),0x80|(ord($1)&0x3F) )
1244             }exg;
1245             }
1246              
1247             sub get_jcode_name {
1248 0     0 0 0 my $src = shift;
1249 0         0 my $dst;
1250 0 0       0 if ( $src =~ /^utf-?8$/i ) {
    0          
    0          
    0          
1251 0         0 $dst = 'utf8';
1252             }
1253             elsif ( $src =~ /^euc.*jp(-?(win|ms))?$/i ) {
1254 0         0 $dst = 'euc';
1255             }
1256             elsif ( $src =~ /^(shift.*jis|cp932|windows-31j)$/i ) {
1257 0         0 $dst = 'sjis';
1258             }
1259             elsif ( $src =~ /^iso-2022-jp/ ) {
1260 0         0 $dst = 'jis';
1261             }
1262 0         0 $dst;
1263             }
1264              
1265             sub xml_escape {
1266 265     265 0 352 my $str = shift;
1267 265 100       796 return '' unless defined $str;
1268             # except for TAB(\x09),CR(\x0D),LF(\x0A)
1269 264         708 $str =~ s{
1270             ([\x00-\x08\x0B\x0C\x0E-\x1F\x7F])
1271             }{
1272 8         34 sprintf( '&#%d;', ord($1) );
1273             }gex;
1274 264         3059 $str =~ s/&(?!#(\d+;|x[\dA-Fa-f]+;))/&/g;
1275 264         361 $str =~ s/
1276 264         671 $str =~ s/>/>/g;
1277 264         359 $str =~ s/'/'/g;
1278 264         341 $str =~ s/"/"/g;
1279 264         536 $str;
1280             }
1281              
1282             sub xml_unescape {
1283 973     973 0 1391 my $str = shift;
1284 973         4827 my $map = {qw( quot " lt < gt > apos ' amp & )};
1285 973         2114 $str =~ s{
1286             (&(?:\#(\d{1,3})|\#x([0-9a-fA-F]{1,2})|(quot|lt|gt|apos|amp));)
1287             }{
1288 6325 100       25241 $4 ? $map->{$4} : &code_to_ascii( $3 ? hex($3) : $2, $1 );
    100          
1289             }gex;
1290 973         3813 $str;
1291             }
1292              
1293             sub xml_deref_octet {
1294 36     36 0 48 my $str = shift;
1295 36         191 my $map = {qw( quot " lt < gt > apos ' amp & )};
1296 36         185 $str =~ s{
1297             (&(?:\#(\d{1,7})|\#x([0-9a-fA-F]{1,6})|(quot|lt|gt|apos|amp));)
1298             }{
1299 36 100       158 $4 ? $map->{$4} : &code_to_utf8( $3 ? hex($3) : $2, $1 );
    50          
1300             }gex;
1301 36         156 $str;
1302             }
1303              
1304             sub xml_deref_string {
1305 42     42 0 60 my $str = shift;
1306 42         212 my $map = {qw( quot " lt < gt > apos ' amp & )};
1307 42         161 $str =~ s{
1308             (&(?:\#(\d{1,7})|\#x([0-9a-fA-F]{1,6})|(quot|lt|gt|apos|amp));)
1309             }{
1310 30 100       160 $4 ? $map->{$4} : pack( U => $3 ? hex($3) : $2 );
    50          
1311             }gex;
1312 42         191 $str;
1313             }
1314              
1315             sub code_to_ascii {
1316 185     185 0 263 my $code = shift;
1317 185 100       463 if ( $code <= 0x007F ) {
1318 158         769 return pack( C => $code );
1319             }
1320 27 50       136 return shift if scalar @_; # default value
1321 0         0 sprintf( '&#%d;', $code );
1322             }
1323              
1324             sub code_to_utf8 {
1325 36     36 0 51 my $code = shift;
1326 36 100       94 if ( $code <= 0x007F ) {
    100          
    50          
    0          
1327 12         59 return pack( C => $code );
1328             }
1329             elsif ( $code <= 0x07FF ) {
1330 12         58 return pack( C2 => 0xC0|($code>>6), 0x80|($code&0x3F));
1331             }
1332             elsif ( $code <= 0xFFFF ) {
1333 12         57 return pack( C3 => 0xE0|($code>>12), 0x80|(($code>>6)&0x3F), 0x80|($code&0x3F));
1334             }
1335             elsif ( $code <= 0x10FFFF ) {
1336 0           return pack( C4 => 0xF0|($code>>18), 0x80|(($code>>12)&0x3F), 0x80|(($code>>6)&0x3F), 0x80|($code&0x3F));
1337             }
1338 0 0         return shift if scalar @_; # default value
1339 0           sprintf( '&#x%04X;', $code );
1340             }
1341              
1342             1;