File Coverage

lib/XML/Compile/Schema/NameSpaces.pm
Criterion Covered Total %
statement 104 137 75.9
branch 39 70 55.7
condition 3 17 17.6
subroutine 19 22 86.3
pod 14 16 87.5
total 179 262 68.3


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::Schema::NameSpaces;
10 50     50   299 use vars '$VERSION';
  50         84  
  50         2244  
11             $VERSION = '1.63';
12              
13              
14 50     50   241 use warnings;
  50         82  
  50         1016  
15 50     50   195 use strict;
  50         81  
  50         918  
16              
17 50     50   209 use Log::Report 'xml-compile';
  50         81  
  50         267  
18              
19             use XML::Compile::Util
20 50     50   11442 qw/pack_type unpack_type pack_id unpack_id SCHEMA2001/;
  50         88  
  50         3162  
21              
22 50     50   285 use XML::Compile::Schema::BuiltInTypes qw/%builtin_types/;
  50         89  
  50         80622  
23              
24              
25             sub new($@)
26 52     52 1 122 { my $class = shift;
27 52         246 (bless {}, $class)->init( {@_} );
28             }
29              
30             sub init($)
31 52     52 0 125 { my ($self, $args) = @_;
32 52         252 $self->{tns} = {};
33 52         140 $self->{sgs} = {};
34 52         134 $self->{use} = [];
35 52         266 $self;
36             }
37              
38              
39 9     9 1 343 sub list() { keys %{shift->{tns}} }
  9         37  
