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   113351 use vars '$VERSION';
  50         90  
  50         2039  
11             $VERSION = '1.63';
12              
13              
14 50     50   264 use warnings;
  50         80  
  50         1084  
15 50     50   194 use strict;
  50         110  
  50         1317  
16              
17 50     50   722 use Log::Report 'xml-compile';
  50         50150  
  50         278  
18 50     50   11919 use XML::LibXML;
  50         30595  
  50         390  
19 50     50   7003 use XML::Compile::Util qw/:constants type_of_node/;
  50         83  
  50         5158  
20              
21 50     50   276 use File::Spec qw();
  50         65  
  50         68184  
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 31209 { my $class = shift;
41 52 50       295 my $top = @_ % 2 ? shift : undef;
42              
43 52 50       216 $class ne __PACKAGE__
44             or panic "you should instantiate a sub-class, $class is base only";
45              
46 52         407 (bless {}, $class)->init( {top => $top, @_} );
47             }
48              
49             sub init($)
50 52     52 0 129 { my ($self, $args) = @_;
51              
52 52   50     345 my $popts = $args->{parser_options} || [];
53 52 50       423 $self->initParser(ref $popts eq 'HASH' ? %$popts : @$popts);
54              
55 52         6132 $self->addSchemaDirs($args->{schema_dirs});
56 52         130 $self;
57             }
58              
59             #-------------------
60              
61             my @schema_dirs;
62             sub addSchemaDirs(@)
63 152     152 1 250 { my $thing = shift;
64 152         283 foreach (@_)
65 152         313 { my $dir = shift;
66 152 50       447 my @dirs = grep {defined} ref $dir eq 'ARRAY' ? @$dir : $dir;
  152         440  
67 152 50       763 my $sep = $^O eq 'MSWin32' ? qr/\;/ : qr/\:/;
68 152         464 foreach (map { split $sep } @dirs)
  50         280  
69 50         82 { my $el = $_;
70 50 50       1164 $el = File::Spec->catfile($el, 'xsd') if $el =~ s/\.pm$//i;
71 50         216 push @schema_dirs, $el;
72             }
73             }
74 152 50       387 defined wantarray ? @schema_dirs : ();
75             }
76              
77             #----------------------
78              
79              
80             sub initParser(@)
81 52     52 1 100 { 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         513 );
92             }
93              
94              
95             sub dataToXML($)
96 451     451 1 1033 { my ($thing, $raw) = @_;
97 451 100       1052 defined $raw or return;
98              
99 450   33     1036 $parser ||= $thing->initParser;
100              
101 450         705 my ($xml, %details);
102 450 100 66     3763 if(ref $raw && UNIVERSAL::isa($raw, 'XML::LibXML::Node'))
    50 0        
    50          
    50          
    0          
    0          
    0          
    0          
103 1         5 { ($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 449         1478 ($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 450 100       2562 wantarray ? ($xml, %details) : $xml;
143             }
144              
145             sub _parsedNode($)
146 1     1   3 { my ($thing, $node) = @_;
147 1         2 my $top = $node;
148              
149 1 50       4 if($node->isa('XML::LibXML::Document'))
    0          
150 1         8 { $top = $node->documentElement;
151 1   50     35 my $eltype = type_of_node($top || '(none)');
152 1         6 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         40 ($top, source => ref $node);
167             }
168              
169             sub _parseScalar($)
170 449     449   908 { my ($thing, $data) = @_;
171 449         2098 trace "parsing XML from string $data";
172 449         12727 my $xml = $parser->parse_string($$data);
173              
174 449 50       83344 ( (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 340 { my ($self, $node, $code) = @_;
205 76 100       176 if($code->($node))
206             { $self->walkTree($_, $code)
207 18         68 for $node->getChildNodes;
208             }
209             }
210              
211              
212             my %namespace_file;
213             sub knownNamespace($;@)
214 50     50 1 79 { my $thing = shift;
215 50 50       189 return $namespace_file{ $_[0] } if @_==1;
216              
217 50         192 while(@_)
218 300         311 { my $ns = shift;
219 300         610 $namespace_file{$ns} = shift;
220             }
221 50         85 undef;
222             }
223              
224              
225             sub findSchemaFile($)
226 1     1 1 110 { my ($thing, $fn) = @_;
227              
228 1 0       10 return (-f $fn ? $fn : undef)
    50          
229             if File::Spec->file_name_is_absolute($fn);
230              
231 1         4 foreach my $dir (@schema_dirs)
232 1         15 { my $full = File::Spec->catfile($dir, $fn);
233 1 50       24 return $full if -f $full;
234             }
235              
236 0           undef;
237             }
238              
239              
240             1;