File Coverage

lib/XML/Compile/Schema/Instance.pm
Criterion Covered Total %
statement 127 146 86.9
branch 39 62 62.9
condition 23 42 54.7
subroutine 23 31 74.1
pod 23 24 95.8
total 235 305 77.0


line stmt bran cond sub pod time code
1             # Copyrights 2006-2024 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.03.
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::Instance;{
10             our $VERSION = '1.64';
11             }
12              
13              
14 50     50   384 use warnings;
  50         101  
  50         3794  
15 50     50   318 use strict;
  50         93  
  50         1587  
16              
17 50     50   293 use Log::Report 'xml-compile';
  50         96  
  50         633  
18 50     50   18026 use XML::Compile::Schema::Specs;
  50         116  
  50         2153  
19 50     50   299 use XML::Compile::Util qw/pack_type unpack_type/;
  50         90  
  50         3461  
20 50     50   302 use Scalar::Util qw/weaken/;
  50         94  
  50         116554  
21              
22             my @defkinds = qw/element attribute simpleType complexType
23             attributeGroup group/;
24             my %defkinds = map +($_ => 1), @defkinds;
25              
26              
27             sub new($@)
28 58     58 1 158 { my $class = shift;
29 58         509 (bless {}, $class)->init( {top => @_} );
30             }
31              
32             sub init($)
33 58     58 0 404 { my ($self, $args) = @_;
34 58         182 my $top = $args->{top};
35 58 50 33     790 defined $top && $top->isa('XML::LibXML::Node')
36             or panic "instance is based on XML node";
37              
38 58         435 $self->{filename} = $args->{filename};
39 58         207 $self->{source} = $args->{source};
40 58         858 $self->{$_} = {} for @defkinds, 'sgs', 'import';
41 58         226 $self->{include} = [];
42              
43 58         338 $self->_collectTypes($top, $args);
44 58         607 $self;
45             }
46              
47              
48 1     1 1 21 sub targetNamespace { shift->{tns} }
49 0     0 1 0 sub schemaNamespace { shift->{xsd} }
50 0     0 1 0 sub schemaInstance { shift->{xsi} }
51 1     1 1 6 sub source { shift->{source} }
52 1     1 1 8 sub filename { shift->{filename} }
53 0     0 1 0 sub schema { shift->{schema} }
54              
55              
56 58     58 1 145 sub tnses() {keys %{shift->{tnses}}}
  58         332  
57              
58              
59 63     63 1 498 sub sgs() { shift->{sgs} }
60              
61              
62 0     0 1 0 sub type($) { $_[0]->{types}{$_[1]} }
63              
64              
65 0     0 1 0 sub element($) { $_[0]->{element}{$_[1]} }
66              
67              
68 5     5 1 11 sub elements() { keys %{shift->{element}} }
  5         132  
69 1     1 1 3 sub attributes() { keys %{shift->{attributes}} }
  1         9  
70 1     1 1 5 sub attributeGroups() { keys %{shift->{attributeGroup}} }
  1         8  
71 1     1 1 4 sub groups() { keys %{shift->{group}} }
  1         10  
72 5     5 1 7212 sub simpleTypes() { keys %{shift->{simpleType}} }
  5         26  
73 7     7 1 12 sub complexTypes() { keys %{shift->{complexType}} }
  7         85  
74              
75              
76 4     4 1 15 sub types() { ($_[0]->simpleTypes, $_[0]->complexTypes) }
77              
78              
79             my %skip_toplevel = map +($_ => 1), qw/annotation notation redefine/;
80              
81             sub _collectTypes($$)
82 58     58   175 { my ($self, $schema, $args) = @_;
83              
84 58 50       479 $schema->localName eq 'schema'
85             or panic "requires schema element";
86              
87 58   50     722 my $xsd = $self->{xsd} = $schema->namespaceURI || '';
88 58 50       316 if(length $xsd)
89             { my $def = $self->{def}
90 58 50       780 = XML::Compile::Schema::Specs->predefinedSchema($xsd)
91             or error __x"schema namespace `{namespace}' not (yet) supported"
92             , namespace => $xsd;
93              
94 58         279 $self->{xsi} = $def->{uri_xsi};
95             }
96              
97 58         162 my $tns;
98 58 100       307 if($tns = $args->{target_namespace})
99 1         12 { $schema->removeAttribute('targetNamespace');
100 1         6 $schema->setAttribute(targetNamespace => $tns);
101             }
102             else
103 57   100     396 { $tns = $schema->getAttribute('targetNamespace') || '';
104             }
105 58         1291 $self->{tns} = $tns;
106              
107             $self->{efd} = $args->{element_form_default}
108 58   100     492 || $schema->getAttribute('elementFormDefault')
109             || 'unqualified';
110              
111             $self->{afd} = $args->{attribute_form_default}
112 58   100     1196 || $schema->getAttribute('attributeFormDefault')
113             || 'unqualified';
114              
115 58         961 $self->{tnses} = {}; # added when used
116 58         213 $self->{types} = {};
117              
118 58         185 $self->{schema} = $schema;
119 58         242 weaken($self->{schema});
120              
121             NODE:
122 58         609 foreach my $node ($schema->childNodes)
123 1020 100       9196 { next unless $node->isa('XML::LibXML::Element');
124 448         1368 my $local = $node->localName;
125 448   50     1599 my $myns = $node->namespaceURI || '';
126 448 50 0     1120 $myns eq $xsd
127             or error __x"schema element `{name}' not in schema namespace {ns} but {other}"
128             , name => $local, ns => $xsd, other => ($myns || '');
129              
130             next
131 448 100       1168 if $skip_toplevel{$local};
132              
133 438 100       960 if($local eq 'import')
134 1   33     16 { my $namespace = $node->getAttribute('namespace') || $tns;
135 1   50     21 my $location = $node->getAttribute('schemaLocation') || '';
136 1         12 push @{$self->{import}{$namespace}}, $location;
  1         6  
137 1         5 next NODE;
138             }
139              
140 437 50       952 if($local eq 'include')
141 0 0       0 { my $location = $node->getAttribute('schemaLocation')
142             or error __x"include requires schemaLocation attribute at line {linenr}"
143             , linenr => $node->line_number;
144              
145 0         0 push @{$self->{include}}, $location;
  0         0  
146 0         0 next NODE;
147             }
148              
149 437 50       1032 unless($defkinds{$local})
150 0         0 { mistake __x"ignoring unknown definition class {class}"
151             , class => $local;
152 0         0 next;
153             }
154              
155 437 50       989 my $name = $node->getAttribute('name')
156             or error __x"schema component {local} without name at line {linenr}"
157             , local => $local, linenr => $node->line_number;
158              
159 437   66     4681 my $tns = $node->getAttribute('targetNamespace') || $tns;
160 437         5009 my $type = pack_type $tns, $name;
161 437         981 $self->{tnses}{$tns}++;
162 437         4547 $self->{$local}{$type} = $node;
163              
164 437 100       941 if(my $sg = $node->getAttribute('substitutionGroup'))
165 5 50       98 { my ($prefix, $l) = $sg =~ m/:/ ? split(/:/, $sg, 2) : ('',$sg);
166 5         93 my $base = pack_type $node->lookupNamespaceURI($prefix), $l;
167 5         12 push @{$self->{sgs}{$base}}, $type;
  5         30  
168             }
169             }
170              
171 58         647 $self;
172             }
173              
174              
175 0     0 1 0 sub includeLocations() { @{shift->{include}} }
  0         0  
