File Coverage

blib/lib/Data/Validate/XSD.pm
Criterion Covered Total %
statement 144 217 66.3
branch 84 156 53.8
condition 19 68 27.9
subroutine 16 21 76.1
pod 7 7 100.0
total 270 469 57.5


line stmt bran cond sub pod time code
1             package Data::Validate::XSD;
2              
3 4     4   141094 use strict;
  4         11  
  4         306  
4              
5             =head1 NAME
6              
7             Data::Validate::XSD - Validate complex structures by definition
8              
9             =head1 SYNOPSIS
10              
11             use Data::Validate::XSD;
12              
13             my $validator = Data::Validate::XSD->new( \%definition );
14              
15             $errors = $validator->validate( \%data );
16              
17             warn Dumper($errors) if $errors;
18              
19             =head1 DESCRIPTION
20            
21             Based on xsd and xml validation, this is an attempt to provide those functions
22             without either xml or the hidous errors given out by modules like XPath.
23              
24             The idea behind the error reporting is that the errors can reflect the structure
25             of the original structure replacing each variable with an error code and message.
26             It is possible to work out a one dimention error reporting scheme too which I may
27             work on next.
28              
29             =head1 INVITATION
30              
31             If you find an example where the W3C definitions and this module differ then
32             please email the author and a new version with fixes can be released.
33              
34             If you find there is a certain type that your always using then let me know
35             I can consider adding the type to the default set and make the module more useful.
36              
37             =head1 EXAMPLES
38              
39             =head2 Definitions
40              
41             A definition is a hash containing information like an xml node containing children.
42              
43             An example definition for registering a user on a website:
44              
45              
46             $def = {
47             root => [
48             { name => 'input', type => 'newuser' },
49             { name => 'foo', type => 'string' },
50             ],
51              
52             simpleTypes => [
53             confirm => { base => 'id', match => '/input/password' },
54             rname => { base => 'name', minLength => 1 },
55             password => { base => 'id', minLength => 6 },
56             ],
57              
58             complexTypes => {
59             newuser => [
60             { name => 'username', type => 'token' },
61             { name => 'password', type => 'password' },
62             { name => 'confirm', type => 'confirm' },
63             { name => 'firstName', type => 'rname' },
64             { name => 'familyName', type => 'name', minOccurs => 0 },
65             { name => 'nickName', type => 'name', minOccurs => 0 },
66             { name => 'emailAddress', type => 'email', minOccurs => 1, maxOccurs => 3 },
67             [
68             { name => 'aim', type => 'index' },
69             { name => 'msn', type => 'email' },
70             { name => 'jabber', type => 'email' },
71             { name => 'irc', type => 'string' },
72             ]
73             ],
74             },
75             };
76              
77              
78             =head2 Data
79              
80             And this is an example of the data that would validate against it:
81              
82              
83             $data = {
84             input => {
85             username => 'abcdef',
86             password => '1234567',
87             confirm => '1234567',
88             firstName => 'test',
89             familyName => 'user',
90             nickName => 'foobar',
91             emailAddress => [ 'foo@bar.com', 'some@other.or', 'great@nice.con' ],
92             msn => 'foo@msn.com',
93             },
94             foo => 'extra content',
95             };
96              
97              
98             We are asking for a username, a password typed twice, some real names, a nick name,
99             between 1 and 3 email addresses and at least one instant message account, foo is an
100             extra string of information to show that the level is arbitary. bellow the definition
101             and all options are explained.
102              
103             =head2 Results
104              
105             The first result you get is a structure the second is a boolean, the boolean explains the total stuctures pass or fail status.
106              
107             The structure that is returned is almost a mirror structure of the input:
108              
109             $errors = {
110             input => {
111             username => 0,
112             password => 0,
113             confirm => 0,
114             firstName => 0,
115             familyName => 0,
116             nickName => 0,
117             emailAddress => 0,
118             }
119             },
120              
121             =head1 DETAILED DEFINITION
122              
123             =head2 Definition Root
124              
125             root - The very first level of all structures, it should contain the first
126             level complex type (see below). The data by default is a hash since
127             all xml have at least one level of xml tags names.
128              
129             import - A list of file names, local to perl that should be loaded to include
130             further and shared simple and complex types. Supported formats are
131             "perl code", xml and yml.
132              
133             simpleTypes - A hash reference containing each simple definition which tests a
134             scalar type (see below for format of each definition)
135            
136              
137             complexTypes - A hash reference containing each complex definition which tests a
138             structure (see below for definition).
139              
140              
141             =head2 Simple Types
142              
143             A simple type is a definition which will validate data directly, it will never validate
144             arrays, hashes or any future wacky structural types. In perl parlance it will only validate
145             SCALAR types. These options should match the w3c simple types definition:
146              
147             base - The name of another simple type to first test the value against.
148             fixed - The value should match this exactly.
149             pattern - Should be a regular expresion reference which matchs the value i.e qr/\w/
150             minLength - The minimum length of a string value.
151             maxLength - The maximum length of a string value.
152             match - An XPath link to another data node it should match.
153             notMatch - An XPath link to another data node it should NOT match.
154             enumeration - An array reference of possible values of which value should be one.
155             custom - Should contain a CODE reference which will be called upon to validate the value.
156             minInclusive - The minimum value of a number value inclusive, i.e greater than or eq to (>=).
157             maxInclusive - The maximum value of a number value inclusive, i.e less than of eq to (<=).
158             minExclusive - The minimum value of a number value exlusive, i.e more than (>).
159             maxExclusive - The maximum value of a number value exlusive, i.e less than (<).
160             fractionDigits - The maximum number of digits on a fractional number.
161              
162             =head2 Complex Types
163              
164             A complex type is a definition which will validate a hash reference, the very first structure,
165             'root' is a complex definition and follows the same syntax as all complex types. each complex
166             type is a list of data which should all occur in the hash, when a list entry is a hash; it
167             equates to one named entry in the hash data and has the following options:
168              
169             name - Required name of the entry in the hash data.
170             minOccurs - The minimum number of the named that this data should have in it.
171             maxOccurs - The maximum number of the named that this data should have in it.
172             type - The type definition which validates the contents of the data.
173              
174             Where the list entry is an array, it will toggle the combine mode and allow further list entries
175             With in it; this allows for parts of the sturcture to be optional only if different parts of the
176             stucture exist.
177              
178             =head1 INBUILT TYPES
179              
180             By default these types are available to all definitions as base types.
181              
182             string - /^.*$/
183             integer - /^[\-]{0,1}\d+$/
184             index - /^\d+$/
185             double - /^[0-9\-\.]*$/
186             token - /^\w+$/
187             boolean - /^1|0|true|false$/
188             email - /^.+@.+\..+$/
189             date - /^\d\d\d\d-\d\d-\d\d$/ + datetime
190             'time' - /^\d\d:\d\d$/ + datetime
191             datetime - /^(\d\d\d\d-\d\d-\d\d)?[T ]?(\d\d:\d\d)?$/ + valid_date method
192             percentage - minInclusive == 0 + maxInclusive == 100 + double
193              
194             =cut
195              
196 4     4   26 use Carp;
  4         8  
  4         409  