40              
41              
42             sub namespace($)
43 4651     4651 1 8809 { my $nss = $_[0]->{tns}{$_[1]};
44 4651 100       9814 $nss ? @$nss : ();
45             }
46              
47              
48             sub add(@)
49 58     58 1 130 { my $self = shift;
50 58         141 foreach my $instance (@_)
51             { # With the "new" targetNamespace attribute on any attribute, one
52             # schema may have contribute to multiple tns's. Also, I have
53             # encounted schema's without elements, but
54 58         242 my @tnses = $instance->tnses;
55 58 50       236 @tnses or @tnses = '(none)';
56              
57             # newest definitions overrule earlier.
58 58         275 unshift @{$self->{tns}{$_}}, $instance
59 58         170 for @tnses;
60              
61             # inventory where to find definitions which belong to some
62             # substitutionGroup.
63 58         121 while(my($base,$ext) = each %{$instance->sgs})
  63         236  
64 5   33     43 { $self->{sgs}{$base}{$_} ||= $instance for @$ext;
65             }
66             }
67 58         135 @_;
68             }
69              
70              
71             sub use($)
72 0     0 1 0 { my $self = shift;
73 0         0 push @{$self->{use}}, @_;
  0         0  
74 0         0 @{$self->{use}};
  0         0  
75             }
76              
77              
78 4650     4650 1 7604 sub schemas($) { $_[0]->namespace($_[1]) }
79              
80              
81             sub allSchemas()
82 2     2 1 3 { my $self = shift;
83 2         8 map {$self->schemas($_)} $self->list;
  2         3  
84             }
85              
86              
87             sub find($$;$)
88 3869     3869 1 5798 { my ($self, $kind) = (shift, shift);
89 3869 50       8714 my ($ns, $name) = (@_%2==1) ? (unpack_type shift) : (shift, shift);
90 3869         6066 my %opts = @_;
91              
92 3869 50       5928 defined $ns or return undef;
93 3869         6325 my $label = pack_type $ns, $name; # re-pack unpacked for consistency
94              
95 3869         6748 foreach my $schema ($self->schemas($ns))
96 2508         5972 { my $def = $schema->find($kind, $label);
97 2508 100       9121 return $def if defined $def;
98             }
99              
100 1623 50       2280 my $used = exists $opts{include_used} ? $opts{include_used} : 1;
101 1623 50       2185 $used or return undef;
102              
103 1623         1525 foreach my $use ( @{$self->{use}} )
  1623         2290  
104 0         0 { my $def = $use->namespaces->find($kind, $label, include_used => 0);
105 0 0       0 return $def if defined $def;
106             }
107              
108 1623         3796 undef;
109             }
110              
111              
112             sub doesExtend($$)
113 815     815 1 1715 { my ($self, $ext, $base) = @_;
114 815 100       1294 return 1 if $ext eq $base;
115 800 50       1228 return 0 if $ext =~ m/^unnamed /;
116              
117 800         844 my ($node, $super, $subnode);
118 800 100       1084 if(my $st = $self->find(simpleType => $ext))
    100          
119             { # pure simple type
120 86         110 $node = $st->{node};
121 86 100       183 if(($subnode) = $node->getChildrenByLocalName('restriction'))
122 80         760 { $super = $subnode->getAttribute('base');
123             }
124             # list an union currently ignored
125             }
126             elsif(my $ct = $self->find(complexType => $ext))
127 4         8 { $node = $ct->{node};
128             # getChildrenByLocalName returns list, we know size one
129 4 100       16 if(my($sc) = $node->getChildrenByLocalName('simpleContent'))
    50          
130             { # tagged
131 2 50       23 if(($subnode) = $sc->getChildrenByLocalName('extension'))
    0          
132 2         17 { $super = $subnode->getAttribute('base');
133             }
134             elsif(($subnode) = $sc->getChildrenByLocalName('restriction'))
135 0         0 { $super = $subnode->getAttribute('base');
136             }
137             }
138             elsif(my($cc) = $node->getChildrenByLocalName('complexContent'))
139             { # real complex
140 2 50       58 if(($subnode) = $cc->getChildrenByLocalName('extension'))
    0          
141 2         20 { $super = $subnode->getAttribute('base');
142             }
143             elsif(($subnode) = $cc->getChildrenByLocalName('restriction'))
144 0         0 { $super = $subnode->getAttribute('base');
145             }
146             }
147             }
148             else
149             { # built-in
150 710         1068 my ($ns, $local) = unpack_type $ext;
151 710 50 33     2544 $ns eq SCHEMA2001 && $builtin_types{$local}
152             or error __x"cannot find {type} as simpleType or complexType"
153             , type => $ext;
154 710         1032 my ($bns, $blocal) = unpack_type $base;
155 710 50       1245 $ns eq $bns
156             or return 0;
157              
158 710         1468 while(my $e = $builtin_types{$local}{extends})
159 2808 100       3290 { return 1 if $e eq $blocal;
160 2796         4607 $local = $e;
161             }
162             }
163              
164 788 100       3000 $super
165             or return 0;
166              
167 84 100       307 my ($prefix, $local) = $super =~ m/:/ ? split(/:/,$super,2) : ('',$super);
168 84         317 my $supertype = pack_type $subnode->lookupNamespaceURI($prefix), $local;
169              
170 84 100       228 $base eq $supertype ? 1 : $self->doesExtend($supertype, $base);
171             }
172              
173              
174             sub findTypeExtensions($)
175 2     2 1 5 { my ($self, $type) = @_;
176              
177 2         5 my %ext;
178 2 50       6 if($self->find(simpleType => $type))
    50          
179             { $self->doesExtend($_, $type) && $ext{$_}++
180 0   0     0 for map $_->simpleTypes, $self->allSchemas;
181             }
182             elsif($self->find(complexType => $type))
183             { $self->doesExtend($_, $type) && $ext{$_}++
184 2   33     6 for map $_->complexTypes, $self->allSchemas;
185             }
186             else
187 0         0 { error __x"cannot find base-type {type} for extensions", type => $type;
188             }
189 2         39 sort keys %ext;
190             }
191              
192             sub autoexpand_xsi_type($)
193 2     2 0 5 { my ($self, $type) = @_;
194 2         10 my @ext = $self->findTypeExtensions($type);
195 2         16 trace "discovered xsi:type choices for $type:\n ". join("\n ", @ext);
196 2         46 \@ext;
197             }
198              
199              
200             sub findSgMembers($$)
201 1119     1119 1 2123 { my ($self, $class, $base) = @_;
202 1119 100       3241 my $s = $self->{sgs}{$base}
203             or return;
204              
205 22         23 my @sgs;
206 22         96 while(my($ext, $instance) = each %$s)
207 36         79 { push @sgs, $instance->find($class => $ext)
208             , $self->findSgMembers($class, $ext);
209             }
210 22         51 @sgs;
211             }
212              
213              
214             sub findID($;$)
215 771     771 1 1158 { my $self = shift;
216 771 50       2873 my ($label, $ns, $id)
217             = @_==1 ? ($_[0], unpack_id $_[0]) : (pack_id($_[0], $_[1]), @_);
218 771 50       1597 defined $ns or return undef;
219              
220 771         18284 my $xpc = XML::LibXML::XPathContext->new;
221 771         3877 $xpc->registerNs(a => $ns);
222              
223 771         1001 my @nodes;
224 771         1891 foreach my $fragment ($self->schemas($ns))
225 0 0       0 { @nodes = $xpc->findnodes("/*/a:*#$id", $fragment->schema)
226             or next;
227              
228 0 0       0 return $nodes[0]
229             if @nodes==1;
230              
231 0   0     0 error "multiple elements with the same id {id} in {source}"
232             , id => $label
233             , source => ($fragment->filename || $fragment->source);
234             }
235              
236 771         7246 undef;
237             }
238              
239              
240             sub printIndex(@)
241 0     0 1   { my $self = shift;
242 0 0         my $fh = @_ % 2 ? shift : select;
243 0           my %opts = @_;
244              
245 0   0       my $nss = delete $opts{namespace} || [$self->list];
246 0 0         foreach my $nsuri (ref $nss eq 'ARRAY' ? @$nss : $nss)
247 0           { $_->printIndex($fh, %opts) for $self->namespace($nsuri);
248             }
249              
250 0 0         my $show_used = exists $opts{include_used} ? $opts{include_used} : 1;
251 0           foreach my $use ($self->use)
252 0           { $use->printIndex(%opts, include_used => 0);
253             }
254              
255 0           $self;
256             }
257              
258              
259             sub importIndex(%)
260 0     0 1   { my ($self, %args) = @_;
261 0           my %import;
262 0           foreach my $fragment (map $self->schemas($_), $self->list)
263 0           { foreach my $import ($fragment->imports)
264 0           { $import{$import}{$_}++ for $fragment->importLocations($import);
265             }
266             }
267 0           foreach my $ns (keys %import)
268 0           { $import{$ns} = [ grep length, keys %{$import{$ns}} ];
  0            
269             }
270 0           \%import;
271             }
272              
273             1;