File Coverage

blib/lib/XML/Validator/Schema/ElementNode.pm
Criterion Covered Total %
statement 31 101 30.6
branch 7 66 10.6
condition 2 20 10.0
subroutine 6 11 54.5
pod 1 7 14.2
total 47 205 22.9


line stmt bran cond sub pod time code
1             package XML::Validator::Schema::ElementNode;
2 5     5   45 use strict;
  5         9  
  5         165  
3 5     5   24 use warnings;
  5         9  
  5         155  
4              
5             =head1 NAME
6              
7             XML::Validator::Schema::ElementNode - an element node in a schema object
8              
9             =head1 DESCRIPTION
10              
11             This is an internal module used by XML::Validator::Schema to represent
12             element nodes derived from XML Schema documents.
13              
14             =cut
15              
16 5     5   25 use base qw(XML::Validator::Schema::Node);
  5         10  
  5         2984  
17 5     5   70 use XML::Validator::Schema::Util qw(_attr _err);
  5         14  
  5         7418  
18              
19             # create a node based on the contents of an found in the
20             # schema document
21             sub parse {
22 5     5 0 698 my ($pkg, $data) = @_;
23 5         29 my $self = $pkg->new();
24              
25 5         235 my $name = _attr($data, 'name');
26 5 50       14 _err('Found element without a name.')
27             unless $name;
28 5         19 $self->name($name);
29              
30 5         36 my $type_name = _attr($data, 'type');
31 5 50       12 if ($type_name) {
32 0         0 $self->{unresolved_type} = 1;
33 0         0 $self->{type_name} = $type_name;
34             }
35              
36 5         14 my $min = _attr($data, 'minOccurs');
37 5 50       16 $min = 1 unless defined $min;
38 5 50       27 _err("Invalid value for minOccurs '$min' found in <$name>.")
39             unless $min =~ /^\d+$/;
40 5         119 $self->{min} = $min;
41              
42 5         20 my $max = _attr($data, 'maxOccurs');
43 5 50       13 $max = 1 unless defined $max;
44 5 50 33     31 _err("Invalid value for maxOccurs '$max' found in <$name>.")
45             unless $max =~ /^\d+$/ or $max eq 'unbounded';
46 5         11 $self->{max} = $max;
47              
48 5         19 return $self;
49             }
50              
51             # override add_daughter to check parent-specific requirements
52             sub add_daughter {
53 14     14 1 128 my ($self, $d) = @_;
54              
55             # check that min/mix are 0 or 1 for 'all' contents
56 14 50 33     40 if ($self->{is_all} and $d->isa('XML::Validator::Schema::ElementNode')) {
57 0 0 0     0 _err("Element '$d->{name}' must have minOccurs of 0 or 1 because it is within an .")
58             unless ($d->{min} eq '0' or $d->{min} eq '1');
59 0 0 0     0 _err("Element '$d->{name}' must have maxOccurs of 0 or 1 because it is within an .")
60             unless ($d->{max} eq '0' or $d->{max} eq '1');
61             }
62              
63 14         57 return $self->SUPER::add_daughter($d);
64             }
65              
66             # check contents of an element against declared type
67             sub check_contents {
68 0     0 0   my ($self, $contents) = @_;
69              
70             # do type check if a type is declared
71 0 0 0       if ($self->{type}) {
    0          
72              
73             # Union isn't really a simple type. In a sense it isn't a type
74             # at all, if it is, it sure as hell isn't simple. It's just
75             # a rather laissez-faire view of what the type might be.
76             # Hence I've not handled union in SimpleType::check. As it's
77             # not handled directly in SimpleType, I've bastardized the usage
78             # of $self->{type} to just contain a string effectively indicating
79             # that it is an exception
80              
81 0           my ( $ok, $msg);
82 0 0         if ($self->{type} eq 'union' ) {
83             # it only has to match one of the member types:
84 0 0         if ( not defined($self->{members}) ){
85 0           die "Internal error: I aint got no members\n";
86             } else {
87 0 0         if (@{$self->{members}} == 0 ) {
  0            
88 0           _err("Element '$self->{name}' is a union with no members.");
89             }
90             }
91              
92 0           my $types = '';
93 0           $ok = 0;
94 0           foreach my $m ( @{$self->{members}} ) {
  0            
95 0 0         if ( not my $x = ref($m) ) {
96 0           die ("Internal error, that isn't a reference\n");
97             }
98 0           ( $ok, $msg ) = $m->{type}->check($contents);
99 0 0         last if $ok;
100 0           $types .= ' '.$m->{type}->{base}->{name};
101             }
102              
103 0 0         if ( not $ok ) {
104             # Just giving the error for the last one checked isn't
105             # really that helpful. We need to make it explicit that
106             # NONE of the tests succeeded.
107 0           $msg = "content does not match any of the union base types".
108             " [ $types ]";
109             }
110             } else {
111 0           ($ok, $msg) = $self->{type}->check($contents);
112             }
113 0 0         _err("Illegal value '$contents' in element <$self->{name}>, $msg")
114             unless $ok;
115             }
116              
117             # mixed content isn't supported, so all complex elements must be
118             # element only or have nothing but whitespace between the elements
119             elsif ($self->{is_complex} and $contents =~ /\S/) {
120 0           _err("Illegal character data found in element <$self->{name}>.");
121             }
122             }
123              
124             # check if a given name is a legal child, and return it if it is
125             sub check_daughter {
126 0     0 0   my ($self, $name) = @_;
127 0           my ($daughter) = grep { $_->{name} eq $name } ($self->daughters);
  0            
128              
129             # doesn't even exist?
130 0 0         _err("Found unexpected <$name> inside <$self->{name}>. This is not a valid child element.")
131             unless $daughter;
132              
133             # push on
134 0   0       push @{$self->{memory} ||= []}, $name;
  0            
135              
136             # check model
137 0 0         $self->{model}->check_model($self->{name}, $self->{memory})
138             if $self->{model};
139              
140             # does this daughter have a valid type? if not, attempt to elaborate
141 0 0         if ($daughter->{unresolved_type}) {
142 0           $self->root->complete_type($daughter);
143 0           ($daughter) = grep { $_->{name} eq $name } ($self->daughters);
  0            
144             }
145            
146             # is this daughter a dangling ref? if so, complete it
147 0 0         if ($daughter->{unresolved_ref}) {
148 0           $self->root->complete_ref($daughter);
149 0           ($daughter) = grep { $_->{name} eq $name } ($self->daughters);
  0            
150             }
151              
152 0           return $daughter;
153             }
154              
155             # check that attributes are kosher
156             sub check_attributes {
157 0     0 0   my ($self, $data) = @_;
158              
159             # get lists required and allowed attributes
160 0           my (@required, %allowed);
161 0 0         foreach my $attr (@{$self->{attr} || []}) {
  0            
162 0           $allowed{$attr->{name}} = $attr;
163 0 0         push(@required, $attr->{name}) if $attr->{required};
164             }
165              
166             # check attributes
167 0           my %saw;
168 0           foreach my $jcname (keys %$data) {
169 0           my $attr = $data->{$jcname};
170              
171             # attributes in the http://www.w3.org/2001/XMLSchema-instance
172             # namespace are processing instructions, not part of the
173             # document to be validated
174 0 0         next if $attr->{NamespaceURI}
175             eq 'http://www.w3.org/2001/XMLSchema-instance';
176              
177             # attributes in http://www.w3.org/2000/xmlns/ are namespace
178             # declarations and don't concern us
179 0 0         next if $attr->{NamespaceURI} eq 'http://www.w3.org/2000/xmlns/';
180              
181 0           my $name = $attr->{LocalName};
182 0           my $obj = $allowed{$name};
183 0 0         _err("Illegal attribute '$name' found in <$self->{name}>.")
184             unless $obj;
185 0           $saw{$name} = 1;
186            
187             # does this obj have an incomplete type? complete it
188 0 0         if ($obj->{unresolved_type}) {
189 0           $self->root->complete_attr_type($obj);
190             }
191              
192             # check value, if attribute is typed
193 0 0         if ($obj->{type}) {
194 0           my ($ok, $msg) = $obj->{type}->check($attr->{Value});
195 0 0         _err("Illegal value '$attr->{Value}' for attribute '$name' in <$self->{name}>, $msg")
196             unless $ok;
197             }
198             }
199            
200             # make sure all required attributes are present
201 0           foreach my $name (@required) {
202 0 0         _err("Missing required attribute '$name' in <$self->{name}>.")
203             unless $saw{$name};
204             }
205             }
206              
207             # finish
208             sub compile {
209 0     0 0   my $self = shift;
210              
211 0 0 0       if ($self->daughters and
212             ($self->daughters)[0]->isa('XML::Validator::Schema::ModelNode')) {
213 0           ($self->daughters)[0]->compile;
214             }
215             }
216              
217             # forget about the past
218             sub clear_memory {
219 0 0   0 0   @{$_[0]->{memory}} = () if $_[0]->{memory};
  0            
220             }
221              
222              
223             1;