197 4     4   25 use Scalar::Util qw/looks_like_number/;
  4         11  
  4         403  
198 4     4   3902 use Date::Parse qw/str2time/;
  4         40145  
  4         13554  
199             our $VERSION = "1.05";
200              
201             # Error codes
202             my $NOERROR = 0x00;
203             my $INVALID_TYPE = 0x01;
204             my $INVALID_PATTERN = 0x02;
205             my $INVALID_MINLENGTH = 0x03;
206             my $INVALID_MAXLENGTH = 0x04;
207             my $INVALID_MATCH = 0x05;
208             my $INVALID_VALUE = 0x06;
209             my $INVALID_NODE = 0x07;
210             my $INVALID_ENUMERATION = 0x08;
211             my $INVALID_MIN_RANGE = 0x09;
212             my $INVALID_MAX_RANGE = 0x0A;
213             my $INVALID_NUMBER = 0x0B;
214             my $INVALID_COMPLEX = 0x0C;
215             my $INVALID_EXIST = 0x0D;
216             my $INVALID_MIN_OCCURS = 0x0E;
217             my $INVALID_MAX_OCCURS = 0x0F;
218             my $INVALID_CUSTOM = 0x10;
219             my $CRITICAL = 0x11;
220              
221             my @errors = (
222             0,
223             'Invalid Node Type',
224             'Invalid Pattern: Regex Pattern failed',
225             'Invalid MinLength: Not enough nodes present',
226             'Invalid MaxLength: Too many nodes present',
227             'Invalid Match: Node to Node match failed',
228             'Invalid Value, Fixed string did not match',
229             'Invalid Node: Required data does not exist for this node',
230             'Invalid Enum: Data not equal to any values supplied',
231             'Invalid Number: Less than allowable range',
232             'Invalid Number: Greater than allowable range',
233             'Invalid Number: Data is not a real number',
234             'Invalid Complex Type: Failed to validate Complex Type',
235             'Invalid Exists: Data didn\'t exist, and should.',
236             'Invalid Occurs: Minium number of occurances not met',
237             'Invalid Occurs: Maxium number of occurances exceeded',
238             'Invalid Custom Filter: Method returned false',
239             'Critical Problem:',
240             );
241              
242             my %complex_types = ();
243              
244             my %simple_types = (
245             string => { pattern => qr/.*/ },
246             integer => { pattern => qr/[\-]{0,1}\d+/ },
247             'index' => { pattern => qr/\d+/ },
248             double => { pattern => qr/[0-9\-\.]*/ },
249             token => { base => 'string', pattern => qr/\w+/ },
250             boolean => { pattern => qr/1|0|true|false/ },
251             email => { pattern => qr/.+@.+\..+/ },
252             date => { pattern => qr/\d\d\d\d-\d\d-\d\d/, base => 'datetime' },
253             'time' => { pattern => qr/\d\d:\d\d/, base => 'datetime' },
254             datetime => { pattern => qr/(\d\d\d\d-\d\d-\d\d)?[T ]?(\d\d:\d\d)?/, custom => sub { _test_datetime(@_) } },
255             percentage => { base => 'double', minInclusive => 0, maxInclusive => 100 },
256             );
257              
258             =head1 METHODS
259              
260             =head2 $class->new( $definition )
261              
262             Create a new validation object, debug will cause
263             All error codes to be replaced by error strings.
264              
265             =cut
266             sub new {
267 3     3 1 175 my ($class, $definition) = @_;
268              
269 3         14 my $self = bless { strict => 1 }, $class;
270              
271 3         17 $self->setDefinition( $definition );
272              
273 3         50 return $self;
274             }
275              
276             =head2 $class->newFromFile( $path, $filename, $debug )
277              
278             Create a new definition from a dumped perl file.
279              
280             =cut
281             sub newFromFile {
282 2     2 1 404 my ($class, $filename, @a) = @_;
283              
284 2 50       40 if(-f $filename) {
285 2         9 my $definition = $class->_load_file( $filename, 1 );
286 1         5 return $class->new( $definition, @a );
287             }
288 0         0 croak("Validation Error: Could not find Validate Configuration '$filename'");
289             }
290              
291             =head2 I<$validator>->validate( $data )
292              
293             Validate a set of data against this validator.
294             Returns an $errors structure or 0 if there were no errors.
295              
296             =cut
297             sub validate {
298 6     6 1 2736 my ($self, $data) = @_;
299 6         13 my $def = $self->{'definition'};
300              
301 6 50 33     46 if(defined($def->{'root'}) and defined($data)) {
302 6         25 return $self->_validate_elements( definition => $def->{'root'}, data => $data );
303             } else {
304 0 0       0 croak("VAL Error: No root document definition") if not defined($def->{'root'});
305 0 0       0 croak("VAL Error: No data provided") if not defined($data);
306             }
307             }
308              
309             =head2 I<$validator>->validateFile( $filename )
310              
311             Validate a file against this validator.
312              
313             =cut
314             sub validateFile {
315 2     2 1 1093 my ($self, $filename, @a) = @_;
316              
317 2 50       35 if(-f $filename) {
318 2         6 my $data = $self->_load_file( $filename );
319 2         7 return $self->validate( $data, @a );
320             }
321 0         0 croak("Validation Error: Could not find data to validate: '$filename'");
322            
323             }
324              
325             =head2 I<$validator>->setStrict( $bool )
326              
327             Should missing data be considered an error.
328              
329             =cut
330             sub setStrict {
331 0     0 1 0 my ($self, $bool) = @_;
332 0         0 $self->{'strict'} = $bool;
333             }
334              
335             =head2 I<$validator>->setDefinition( $definition )
336              
337             Set the validators definition, will load it (used internally too)
338              
339             =cut
340             sub setDefinition {
341 3     3 1 8 my ($self, $definition) = @_;
342 3         15 $self->{'definition'} = $self->_load_definition( $definition );
343             }
344              
345             =head2 I<$validator>->getErrorString( $error_code )
346              
347             Return a human readable string for each error code.
348              
349             =cut
350             sub getErrorString {
351 0     0 1 0 my ($self, $e) = @_;
352 0 0 0     0 if($e>0 and $e<=$#errors) {
353 0         0 return $errors[$e];
354             }
355 0         0 return 'Invalid error code';
356             }
357              
358             =head1 INTERNAL METHODS
359              
360             Only read on if you are interesting in knowing some extra stuff about
361             the internals of this module.
362              
363             =head2 I<$validator>->_load_definition( $definition )
364              
365             Internal method for loading a definition into the validator
366              
367             =cut
368             sub _load_definition
369             {
370 3     3   5 my ($self, $definition) = @_;
371              
372 3 50       28 $definition->{'simpleTypes'} = { %simple_types, %{$definition->{'simpleTypes'} || {}} };
  3         94  
373 3 50       14 $definition->{'complexTypes'} = { %complex_types, %{$definition->{'complexTypes'} || {}} };
  3         38  
374              
375 3 50       59 if(defined($definition->{'include'})) {
376 0 0       0 if(ref($definition->{'include'}) eq "ARRAY") {
377 0         0 foreach my $include (@{$definition->{'include'}}) {
  0         0  
378              
379 0 0       0 my $def = ref($include) ? $self->_load_definition( $include ) : $self->_load_definition_from_file( $include );
380            
381 0 0       0 if(defined($def->{'simpleTypes'})) {
382 0         0 $self->_push_hash($definition->{'simpleTypes'}, $def->{'simpleTypes'});
383             }
384              
385 0 0       0 if(defined($def->{'complexTypes'})) {
386 0         0 $self->_push_hash($definition->{'complexTypes'}, $def->{'complexTypes'});
387             }
388             }
389             } else {
390 0         0 croak("Validator Error: include format needs to be an Array []");
391             }
392             }
393 3         25 return $definition;
394             }
395              
396             =head2 I<$validator>->_load_definition_from_file( $filename )
397              
398             Internal method for loading a definition from a file
399              
400             =cut
401             sub _load_definition_from_file {
402 0     0   0 my ($self, $filename) = @_;
403 0         0 my $definition = $self->_load_file( $filename );
404 0         0 return $self->_load_definition( $definition );
405             }
406              
407             =head2 I<$validator>->_validate_elements( %p )
408              
409             Internal method for validating a list of elements;
410             p: definition, data, mode
411              
412             =cut
413             sub _validate_elements
414             {
415 24     24   72 my ($self, %p) = @_;
416              
417 24         36 my $definition = $p{'definition'};
418 24         35 my $data = $p{'data'};
419 24         38 my $errors = {};
420              
421             # This should be AND or OR and controls the logic flow of the data varify
422 24   100     96 my $mode = $p{'mode'} || 'AND';
423            
424 24 50       97 if(not UNIVERSAL::isa($definition, 'ARRAY')) {
425 0         0 croak("definition is not in the correct format: expected array");
426             }
427              
428 24         35 foreach my $element (@{$definition}) {
  24         52  
429              
430             # Element data check
431 59 100       177 if(UNIVERSAL::isa($element, 'HASH')) {
    50          
432            
433 53         89 my $name = $element->{'name'};
434              
435             # Skip element if it's not defined
436 53 50       114 next if(not $name);
437              
438 53 100       137 $element->{'minOccurs'} = 1 if not defined($element->{'minOccurs'});
439 53 100       124 $element->{'maxOccurs'} = 1 if not defined($element->{'maxOccurs'});
440 53 50       123 $element->{'type'} = 'string' if not defined($element->{'type'});
441              
442 53         212 my $terrors = $self->_validate_element(
443             definition => $element,
444             data => $data->{$name},
445             name => $name,
446             );
447            
448             # Fill Errors with required results.
449 53 100       204 $errors->{$name} = $terrors if $terrors;
450              
451             } elsif(UNIVERSAL::isa($element, 'ARRAY')) {
452              
453              
454 6         13 my $subr = {};
455 6 100       27 $subr = $self->_validate_elements(
456             definition => $element,
457             data => $data,
458             mode => $mode eq 'OR' ? 'AND' : 'OR',
459             );
460              
461 6 100 66     35 map { $errors->{$_} = $subr->{$_} } keys(%{$subr}) if $subr and ref($subr);
  3         24  
  2         7  
462             } else {
463 0         0 carp "This is a complex type, but it doesn't look like one: $element";
464             }
465             }
466              
467 24 100       71 if($mode eq 'OR') {
468             # Only invalidate parent if all elements have errored
469 3         5 foreach (%{$errors}) {
  3         10  
470 6 100       29 return 0 if not $errors->{$_};
471             }
472 0         0 return $errors;
473             }
474              
475 21 100       27 return %{$errors} ? $errors : 0;
  21         133  
476             }
477              
478             =head2 I<$validator>->_validate_element( %p )
479              
480             Internal method for validating a single element
481             p: data, definition, mode
482              
483             =cut
484             sub _validate_element {
485 53     53   173 my ($self, %p) = @_;
486              
487 53         83 my $definition = $p{'definition'};
488 53         77 my $data = $p{'data'};
489 53         66 my $name = $p{'name'};
490              
491 53         60 my @results;
492 53         66 my $proped = 0;
493              
494 53 100 100     271 if(ref($data) ne "ARRAY" and defined($data)) {
495 36         46 $proped = 1;
496 36         91 $data = [$data];
497             }
498              
499             # minOccurs checking
500 53 100       135 if($definition->{'minOccurs'} >= 1) {
501 49 100       110 if(defined($data)) {
502 40 50       51 if($definition->{'minOccurs'} > @{$data}) {
  40         124  
503 0         0 return $INVALID_MIN_OCCURS;
504             }
505             } else {
506 9         33 return $INVALID_EXIST;
507             }
508             }
509              
510 44 100       112 if(defined($data)) {
511              
512             # maxOccurs Checking
513 42 100       122 if($definition->{'maxOccurs'} ne 'unbounded') {
514 40 100       53 if($definition->{'maxOccurs'} < @{$data}) {
  40         108  
515 4         14 return $INVALID_MAX_OCCURS;
516             }
517             }
518            
519 38         54 foreach my $element (@{$data}) {
  38         73  
520             # fixed and default checking
521 40 50       96 if(defined($definition->{'fixed'})) {
522 0 0 0     0 if(ref($element) ne "" or ($element and $element ne $definition->{'fixed'})) {
      0        
523 0         0 push @results, $INVALID_VALUE;
524 0         0 next;
525             }
526             }
527              
528 40 50       107 if(defined($definition->{'default'})) {
529 0 0       0 $element = $definition->{'default'} if not defined($element);
530             }
531              
532 40         53 my %po;
533 40         78 foreach ('minLength', 'maxLength') {
534 80 100       257 $po{$_} = $definition->{$_} if defined($definition->{$_});
535             }
536              
537             # Element type checking
538 40         189 my ($result, $te) = $self->_validate_type(
539             type => $definition->{'type'},
540             data => $element,
541             %po, #Passable Options
542             );
543              
544 40 100       211 push @results, $result if $result;
545             }
546             }
547              
548 40 100       107 if(@results > 0) {
549 8 50       37 return ($proped ? $results[0] : \@results);
550             }
551 32         132 return 0;
552             }
553              
554             =head2 I<$validator>->_validate_type( %p )
555              
556             Internal method for validating a single data type
557              
558             =cut
559             sub _validate_type {
560 42     42   124 my ($self, %p) = @_;
561              
562 42         79 my $data = delete($p{'data'});
563 42         78 my $type = delete($p{'type'});
564 42         72 my $definition = $self->{'definition'};
565 42         82 my %pdef = %p;
566              
567 42 100       138 if(defined($definition->{'simpleTypes'}->{$type})) {
    50          
568              
569 30         40 my $typedef = { %{$definition->{'simpleTypes'}->{$type}}, %pdef };
  30         127  
570              
571             # Base type check
572 30 100       93 if(defined($typedef->{'base'})) {
573 2         15 my $err = $self->_validate_type(
574             type => $typedef->{'base'},
575             data => $data,
576             );
577 2 50       16 return $err if $err;
578             }
579              
580             # Pattern type check
581 30 50 33     167 if(defined($typedef->{'pattern'}) and ref($typedef->{'pattern'}) eq 'REGEX') {
582 0 0       0 if($data !~ $typedef->{'pattern'}) {
583 0         0 return $INVALID_PATTERN;
584             }
585             }
586              
587             # Custom method check
588 30 100       82 if(defined($typedef->{'custom'})) {
589 6         9 my $method = $typedef->{'custom'};
590              
591 6 50 33     30 if(ref($method) ne 'CODE' or not $method->($data, $typedef)) {
592 0         0 return $INVALID_CUSTOM;
593             }
594             }
595              
596             # Length checks
597 30 100       91 if(defined($typedef->{'maxLength'})) {
598 6 100       21 if(length($data) > $typedef->{'maxLength'}) {
599 2         10 return $INVALID_MAXLENGTH;
600             }
601             }
602              
603 28 100       67 if(defined($typedef->{'minLength'})) {
604 4 100       14 if(length($data) < $typedef->{'minLength'}) {
605 2         10 return $INVALID_MINLENGTH;
606             }
607             }
608              
609             # Match another node
610 26 50 33     140 if(defined($typedef->{'match'}) or defined($typedef->{'notMatch'})) {
611 0   0     0 my $path = $typedef->{'match'} || $typedef->{'notMatch'};
612 0         0 my $result = $self->_find_value( path => $path, data => $data );
613 0 0 0     0 if( ($data ne $result and $typedef->{'match'})
      0        
      0        
614             or ($data eq $result and $typedef->{'notMatch'})) {
615 0         0 return $INVALID_MATCH;
616             }
617             }
618              
619 26 50       63 if(defined($typedef->{'enumeration'})) {
620 0 0       0 if(ref($typedef->{'enumeration'}) ne 'ARRAY') {
621 0         0 croak("Validator Error: Enumberation not of the correct type");
622             }
623 0         0 my $found = 0;
624 0         0 foreach (@{$typedef->{'enumeration'}}) {
  0         0  
625 0 0       0 $found = 1 if $_ eq $data;
626             }
627 0 0       0 return $INVALID_ENUMERATION if not $found;
628             }
629              
630 26 100 33     415 if(looks_like_number($data)) {
    50 33        
      33        
      33        
631 3 50 33     13 return $INVALID_MIN_RANGE if defined($typedef->{'minInclusive'}) and $data < $typedef->{'minInclusive'};
632 3 50 33     10 return $INVALID_MAX_RANGE if defined($typedef->{'maxInclusive'}) and $data > $typedef->{'maxInclusive'};
633 3 50 33     20 return $INVALID_MIN_RANGE if defined($typedef->{'minExclusive'}) and $data <= $typedef->{'minExclusive'};
634 3 50 33     15 return $INVALID_MAX_RANGE if defined($typedef->{'maxExclusive'}) and $data >= $typedef->{'maxExclusive'};
635              
636             # return $INVALID_FRACTION if defined($typedef->{'fractionDigits'}) and $data !~ /\.(\d{})$/;
637              
638             } elsif(defined($typedef->{'minInclusive'}) or defined($typedef->{'maxInclusive'}) or
639             defined($typedef->{'minExclusive'}) or defined($typedef->{'maxExclusive'}) or
640             defined($typedef->{'fractionDigits'})) {
641 0         0 return $INVALID_NUMBER;
642             }
643              
644             } elsif(defined($definition->{'complexTypes'}->{$type})) {
645 12         24 my $typedef = $definition->{'complexTypes'}->{$type};
646 12 50       34 if(ref($data) eq "HASH") {
647 12         48 return $self->_validate_elements( definition => $typedef, data => $data );
648             } else {
649 0         0 return $INVALID_COMPLEX;
650             }
651             } else {
652 0         0 croak("Validator Error: Can not find type definition '$type'");
653 0         0 return $CRITICAL;
654             }
655            
656 26         79 return $NOERROR;
657             }
658              
659             =head2 I<$validator>->_find_value( %p )
660              
661             Internal method for finding a value match (basic xpath)
662              
663             =cut
664             sub _find_value
665             {
666 0     0   0 my ($self, %p) = @_;
667             # Remove root path, and stop localisation
668 0 0       0 if($p{'path'} =~ s/^\///){ $p{'data'} = $self->{'data'}; }
  0         0  
669              
670 0         0 my @paths = split('/', $p{'path'});
671 0         0 my $data = $p{'data'};
672              
673 0         0 foreach my $path (@paths) {
674 0 0       0 if(UNIVERSAL::isa($data, 'HASH')) {
675 0 0       0 if(defined($data->{$path})) {
676 0         0 $data = $data->{$path};
677             } else {
678 0 0       0 carp "Validator Error: Can't find nodes for '$p{'path'}' in _find_value\n" if $self->{'debug'};
679             }
680             } else {
681 0 0       0 carp "Validator Error: Can't find nodes for '$p{'path'}' in _find_value\n" if $self->{'debug'};
682             }
683             }
684 0         0 return $data;
685             }
686              
687             =head2 I<$validator>->_push_hash( $dest, $source )
688              
689             Internal method for copying a hash to another
690              
691             =cut
692             sub _push_hash
693             {
694 0     0   0 my($self, $dest, $source) = @_;
695              
696 0         0 foreach my $key (keys(%{$source})) {
  0         0  
697 0 0       0 if(not $dest->{$key}) {
698 0         0 $dest->{$key} = $source->{$key};
699             }
700             }
701 0         0 return $dest;
702             }
703              
704             =head2 I<$validator>->_load_file( $file )
705              
706             Internal method for loading a file, must be valid perl syntax.
707             Yep that's right, be bloody careful when loading from files.
708              
709             =cut
710             sub _load_file {
711 4     4   7 my ($self, $filename, $def) = @_;
712 4         114 open( VALIDATE, $filename );
713 4         124 my $content = join('', );
714 4         43 close( VALIDATE );
715            
716 4         6 my $data;
717 4 100       15 if($content =~ /^<\?xml/) {
718             # XML File
719 1     1   72 eval("use Data::Validate::XSD::ParseXML");
  1         683  
  0            
  0            
720 1 50       409 croak("Did you forget to install XML::SAX? ($@)") if $@;
721 0         0 my $parser = Data::Validate::XSD::ParseXML->new( $content );
722 0 0 0     0 if($def and $content =~ /XMLSchema/) {
723 0         0 $data = $parser->definition();
724             } else {
725 0         0 $data = $parser->data();
726             }
727             } else {
728 3         289 $data = eval('{ '.$content.' }');
729 3 50       16 croak("Validator Error! $@") if $@;
730             }
731 3         10 return $data;
732             }
733              
734             =head2 $validate->_test_datetime( $typedef )
735              
736             Test a date time range is a valid date.
737              
738             =cut
739             sub _test_datetime {
740 6     6   10 my ($data, $typedef) = @_;
741 6 50       16 if($data) {
742 6         21 my $epoch = str2time( $data );
743 6 50       1556 if($epoch) {
744 6         33 return 1;
745             }
746             }
747 0         0 return undef;
748             }
749              
750             =head1 KNOWN BUGS
751              
752             * XML and YML suport not added yet.
753             * Fraction Didgets test doesn't work yet.
754              
755             =head1 AUTHOR
756              
757             Copyright, Martin Owens 2007-2008, Affero General Public License (AGPL)
758              
759             http://www.fsf.org/licensing/licenses/agpl-3.0.html
760              
761             =cut
762             1;