File Coverage

lib/XML/Compile.pm
Criterion Covered Total %
statement 74 102 72.5
branch 23 60 38.3
condition 5 16 31.2
subroutine 16 19 84.2
pod 7 8 87.5
total 125 205 60.9


line stmt bran cond sub pod time code
1             # Copyrights 2006-2024 by [Mark Overmeer ].
2             # For other contributors see ChangeLog.
3             # See the manual pages for details on the licensing terms.
4             # Pod stripped from pm file by OODoc 2.03.
5             # This code is part of distribution XML-Compile. Meta-POD processed with
6             # OODoc into POD and HTML manual-pages. See README.md
7             # Copyright Mark Overmeer. Licensed under the same terms as Perl itself.
8              
9             package XML::Compile;{
10             our $VERSION = '1.64';
11             }
12              
13              
14 50     50   588431 use warnings;
  50         122  
  50         3416  
15 50     50   295 use strict;
  50         96  
  50         1450  
16              
17 50     50   909 use Log::Report 'xml-compile';
  50         150193  
  50         373  
18 50     50   16332 use XML::LibXML;
  50         52422  
  50         505  
19 50     50   11045 use XML::Compile::Util qw/:constants type_of_node/;
  50         100  
  50         6981  
20              
21 50     50   329 use File::Spec qw();
  50         84  
  50         104872  
