File Coverage

lib/XML/Compile.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


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