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   358 use vars '$VERSION';
  50         105  
  50         2566  
11             $VERSION = '1.62';
12              
13              
14 50     50   292 use warnings;
  50         98  
  50         1171  
15 50     50   241 use strict;
  50         96  
  50         1086  
16              
17 50     50   222 use Log::Report 'xml-compile';
  50         99  
  50         271  
18              
19             use XML::Compile::Util
20 50     50   13424 qw/pack_type unpack_type pack_id unpack_id SCHEMA2001/;
  50         106  
  50         3592  
21              
22 50     50   355 use XML::Compile::Schema::BuiltInTypes qw/%builtin_types/;
  50         94  
  50         94191  
23              
24              
25             sub new($@)
26 52     52 1 153 { my $class = shift;
27 52         270 (bless {}, $class)->init( {@_} );
28             }
29              
30             sub init($)
31 52     52 0 139 { my ($self, $args) = @_;
32 52         291 $self->{tns} = {};
33 52         160 $self->{sgs} = {};
34 52         147 $self->{use} = [];
35 52         293 $self;
36             }
37              
38              
39 9     9 1 367 sub list() { keys %{shift->{tns}} }
  9         35  
40              
41              
42             sub namespace($)
43 4651     4651 1 10140 { my $nss = $_[0]->{tns}{$_[1]};
44 4651 100       11356 $nss ? @$nss : ();
45             }
46              
47              
48             sub add(@)
49 58     58 1 144 { my $self = shift;
50 58         176 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         289 my @tnses = $instance->tnses;
55 58 50       242 @tnses or @tnses = '(none)';
56              
57             # newest definitions overrule earlier.
58 58         297 unshift @{$self->{tns}{$_}}, $instance
59 58         171 for @tnses;
60              
61             # inventory where to find definitions which belong to some
62             # substitutionGroup.
63 58         131 while(my($base,$ext) = each %{$instance->sgs})
  63         260  
64 5   33     39 { $self->{sgs}{$base}{$_} ||= $instance for @$ext;
65             }
66             }
67 58         181 @_;
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 9476 sub schemas($) { $_[0]->namespace($_[1]) }
79              
80              
81             sub allSchemas()
82 2     2 1 4 { my $self = shift;
83 2         7 map {$self->schemas($_)} $self->list;
  2         5  
84             }
85              
86              
87             sub find($$;$)
88 3869     3869 1 6821 { my ($self, $kind) = (shift, shift);
89 3869 50       10965 my ($ns, $name) = (@_%2==1) ? (unpack_type shift) : (shift, shift);
90 3869         7384 my %opts = @_;
91              
92 3869 50       6963 defined $ns or return undef;
93 3869         8070 my $label = pack_type $ns, $name; # re-pack unpacked for consistency
94              
95 3869         7969 foreach my $schema ($self->schemas($ns))
96 2508         7088 { my $def = $schema->find($kind, $label);
97 2508 100       10699 return $def if defined $def;
98             }
99              
100 1623 50       2731 my $used = exists $opts{include_used} ? $opts{include_used} : 1;
101 1623 50       2920 $used or return undef;
102              
103 1623         1853 foreach my $use ( @{$self->{use}} )
  1623         2774  
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         4509 undef;
109             }
110              
111              
112             sub doesExtend($$)
113 815     815 1 2084 { my ($self, $ext, $base) = @_;
114 815 100       1640 return 1 if $ext eq $base;
115 800 50       1537 return 0 if $ext =~ m/^unnamed /;
116              
117 800         1007 my ($node, $super, $subnode);
118 800 100       1353 if(my $st = $self->find(simpleType => $ext))
    100          
119             { # pure simple type
120 86         141 $node = $st->{node};
121 86 100       229 if(($subnode) = $node->getChildrenByLocalName('restriction'))
122 80         922 { $super = $subnode->getAttribute('base');
123             }
124             # list an union currently ignored
125             }
126             elsif(my $ct = $self->find(complexType => $ext))
127 4         9 { $node = $ct->{node};
128             # getChildrenByLocalName returns list, we know size one
129 4 100       19 if(my($sc) = $node->getChildrenByLocalName('simpleContent'))
    50          
130             { # tagged
131 2 50       26 if(($subnode) = $sc->getChildrenByLocalName('extension'))
    0          
132 2         24 { $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       68 if(($subnode) = $cc->getChildrenByLocalName('extension'))
    0          
141 2         23 { $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         1685 my ($ns, $local) = unpack_type $ext;
151 710 50 33     3163 $ns eq SCHEMA2001 && $builtin_types{$local}
152             or error __x"cannot find {type} as simpleType or complexType"
153             , type => $ext;
154 710         1415 my ($bns, $blocal) = unpack_type $base;
155 710 50       1521 $ns eq $bns
156             or return 0;
157              
158 710         1787 while(my $e = $builtin_types{$local}{extends})
159 2808 100       4086 { return 1 if $e eq $blocal;
160 2796         6129 $local = $e;
161             }
162             }
163              
164 788 100       3635 $super
165             or return 0;
166              
167 84 100       346 my ($prefix, $local) = $super =~ m/:/ ? split(/:/,$super,2) : ('',$super);
168 84         364 my $supertype = pack_type $subnode->lookupNamespaceURI($prefix), $local;
169              
170 84 100       262 $base eq $supertype ? 1 : $self->doesExtend($supertype, $base);
171             }
172              
173              
174             sub findTypeExtensions($)
175 2     2 1 7 { my ($self, $type) = @_;
176              
177 2         3 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         15 sort keys %ext;
190             }
191              
192             sub autoexpand_xsi_type($)
193 2     2 0 7 { my ($self, $type) = @_;
194 2         7 my @ext = $self->findTypeExtensions($type);
195 2         17 trace "discovered xsi:type choices for $type:\n ". join("\n ", @ext);
196 2         54 \@ext;
197             }
198              
199              
200             sub findSgMembers($$)
201 1119     1119 1 2414 { my ($self, $class, $base) = @_;
202 1119 100       3623 my $s = $self->{sgs}{$base}
203             or return;
204              
205 22         33 my @sgs;
206 22         105 while(my($ext, $instance) = each %$s)
207 36         85 { push @sgs, $instance->find($class => $ext)
208             , $self->findSgMembers($class, $ext);
209             }
210 22         60 @sgs;
211             }
212              
213              
214             sub findID($;$)
215 771     771 1 1302 { my $self = shift;
216 771 50       2958 my ($label, $ns, $id)
217             = @_==1 ? ($_[0], unpack_id $_[0]) : (pack_id($_[0], $_[1]), @_);
218 771 50       1892 defined $ns or return undef;
219              
220 771         20607 my $xpc = XML::LibXML::XPathContext->new;
221 771         4322 $xpc->registerNs(a => $ns);
222              
223 771         1093 my @nodes;
224 771         2076 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         8207 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;