22              
23             my $parser;
24              
25             __PACKAGE__->knownNamespace
26             ( &XMLNS => '1998-namespace.xsd'
27             , &SCHEMA1999 => '1999-XMLSchema.xsd'
28             , &SCHEMA2000 => '2000-XMLSchema.xsd'
29             , &SCHEMA2001 => '2001-XMLSchema.xsd'
30             , &SCHEMA2001i => '2001-XMLSchema-instance.xsd'
31             , 'http://www.w3.org/1999/part2.xsd'
32             => '1999-XMLSchema-part2.xsd'
33             );
34              
35             __PACKAGE__->addSchemaDirs($ENV{SCHEMA_DIRECTORIES});
36             __PACKAGE__->addSchemaDirs(__FILE__);
37              
38              
39             sub new(@)
40 52     52 1 12896660 { my $class = shift;
41 52 50       361 my $top = @_ % 2 ? shift : undef;
42              
43 52 50       271 $class ne __PACKAGE__
44             or panic "you should instantiate a sub-class, $class is base only";
45              
46 52         614 (bless {}, $class)->init( {top => $top, @_} );
47             }
48              
49             sub init($)
50 52     52 0 168 { my ($self, $args) = @_;
51              
52 52   50     377 my $popts = $args->{parser_options} || [];
53 52 50       539 $self->initParser(ref $popts eq 'HASH' ? %$popts : @$popts);
54              
55 52         7647 $self->addSchemaDirs($args->{schema_dirs});
56 52         178 $self;
57             }
58              
59             #-------------------
60              
61             my @schema_dirs;
62             sub addSchemaDirs(@)
63 152     152 1 329 { my $thing = shift;
64 152         416 foreach (@_)
65 152         389 { my $dir = shift;
66 152 50       606 my @dirs = grep {defined} ref $dir eq 'ARRAY' ? @$dir : $dir;
  152         542  
67 152 50       954 my $sep = $^O eq 'MSWin32' ? qr/\;/ : qr/\:/;
68 152         2007 foreach (map { split $sep } @dirs)
  50         481  
69 50         199 { my $el = $_;
70 50 50       1598 $el = File::Spec->catfile($el, 'xsd') if $el =~ s/\.pm$//i;
71 50         309 push @schema_dirs, $el;
72             }
73             }
74 152 50       524 defined wantarray ? @schema_dirs : ();
75             }
76              
77             #----------------------
78              
79              
80             sub initParser(@)
81 52     52 1 115 { my $thing = shift;
82             $parser = XML::LibXML->new
83             ( line_numbers => 1
84             , no_network => 1
85             , expand_xinclude => 0
86             , expand_entities => 1
87             , load_ext_dtd => 0
88             , ext_ent_handler =>
89 0     0   0 sub { alert __x"parsing external entities disabled"; '' }
  0         0  
90             , @_
91 52         722 );
92             }
93              
94              
95             sub dataToXML($)
96 455     455 1 1628 { my ($thing, $raw) = @_;
97 455 100       1794 defined $raw or return;
98              
99 454   33     1823 $parser ||= $thing->initParser;
100              
101 454         945 my ($xml, %details);
102 454 100 66     7138 if(ref $raw && UNIVERSAL::isa($raw, 'XML::LibXML::Node'))
    50 0        
    50          
    50          
    0          
    0          
    0          
    0          
103 1         8 { ($xml, %details) = $thing->_parsedNode($raw);
104             }
105             elsif(ref $raw eq 'SCALAR') # XML string as ref
106 0         0 { ($xml, %details) = $thing->_parseScalar($raw);
107             }
108             elsif(ref $raw eq 'GLOB') # from file-handle
109 0         0 { ($xml, %details) = $thing->_parseFileHandle($raw);
110             }
111             elsif($raw =~ m/^[\s\x{FFFE}\x{FEFF}]*\
112             { # XML starts with '<', rare for files
113 453         2148 ($xml, %details) = $thing->_parseScalar(\$raw);
114             }
115             elsif(my $known = $thing->knownNamespace($raw))
116 0 0       0 { my $fn = $thing->findSchemaFile($known)
117             or error __x"cannot find pre-installed name-space file named {path} for {name}"
118             , path => $known, name => $raw;
119              
120 0         0 ($xml, %details) = $thing->_parseFile($fn);
121 0         0 $details{source} = "known namespace $raw";
122             }
123             elsif(my $fn = $thing->findSchemaFile($raw))
124 0         0 { ($xml, %details) = $thing->_parseFile($fn);
125 0         0 $details{source} = "filename in schema-dir $raw";
126             }
127             elsif(-f $raw)
128 0         0 { ($xml, %details) = $thing->_parseFile($raw);
129             }
130             elsif($raw !~ /[\n\r<]/ && $raw =~ m![/\\]|\.xsd$|\.wsdl$!i)
131 0         0 { error __x"file {fn} does not exist", fn => $raw;
132             }
133             else
134 0         0 { my $data = "$raw";
135 0 0 0     0 $data = substr($data, 0, 59) . '...'
136             if length($data) > 60 && $data =~ m/\
137              
138 0         0 error __x"don't known how to interpret XML data\n {data}"
139             , data => $data;
140             }
141              
142 454 100       3820 wantarray ? ($xml, %details) : $xml;
143             }
144              
145             sub _parsedNode($)
146 1     1   4 { my ($thing, $node) = @_;
147 1         3 my $top = $node;
148              
149 1 50       21 if($node->isa('XML::LibXML::Document'))
    0          
150 1         12 { $top = $node->documentElement;
151 1   50     57 my $eltype = type_of_node($top || '(none)');
152 1         19 trace "using preparsed XML document with element <$eltype>";
153             }
154             elsif($node->isa('XML::LibXML::Element'))
155 0         0 { trace 'using preparsed XML node <'.type_of_node($node).'>';
156             }
157             else
158 0         0 { my $text = $node->toString;
159 0         0 $text =~ s/\s+/ /gs;
160 0 0       0 substr($text, 70, -1, '...')
161             if length $text > 75;
162 0         0 error __x"dataToXML() accepts pre-parsed document or element\n {got}"
163             , got => $text;
164             }
165              
166 1         53 ($top, source => ref $node);
167             }
168              
169             sub _parseScalar($)
170 453     453   1262 { my ($thing, $data) = @_;
171 453         3471 trace "parsing XML from string $data";
172 453         20418 my $xml = $parser->parse_string($$data);
173              
174 453 50       118620 ( (defined $xml ? $xml->documentElement : undef)
175             , source => ref $data
176             );
177             }
178              
179             sub _parseFile($)
180 0     0   0 { my ($thing, $fn) = @_;
181 0         0 trace "parsing XML from file $fn";
182 0         0 my $xml = $parser->parse_file($fn);
183              
184 0 0       0 ( (defined $xml ? $xml->documentElement : undef)
185             , source => 'file'
186             , filename => $fn
187             );
188             }
189              
190             sub _parseFileHandle($)
191 0     0   0 { my ($thing, $fh) = @_;
192 0         0 trace "parsing XML from open file $fh";
193 0         0 my $xml = $parser->parse_fh($fh);
194              
195 0 0       0 ( (defined $xml ? $xml->documentElement : undef)
196             , source => ref $thing
197             );
198             }
199              
200             #--------------------------
201              
202              
203             sub walkTree($$)
204 76     76 1 462 { my ($self, $node, $code) = @_;
205 76 100       248 if($code->($node))
206             { $self->walkTree($_, $code)
207 18         120 for $node->getChildNodes;
208             }
209             }
210              
211              
212             my %namespace_file;
213             sub knownNamespace($;@)
214 50     50 1 132 { my $thing = shift;
215 50 50       258 return $namespace_file{ $_[0] } if @_==1;
216              
217 50         165 while(@_)
218 300         502 { my $ns = shift;
219 300         837 $namespace_file{$ns} = shift;
220             }
221 50         116 undef;
222             }
223              
224              
225             sub findSchemaFile($)
226 1     1 1 181345 { my ($thing, $fn) = @_;
227              
228 1 0       17 return (-f $fn ? $fn : undef)
    50          
229             if File::Spec->file_name_is_absolute($fn);
230              
231 1         3 foreach my $dir (@schema_dirs)
232 1         14 { my $full = File::Spec->catfile($dir, $fn);
233 1 50       36 return $full if -f $full;
234             }
235              
236 0           undef;
237             }
238              
239              
240             1;