176              
177              
178 0     0 1 0 sub imports() { keys %{shift->{import}} }
  0         0  
179              
180              
181             sub importLocations($)
182 0     0 1 0 { my $locs = $_[0]->{import}{$_[1]};
183 0 0       0 $locs ? @$locs : ();
184             }
185              
186              
187             sub printIndex(;$)
188 1     1 1 1319 { my $self = shift;
189 1 50       7 my $fh = @_ % 2 ? shift : select;
190 1         4 my %args = @_;
191              
192 1         6 $fh->print("namespace: ", $self->targetNamespace, "\n");
193 1 50       14 if(defined(my $filename = $self->filename))
    50          
194 0         0 { $fh->print(" filename: $filename\n");
195             }
196             elsif(defined(my $source = $self->source))
197 1         5 { $fh->print(" source: $source\n");
198             }
199              
200             my @kinds
201             = ! defined $args{kinds} ? @defkinds
202 0         0 : ref $args{kinds} eq 'ARRAY' ? @{$args{kinds}}
203 1 0       13 : $args{kinds};
    50          
204              
205             my $list_abstract
206 1 50       4 = exists $args{list_abstract} ? $args{list_abstract} : 1;
207              
208 1         150 foreach my $kind (@kinds)
209 6         57 { my $table = $self->{$kind};
210 6 100       21 keys %$table or next;
211 5 50       26 $fh->print(" definitions of ${kind}s:\n") if @kinds > 1;
212              
213 5         142 foreach (sort keys %$table)
214 145         1281 { my $info = $self->find($kind, $_);
215 145         400 my ($ns, $name) = unpack_type $_;
216 145 50 66     432 next if $info->{abstract} && ! $list_abstract;
217 145 100       309 my $abstract = $info->{abstract} ? ' [abstract]' : '';
218 145 50       290 my $final = $info->{final} ? ' [final]' : '';
219 145         562 $fh->print(" $name$abstract$final\n");
220             }
221             }
222             }
223              
224              
225             sub find($$)
226 2737     2737 1 10212 { my ($self, $kind, $full) = @_;
227 2737 100       12029 my $node = $self->{$kind}{$full}
228             or return;
229              
230 2466 100       10744 return $node # translation of XML node into info is cached
231             if ref $node eq 'HASH';
232              
233 433         2299 my %info = (type => $kind, node => $node, full => $full);
234 433         1296 @info{'ns', 'name'} = unpack_type $full;
235              
236 433         1454 $self->{$kind}{$full} = \%info;
237              
238 433   100     1497 my $abstract = $node->getAttribute('abstract') || '';
239 433   66     9919 $info{abstract} = $abstract eq 'true' || $abstract eq '1';
240              
241 433   50     1174 my $final = $node->getAttribute('final') || '';
242 433   33     6346 $info{final} = $final eq 'true' || $final eq '1';
243              
244 433         1783 my $local = $node->localName;
245 433 100       1373 if($local eq 'element') { $info{efd} = $node->getAttribute('form') }
  245 100       686  
246 4         9 elsif($local eq 'attribute'){ $info{afd} = $node->getAttribute('form') }
247 433   33     6566 $info{efd} ||= $self->{efd}; # both needed for nsContext
248 433   33     2255 $info{afd} ||= $self->{afd};
249 433         1374 \%info;
250             }
251              
252             1;