File Coverage

lib/XML/Compile/Schema/Instance.pm
Criterion Covered Total %
statement 130 149 87.2
branch 39 62 62.9
condition 23 42 54.7
subroutine 24 32 75.0
pod 23 24 95.8
total 239 309 77.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::Instance;
10 50     50   314 use vars '$VERSION';
  50         84  
  50         2564  
11             $VERSION = '1.63';
12              
13              
14 50     50   249 use warnings;
  50         81  
  50         1147  
15 50     50   217 use strict;
  50         71  
  50         952  
16              
17 50     50   204 use Log::Report 'xml-compile';
  50         83  
  50         398  
18 50     50   12726 use XML::Compile::Schema::Specs;
  50         95  
  50         1305  
19 50     50   243 use XML::Compile::Util qw/pack_type unpack_type/;
  50         88  
  50         2343  
20 50     50   286 use Scalar::Util qw/weaken/;
  50         103  
  50         79136  
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 129 { my $class = shift;
29 58         353 (bless {}, $class)->init( {top => @_} );
30             }
31              
32             sub init($)
33 58     58 0 126 { my ($self, $args) = @_;
34 58         130 my $top = $args->{top};
35 58 50 33     437 defined $top && $top->isa('XML::LibXML::Node')
36             or panic "instance is based on XML node";
37              
38 58         273 $self->{filename} = $args->{filename};
39 58         142 $self->{source} = $args->{source};
40 58         602 $self->{$_} = {} for @defkinds, 'sgs', 'import';
41 58         165 $self->{include} = [];
42              
43 58         231 $self->_collectTypes($top, $args);
44 58         371 $self;
45             }
46              
47              
48 1     1 1 16 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 4 sub source { shift->{source} }
52 1     1 1 6 sub filename { shift->{filename} }
53 0     0 1 0 sub schema { shift->{schema} }
54              
55              
56 58     58 1 96 sub tnses() {keys %{shift->{tnses}}}
  58         310  
57              
58              
59 63     63 1 396 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 9 sub elements() { keys %{shift->{element}} }
  5         50  
69 1     1 1 2 sub attributes() { keys %{shift->{attributes}} }
  1         6  
70 1     1 1 2 sub attributeGroups() { keys %{shift->{attributeGroup}} }
  1         7  
71 1     1 1 2 sub groups() { keys %{shift->{group}} }
  1         5  
72 5     5 1 2283 sub simpleTypes() { keys %{shift->{simpleType}} }
  5         22  
73 7     7 1 11 sub complexTypes() { keys %{shift->{complexType}} }
  7         61  
