File Coverage

blib/lib/XML/Pastor/SimpleType.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1 10     10   54 use utf8;
  10         19  
  10         52  
2 10     10   245 use strict;
  10         18  
  10         260  
3 10     10   46 use warnings;
  10         25  
  10         252  
4 10     10   47 no warnings qw(uninitialized);
  10         17  
  10         395  
5            
6             #======================================================
7             package XML::Pastor::SimpleType;
8            
9 10     10   6253 use XML::LibXML;
  0            
  0            
10             use XML::Pastor::Type;
11            
12             use Scalar::Util qw(reftype);
13             use XML::Pastor::Util qw(getAttributeHash getChildrenHashDOM);
14            
15             our @ISA = qw(XML::Pastor::Type);
16            
17            
18            
19            
20            
21             #----------------------------------------------
22             # xml_validate_value
23             #----------------------------------------------
24             sub xml_validate_value {
25             my $self = shift;
26             my $path = shift || '';
27             my $type = $self->XmlSchemaType();
28             my $value = $self->__value;
29             $value = $self->normalize_whitespace($value);
30            
31             unless (defined $type) {
32             return ($self->xml_validate_further(@_) && $self->xml_validate_ancestors(@_));
33             }
34            
35             if (defined(my $prop = $type->length)) {
36             $prop = (reftype($prop) eq 'ARRAY') ? $prop : [$prop];
37             foreach my $len (@$prop) {
38             ($len == length($value)) or die "Pastor : Validate : $path : Length must be exactly '$len' for value '$value'";
39             }
40             }
41            
42             if (defined(my $prop = $type->minLength)) {
43             $prop = (reftype($prop) eq 'ARRAY') ? $prop : [$prop];
44             foreach my $minLen (@$prop) {
45             (length($value) >= $minLen) or die "Pastor : Validate : $path : Length must be minimum '$minLen' for value '$value'";
46             }
47             }
48            
49             if (defined(my $prop = $type->maxLength)) {
50             $prop = (reftype($prop) eq 'ARRAY') ? $prop : [$prop];
51             foreach my $maxLen (@$prop) {
52             (length($value) <= $maxLen) or die "Pastor : Validate : $path : Length must be maximum '$maxLen' for value '$value'";
53             }
54             }
55            
56             if (defined(my $prop = $type->regex)) {
57             $prop = (reftype($prop) eq 'ARRAY') ? $prop : [$prop];
58             my $pass=0;
59             foreach my $regex (@$prop) {
60             if ($value =~ /$regex/) {
61             $pass =1;
62             last;
63             }
64             }
65             $pass or die "Pastor : Validate : $path : Value does not match any of the given regexes. Value is '$value'";
66             }
67            
68             if (defined(my $prop = $type->pattern)) {
69             $prop = (reftype($prop) eq 'ARRAY') ? $prop : [$prop];
70             my $pass=0;
71             foreach my $pattern (@$prop) {
72             if ($value =~ /$pattern/) {
73             $pass =1;
74             last;
75             }
76             }
77             $pass or die "Pastor : Validate : $path : Value does not match any of the given patterns. Value is '$value'";
78             }
79            
80            
81             if (defined(my $enumeration = $type->enumeration)) {
82             (exists $enumeration->{$value}) or die "Pastor : Validate : $path : Not in the permitted enumeration : value '$value'";
83             }
84            
85            
86             if (defined(my $prop = $type->minInclusive)) {
87             $prop = (reftype($prop) eq 'ARRAY') ? $prop : [$prop];
88             foreach my $min (@$prop) {
89             ($value >= $min) or die "Pastor : Validate : $path : Value must be at least (minimum) '$min' : But value is '$value'";
90             }
91             }
92            
93             if (defined(my $prop = $type->maxInclusive)) {
94             $prop = (reftype($prop) eq 'ARRAY') ? $prop : [$prop];
95             foreach my $max (@$prop) {
96             ($value <= $max) or die "Pastor : Validate : $path : Value must be at most (maximum) '$max' : But value is '$value'";
97             }
98             }
99            
100             if (defined(my $prop = $type->minExclusive)) {
101             $prop = (reftype($prop) eq 'ARRAY') ? $prop : [$prop];
102             foreach my $min (@$prop) {
103             ($value > $min) or die "Pastor : Validate : $path : Value must be greater than '$min' : But value is '$value'";
104             }
105             }
106            
107             if (defined(my $prop = $type->maxExclusive)) {
108             $prop = (reftype($prop) eq 'ARRAY') ? $prop : [$prop];
109             foreach my $max (@$prop) {
110             ($value < $max) or die "Pastor : Validate : $path : Value must be less than '$max' : But value is '$value'";
111             }
112             }
113            
114            
115             # Digits part is shamelessly copied from XML::Validator::Schema by Sam Tregar
116             if (defined($type->totalDigits) || defined($type->fractionDigits)) {
117             # strip leading and trailing zeros for numeric constraints
118             my $digits = $value;
119             $digits =~ s/^([+-]?)0*(\d*\.?\d*?)0*$/$1$2/g;
120            
121             if (defined(my $prop=$type->totalDigits)) {
122             $prop = (reftype($prop) eq 'ARRAY') ? $prop : [$prop];
123             foreach my $tdigits (@$prop) {
124             die "Pastor : Validate : $path : Value has more total digits than the allowed '$tdigits'"
125             if $digits =~ tr!0-9!! > $tdigits;
126             }
127             }
128            
129             if (defined(my $prop=$type->fractionDigits)) {
130             $prop = (reftype($prop) eq 'ARRAY') ? $prop : [$prop];
131             foreach my $fdigits (@$prop) {
132             die "Pastor : Validate : $path : Value has more fraction digits than the allowed '$fdigits'"
133             if $digits =~ /\.\d{$fdigits}\d/;
134             }
135             }
136             }
137            
138            
139             return 1;
140             }
141            
142            
143            
144             #-----------------------------------------------------------------------------
145             # By default, this just returns TRUE. But it could be overriden by descendants (like 'date').
146             #-----------------------------------------------------------------------------
147             sub xml_validate_further {
148             return 1;
149             }
150            
151            
152             #-----------------------------------------------------------------------------
153             # Validate the ancestors. Base classes need to be validated.
154             #-----------------------------------------------------------------------------
155             sub xml_validate_ancestors {
156             my $self = shift;
157             my $value = $self->__value;
158             my @ancestors = $self->get_ancestors();
159            
160             foreach my $class (@ancestors) {
161             next unless (UNIVERSAL::can($class, 'new') && UNIVERSAL::can($class, 'xml_validate'));
162            
163             my $obj=$class->new(__value => $value);
164             return 0 unless $obj->xml_validate(@_);
165             }
166            
167             return 1;
168             }
169            
170            
171             #-----------------------------------------------------------------------------
172             # Normalize white space.
173             #-----------------------------------------------------------------------------
174             sub normalize_whitespace {
175             my $self = shift;
176             my $value = shift;
177             my $type = $self->XmlSchemaType();
178            
179             if (defined($type) and defined(my $prop = $type->whiteSpace)) {
180             $prop = (reftype($prop) eq 'ARRAY') ? $prop : [$prop];
181             foreach my $ws (@$prop) {
182             if ($ws =~ /^replace$/i) {
183             $value =~ s![\t\n\r]! !g;
184             } elsif ($ws =~ /^collapse$/i) {
185             $value =~ s!\s+! !g;
186             $value =~ s!^\s!!g;
187             $value =~ s!\s$!!g;
188             }
189             return $value; # only the first one gets treated!
190             }
191             }else {
192             my @ancestors = $self->get_ancestors();
193             foreach my $class(@ancestors) {
194             next unless UNIVERSAL::can($class, 'normalize_whitespace') && UNIVERSAL::can($class, 'new');
195             my $object = $class->new(__value=>$value);
196             my $nvalue = $object->normalize_whitespace($value);
197            
198             return $nvalue if ($nvalue ne $value);
199             }
200             }
201            
202             return $value;
203             }
204            
205             1;
206            
207             __END__