File Coverage

blib/lib/XML/Validator/Schema/Parser.pm
Criterion Covered Total %
statement 12 149 8.0
branch 0 92 0.0
condition 0 74 0.0
subroutine 4 7 57.1
pod 2 3 66.6
total 18 325 5.5


line stmt bran cond sub pod time code
1             package XML::Validator::Schema::Parser;
2 5     5   25 use strict;
  5         9  
  5         202  
3 5     5   25 use warnings;
  5         8  
  5         176  
4              
5             =head1 NAME
6              
7             XML::Validator::Schema::Parser - XML Schema Document Parser
8              
9             =head1 DESCRIPTION
10              
11             This is an internal module used by XML::Validator::Schema to parse XML
12             Schema documents.
13              
14             =cut
15              
16 5     5   24 use base 'XML::SAX::Base';
  5         7  
  5         388  
17 5     5   2801 use XML::Validator::Schema::Util qw(_attr _err);
  5         12  
  5         8972  
18              
19             sub new {
20 0     0 0   my $pkg = shift;
21 0 0         my $opt = (@_ == 1) ? { %{shift()} } : {@_};
  0            
22 0           my $self = bless $opt, $pkg;
23              
24             # start with a dummy root node and an empty stack of elements
25 0           $self->{node_stack} = $self->{schema}{node_stack};
26              
27 0           return $self;
28             }
29              
30             sub start_element {
31 0     0 1   my ($self, $data) = @_;
32 0           my $node_stack = $self->{node_stack};
33 0 0         my $mother = @$node_stack ? $node_stack->[-1] : undef;
34 0           my $name = $data->{LocalName};
35              
36             # make sure schema comes first
37 0 0 0       _err("Root element must be , fount <$name> instead.")
38             if @$node_stack == 0 and $name ne 'schema';
39            
40             # starting up?
41 0 0 0       if ($name eq 'schema') {
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
      0        
      0        
42 0           my $node = XML::Validator::Schema::RootNode->new;
43 0           $node->name('<<>>');
44 0           push(@$node_stack, $node);
45              
46             # make sure elementFormDefault and attributeFormDefault are
47             # 'unqualified' if declared since that's all we're up to
48 0           for (qw(elementFormDefault attributeFormDefault)) {
49 0           my $a = _attr($data, $_);
50 0 0 0       _err("$_ in must be 'unqualified', ".
51             "'qualified' is not supported.")
52             if $a and $a ne 'unqualified';
53             }
54              
55             # ignoring targetSchema intentionally. With both Defaults
56             # unqualified there isn't much point looking at it.
57             }
58              
59             # handle element declaration
60             elsif ($name eq 'element') {
61 0           my $node;
62 0 0         if (_attr($data, 'ref')) {
63 0           $node = XML::Validator::Schema::ElementRefNode->parse($data);
64             } else {
65             # create a new node for the element
66 0           $node = XML::Validator::Schema::ElementNode->parse($data);
67             }
68              
69             # add to current node's daughter list and become the current node
70 0           $mother->add_daughter($node);
71 0           push @$node_stack, $node;
72             }
73              
74             elsif ($name eq 'attribute') {
75             # check anonymous/named constraints
76 0           my $name = _attr($data, 'name');
77 0 0 0       _err("Anonymous global simpleType not allowed.")
78             if not $name and $mother->is_root;
79              
80             # parse it into an AttributeNode and tell Mom about it
81 0           my $node = XML::Validator::Schema::AttributeNode->parse($data);
82 0           $mother->add_daughter($node);
83 0           push @$node_stack, $node;
84             }
85              
86             elsif ($name eq 'simpleContent') {
87 0 0 0       _err("Found simpleContent outside a complexType.")
88             unless $mother->{is_complex} or
89             $mother->isa('XML::Validator::Schema::ComplexTypeNode');
90              
91 0           $mother->{simple_content} = 1;
92             }
93              
94             elsif ($name eq 'extension') {
95 0 0         _err("Found illegal outside simpleContent.")
96             unless $mother->{simple_content};
97              
98             # extract simpleType from base
99 0           my $base = _attr($data, 'base');
100 0 0         _err("Found without required 'base' attribute.")
101             unless $base;
102 0           $mother->{type_name} = $base;
103 0           $mother->{unresolved_type} = 1;
104             }
105              
106             elsif ($name eq 'simpleType') {
107 0           my $name = _attr($data, 'name');
108 0 0         if ($name) {
109 0 0         _err("Named simpleType must be global.")
110             unless $mother->is_root;
111              
112             # this is a named type, parse it into an SimpleTypeNode
113             # and tell Mom about it
114 0           my $node = XML::Validator::Schema::SimpleTypeNode->parse($data);
115 0           $mother->add_daughter($node);
116 0           push @$node_stack, $node;
117              
118             } else {
119 0 0         _err("Anonymous global simpleType not allowed.")
120             if $mother->is_root;
121            
122 0 0         _err("Found illegally combined with .")
123             if $mother->{is_complex};
124              
125             # this is a named type, parse it into a SimpleTypeNode
126             # and tell Mom about it
127 0           my $node = XML::Validator::Schema::SimpleTypeNode->parse($data);
128 0           $mother->add_daughter($node);
129 0           push @$node_stack, $node;
130              
131             }
132             }
133            
134             elsif ($name eq 'restriction') {
135 0 0         _err("Found outside a definition.")
136             unless $mother->isa('XML::Validator::Schema::SimpleTypeNode');
137 0           $mother->parse_restriction($data);
138             }
139              
140             elsif ( $name eq 'union' ) {
141 0 0         _err("Found outside a definition.")
142             unless $mother->isa('XML::Validator::Schema::SimpleTypeNode');
143             # The union might just have a 'memberTypes' attribute or it might
144             # contain a bunch of inline anonymous simpleTypes.
145              
146 0           my $node = XML::Validator::Schema::ModelNode->parse($data);
147 0           my $gran = $mother->{mother};
148              
149 0           $mother->add_daughter($node);
150              
151             # At parse time, the only node that gets inspected is the
152             # grandmother node, so let's load everything required at runtime
153             # onto that
154 0           $mother->{got_union} = 1;
155 0           $node->{is_union} = 1;
156 0           $node->{next_instance} = 0;
157 0           $gran->{members} = []; # array of member ElementNodes
158              
159 0 0         if ( _attr($data,'memberTypes') ) {
160             # Stuff stolen pretty indiscriminately from SimpleTypeNode
161 0           my @mts = split(/ +/,_attr($data,'memberTypes'));
162 0           foreach my $m ( @mts ) {
163 0           my $mbr = XML::Validator::Schema::ElementNode->new();
164 0           my $mt = XML::Validator::Schema::SimpleTypeNode->new();
165 0           $mt->{base} = $m;
166             # Why mother->root? well any old valid ref to root will do and
167             # I can't find one anywhere else...
168 0           my $base = $mother->root->{type_library}->find(name => $mt->{base});
169 0           my $type = $base->derive();
170 0           $mbr->{type} = $type;
171 0           $mbr->add_daughter($mt);
172 0           push(@{$gran->{members}},$mbr);
  0            
173 0           $node->{next_instance} ++;
174             }
175             }
176              
177 0           $node ->{name} = $gran->{name} . '__';
178 0           push @$node_stack,$node;
179              
180             }
181              
182             elsif ($name eq 'whiteSpace' or
183             $name eq 'pattern' or
184             $name eq 'enumeration' or
185             $name eq 'length' or
186             $name eq 'minLength' or
187             $name eq 'maxLength' or
188             $name eq 'minInclusive' or
189             $name eq 'minExclusive' or
190             $name eq 'maxInclusive' or
191             $name eq 'maxExclusive' or
192             $name eq 'totalDigits' or
193             $name eq 'fractionDigits') {
194 0 0         _err("Found <$name> outside a definition.")
195             unless $mother->isa('XML::Validator::Schema::SimpleTypeNode');
196 0           $mother->parse_facet($data);
197             }
198              
199             elsif ($name eq 'complexType') {
200 0           my $name = _attr($data, 'name');
201 0 0         if ($name) {
202 0 0         _err("Named complexType must be global.")
203             unless $mother->is_root;
204              
205             # this is a named type, parse it into an ComplexTypeNode
206             # and tell Mom about it
207 0           my $node = XML::Validator::Schema::ComplexTypeNode->parse($data);
208 0           $mother->add_daughter($node);
209 0           push @$node_stack, $node;
210              
211            
212             } else {
213 0 0         _err("Anonymous global complexType not allowed.")
214             if $mother->is_root;
215              
216             # anonymous complexTypes are just noted and passed on
217 0           $mother->{is_complex} = 1;
218             }
219            
220             }
221              
222             elsif ($name eq 'sequence' or $name eq 'choice' or $name eq 'all') {
223             # create a new node for the model
224 0           my $node = XML::Validator::Schema::ModelNode->parse($data);
225            
226             # add to current node's daughter list and become the current node
227 0           $mother->add_daughter($node);
228 0           push @$node_stack, $node;
229              
230             # all needs special support due to the restrictions on its use
231 0 0         $mother->{is_all} = 1 if $name eq 'all';
232             }
233              
234             elsif ($name eq 'annotation' or $name eq 'documentation') {
235             # skip
236             }
237              
238             else {
239             # getting here is bad news
240 0           _err("Unrecognized element '<$name>' found.");
241             }
242             }
243              
244             sub end_element {
245 0     0 1   my ($self, $data) = @_;
246 0           my $node_stack = $self->{node_stack};
247 0           my $node = $node_stack->[-1];
248 0           my $name = $data->{LocalName};
249              
250             # all done?
251 0 0         if ($name eq 'schema') {
252 0 0         croak("Module done broke, man. That element stack ain't empty!")
253             unless @$node_stack == 1;
254              
255             # finish up
256 0           $node_stack->[-1]->compile();
257              
258 0           return;
259             }
260              
261             # end of an element?
262 0 0         if ($name eq 'element') {
263 0           $node->compile();
264 0           pop @$node_stack;
265 0           return;
266             }
267              
268             # end of a model?
269 0 0 0       if ($name eq 'sequence' or $name eq 'choice' or $name eq 'all') {
      0        
270 0           pop @$node_stack;
271 0           return;
272             }
273              
274             # end of a named complexType?
275 0 0 0       if ($name eq 'complexType' and
276             $node->isa('XML::Validator::Schema::ComplexTypeNode')) {
277 0           $node->compile;
278              
279 0           $node->mother->remove_daughter($node);
280 0           pop @{$self->{node_stack}};
  0            
281 0           return;
282             }
283              
284             # end of a union?
285 0 0         if ( $name eq 'union' ) {
286             # Fail if it has no members
287 0 0         if ( $node->{is_union} ) {
288 0 0         if ( not defined($node->{next_instance}) ) {
289 0           die "Node is_union but has no next_instance!";
290             }else {
291 0 0         if ( $node->{next_instance} == 0 ) {
292 0           _err("Union defined with no members");
293             }
294             }
295             } else {
296 0           die "Node is a a union but not is_union - something is wrong.";
297             }
298 0           pop @$node_stack;
299 0           return;
300             }
301              
302             # end of named simpleType?
303 0 0 0       if ( $name eq 'simpleType' and
304             $node->isa('XML::Validator::Schema::SimpleTypeNode')
305             )
306             {
307 0           $node->check_constraints();
308 0           my $type = $node->compile();
309             # If the node doesn't have a name, set parent's type
310             # to be the type of this node
311 0 0         $node->mother->{type} = $type unless $node->{name};
312 0           $node->mother->remove_daughter($node);
313 0           pop @{$self->{node_stack}};
  0            
314 0           return;
315             }
316              
317             # end of an attribute?
318 0 0 0       if ($name eq 'attribute' and
319             $node->isa('XML::Validator::Schema::AttributeNode')) {
320 0           my $attr = $node->compile();
321 0           my $mother = $node->mother();
322 0           my $name = $attr->{name};
323              
324 0 0 0       if ($name and $mother->is_root) {
325             # named attribute in the root gets added to the attribute library
326 0           $mother->{attribute_library}->add(name => $name,
327             obj => $attr);
328             } else {
329             # attribute in an element goes on the attr array
330 0   0       push @{$mother->{attr} ||= []}, $attr;
  0            
331             }
332              
333 0           $node->mother->remove_daughter($node);
334 0           pop @{$self->{node_stack}};
  0            
335 0           return;
336             }
337            
338             # it's ok to fall off the end here, not all elements recognized in
339             # start_element need finalizing.
340             }
341              
342             1;