74              
75              
76 4     4 1 18 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   143 { my ($self, $schema, $args) = @_;
83              
84 58 50       311 $schema->localName eq 'schema'
85             or panic "requires schema element";
86              
87 58   50     524 my $xsd = $self->{xsd} = $schema->namespaceURI || '';
88 58 50       246 if(length $xsd)
89             { my $def = $self->{def}
90 58 50       436 = XML::Compile::Schema::Specs->predefinedSchema($xsd)
91             or error __x"schema namespace `{namespace}' not (yet) supported"
92             , namespace => $xsd;
93              
94 58         197 $self->{xsi} = $def->{uri_xsi};
95             }
96              
97 58         93 my $tns;
98 58 100       197 if($tns = $args->{target_namespace})
99 1         10 { $schema->removeAttribute('targetNamespace');
100 1         5 $schema->setAttribute(targetNamespace => $tns);
101             }
102             else
103 57   100     303 { $tns = $schema->getAttribute('targetNamespace') || '';
104             }
105 58         919 $self->{tns} = $tns;
106              
107             $self->{efd} = $args->{element_form_default}
108 58   100     276 || $schema->getAttribute('elementFormDefault')
109             || 'unqualified';
110              
111             $self->{afd} = $args->{attribute_form_default}
112 58   100     918 || $schema->getAttribute('attributeFormDefault')
113             || 'unqualified';
114              
115 58         647 $self->{tnses} = {}; # added when used
116 58         128 $self->{types} = {};
117              
118 58         128 $self->{schema} = $schema;
119 58         250 weaken($self->{schema});
120              
121             NODE:
122 58         362 foreach my $node ($schema->childNodes)
123 1010 100       6204 { next unless $node->isa('XML::LibXML::Element');
124 443         935 my $local = $node->localName;
125 443   50     1185 my $myns = $node->namespaceURI || '';
126 443 50 0     738 $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 443 100       801 if $skip_toplevel{$local};
132              
133 433 100       695 if($local eq 'import')
134 1   33     5 { my $namespace = $node->getAttribute('namespace') || $tns;
135 1   50     12 my $location = $node->getAttribute('schemaLocation') || '';
136 1         9 push @{$self->{import}{$namespace}}, $location;
  1         4  
137 1         3 next NODE;
138             }
139              
140 432 50       673 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 432 50       730 unless($defkinds{$local})
150 0         0 { mistake __x"ignoring unknown definition class {class}"
151             , class => $local;
152 0         0 next;
153             }
154              
155 432 50       736 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 432   66     3314 my $tns = $node->getAttribute('targetNamespace') || $tns;
160 432         3713 my $type = pack_type $tns, $name;
161 432         762 $self->{tnses}{$tns}++;
162 432         995 $self->{$local}{$type} = $node;
163              
164 432 100       724 if(my $sg = $node->getAttribute('substitutionGroup'))
165 5 50       106 { my ($prefix, $l) = $sg =~ m/:/ ? split(/:/, $sg, 2) : ('',$sg);
166 5         37 my $base = pack_type $node->lookupNamespaceURI($prefix), $l;
167 5         11 push @{$self->{sgs}{$base}}, $type;
  5         23  
168             }
169             }
170              
171 58         429 $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 1608 { my $self = shift;
189 1 50       4 my $fh = @_ % 2 ? shift : select;
190 1         3 my %args = @_;
191              
192 1         5 $fh->print("namespace: ", $self->targetNamespace, "\n");
193 1 50       13 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         4 { $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       10 : $args{kinds};
    50          
204              
205             my $list_abstract
206 1 50       3 = exists $args{list_abstract} ? $args{list_abstract} : 1;
207              
208 1         3 foreach my $kind (@kinds)
209 6         40 { my $table = $self->{$kind};
210 6 100       16 keys %$table or next;
211 5 50       40 $fh->print(" definitions of ${kind}s:\n") if @kinds > 1;
212              
213 5         115 foreach (sort keys %$table)
214 145         923 { my $info = $self->find($kind, $_);
215 145         211 my ($ns, $name) = unpack_type $_;
216 145 50 66     263 next if $info->{abstract} && ! $list_abstract;
217 145 100       186 my $abstract = $info->{abstract} ? ' [abstract]' : '';
218 145 50       170 my $final = $info->{final} ? ' [final]' : '';
219 145         453 $fh->print(" $name$abstract$final\n");
220             }
221             }
222             }
223              
224              
225             sub find($$)
226 2691     2691 1 4985 { my ($self, $kind, $full) = @_;
227 2691 100       7331 my $node = $self->{$kind}{$full}
228             or return;
229              
230 2428 100       7644 return $node # translation of XML node into info is cached
231             if ref $node eq 'HASH';
232              
233 428         1690 my %info = (type => $kind, node => $node, full => $full);
234 428         888 @info{'ns', 'name'} = unpack_type $full;
235              
236 428         1200 $self->{$kind}{$full} = \%info;
237              
238 428   100     1116 my $abstract = $node->getAttribute('abstract') || '';
239 428   66     6108 $info{abstract} = $abstract eq 'true' || $abstract eq '1';
240              
241 428   50     867 my $final = $node->getAttribute('final') || '';
242 428   33     4370 $info{final} = $final eq 'true' || $final eq '1';
243              
244 428         1279 my $local = $node->localName;
245 428 100       1001 if($local eq 'element') { $info{efd} = $node->getAttribute('form') }
  241 100       498  
246 4         9 elsif($local eq 'attribute'){ $info{afd} = $node->getAttribute('form') }
247 428   33     3432 $info{efd} ||= $self->{efd}; # both needed for nsContext
248 428   33     1513 $info{afd} ||= $self->{afd};
249 428         878 \%info;
250             }
251              
252             1;