File Coverage

lib/XML/Compile.pm
Criterion Covered Total %
statement 77 105 73.3
branch 23 60 38.3
condition 5 16 31.2
subroutine 17 20 85.0
pod 7 8 87.5
total 129 209 61.7


line stmt bran cond sub pod time code
1             # Copyrights 2006-2019 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.02.
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 50     50   115329 use vars '$VERSION';
  50         148  
  50         2365  
11             $VERSION = '1.62';
12              
13              
14 50     50   297 use warnings;
  50         99  
  50         1204  
15 50     50   220 use strict;
  50         98  
  50         1485  
16              
17 50     50   805 use Log::Report 'xml-compile';
  50         61205  
  50         325  
18 50     50   13635 use XML::LibXML;
  50         37809  
  50         445  
19 50     50   8239 use XML::Compile::Util qw/:constants type_of_node/;
  50         101  
  50         5878  
20              
21 50     50   317 use File::Spec qw();
  50         89  
  50         78737  
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 34556 { my $class = shift;
41 52 50       332 my $top = @_ % 2 ? shift : undef;
42              
43 52 50       231 $class ne __PACKAGE__
44             or panic "you should instantiate a sub-class, $class is base only";
45              
46 52         447 (bless {}, $class)->init( {top => $top, @_} );
47             }
48              
49             sub init($)
50 52     52 0 149 { my ($self, $args) = @_;
51              
52 52   50     361 my $popts = $args->{parser_options} || [];
53 52 50       478 $self->initParser(ref $popts eq 'HASH' ? %$popts : @$popts);
54              
55 52         6887 $self->addSchemaDirs($args->{schema_dirs});
56 52         148 $self;
57             }
58              
59             #-------------------
60              
61             my @schema_dirs;
62             sub addSchemaDirs(@)
63 152     152 1 298 { my $thing = shift;
64 152         339 foreach (@_)
65 152         359 { my $dir = shift;
66 152 50       507 my @dirs = grep {defined} ref $dir eq 'ARRAY' ? @$dir : $dir;
  152         486  
67 152 50       898 my $sep = $^O eq 'MSWin32' ? qr/\;/ : qr/\:/;
68 152         562 foreach (map { split $sep } @dirs)
  50         317  
69 50         102 { my $el = $_;
70 50 50       1408 $el = File::Spec->catfile($el, 'xsd') if $el =~ s/\.pm$//i;
71 50         249 push @schema_dirs, $el;
72             }
73             }
74 152 50       431 defined wantarray ? @schema_dirs : ();
75             }
76              
77             #----------------------
78              
79              
80             sub initParser(@)
81 52     52 1 110 { 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         550 );
92             }
93              
94              
95             sub dataToXML($)
96 451     451 1 1107 { my ($thing, $raw) = @_;
97 451 100       1130 defined $raw or return;
98              
99 450   33     1112 $parser ||= $thing->initParser;
100              
101 450         770 my ($xml, %details);
102 450 100 66     4305 if(ref $raw && UNIVERSAL::isa($raw, 'XML::LibXML::Node'))
    50 0        
    50          
    50          
    0          
    0          
    0          
    0          
103 1         4 { ($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*\
112 449         1566 { ($xml, %details) = $thing->_parseScalar(\$raw);
113             }
114             elsif(my $known = $thing->knownNamespace($raw))
115 0 0       0 { my $fn = $thing->findSchemaFile($known)
116             or error __x"cannot find pre-installed name-space file named {path} for {name}"
117             , path => $known, name => $raw;
118              
119 0         0 ($xml, %details) = $thing->_parseFile($fn);
120 0         0 $details{source} = "known namespace $raw";
121             }
122             elsif(my $fn = $thing->findSchemaFile($raw))
123 0         0 { ($xml, %details) = $thing->_parseFile($fn);
124 0         0 $details{source} = "filename in schema-dir $raw";
125             }
126             elsif(-f $raw)
127 0         0 { ($xml, %details) = $thing->_parseFile($raw);
128             }
129             elsif($raw !~ /[\n\r<]/ && $raw =~ m![/\\]|\.xsd$|\.wsdl$!i)
130 0         0 { error __x"file {fn} does not exist", fn => $raw;
131             }
132             else
133 0         0 { my $data = "$raw";
134 0 0 0     0 $data = substr($data, 0, 59) . '...'
135             if length($data) > 60 && $data =~ m/\
136              
137 0         0 error __x"don't known how to interpret XML data\n {data}"
138             , data => $data;
139             }
140              
141 450 100       3322 wantarray ? ($xml, %details) : $xml;
142             }
143              
144             sub _parsedNode($)
145 1     1   3 { my ($thing, $node) = @_;
146 1         2 my $top = $node;
147              
148 1 50       4 if($node->isa('XML::LibXML::Document'))
    0          
149 1         7 { $top = $node->documentElement;
150 1   50     32 my $eltype = type_of_node($top || '(none)');
151 1         8 trace "using preparsed XML document with element <$eltype>";
152             }
153             elsif($node->isa('XML::LibXML::Element'))
154 0         0 { trace 'using preparsed XML node <'.type_of_node($node).'>';
155             }
156             else
157 0         0 { my $text = $node->toString;
158 0         0 $text =~ s/\s+/ /gs;
159 0 0       0 substr($text, 70, -1, '...')
160             if length $text > 75;
161 0         0 error __x"dataToXML() accepts pre-parsed document or element\n {got}"
162             , got => $text;
163             }
164              
165 1         40 ($top, source => ref $node);
166             }
167              
168             sub _parseScalar($)
169 449     449   1020 { my ($thing, $data) = @_;
170 449         2285 trace "parsing XML from string $data";
171 449         14865 my $xml = $parser->parse_string($$data);
172              
173 449 50       92046 ( (defined $xml ? $xml->documentElement : undef)
174             , source => ref $data
175             );
176             }
177              
178             sub _parseFile($)
179 0     0   0 { my ($thing, $fn) = @_;
180 0         0 trace "parsing XML from file $fn";
181 0         0 my $xml = $parser->parse_file($fn);
182              
183 0 0       0 ( (defined $xml ? $xml->documentElement : undef)
184             , source => 'file'
185             , filename => $fn
186             );
187             }
188              
189             sub _parseFileHandle($)
190 0     0   0 { my ($thing, $fh) = @_;
191 0         0 trace "parsing XML from open file $fh";
192 0         0 my $xml = $parser->parse_fh($fh);
193              
194 0 0       0 ( (defined $xml ? $xml->documentElement : undef)
195             , source => ref $thing
196             );
197             }
198              
199             #--------------------------
200              
201              
202             sub walkTree($$)
203 76     76 1 376 { my ($self, $node, $code) = @_;
204 76 100       204 if($code->($node))
205             { $self->walkTree($_, $code)
206 18         74 for $node->getChildNodes;
207             }
208             }
209              
210              
211             my %namespace_file;
212             sub knownNamespace($;@)
213 50     50 1 100 { my $thing = shift;
214 50 50       220 return $namespace_file{ $_[0] } if @_==1;
215              
216 50         234 while(@_)
217 300         371 { my $ns = shift;
218 300         696 $namespace_file{$ns} = shift;
219             }
220 50         94 undef;
221             }
222              
223              
224             sub findSchemaFile($)
225 1     1 1 105 { my ($thing, $fn) = @_;
226              
227 1 0       8 return (-f $fn ? $fn : undef)
    50          
228             if File::Spec->file_name_is_absolute($fn);
229              
230 1         2 foreach my $dir (@schema_dirs)
231 1         17 { my $full = File::Spec->catfile($dir, $fn);
232 1 50       23 return $full if -f $full;
233             }
234              
235 0           undef;
236             }
237              
238              
239             1;