File Coverage

lib/XML/Schema/Attribute/Group.pm
Criterion Covered Total %
statement 189 208 90.8
branch 123 180 68.3
condition 32 68 47.0
subroutine 18 21 85.7
pod 1 15 6.6
total 363 492 73.7


line stmt bran cond sub pod time code
1             #============================================================= -*-perl-*-
2             #
3             # XML::Schema::Attribute::Group.pm
4             #
5             # DESCRIPTION
6             # Module implementing an attribute group which is used by the
7             # XML::Schema::Type::Complex module to store attributes for a
8             # complex type, and also to define Attribute Groups within a
9             # schema to represent relocatable collections of attributes.
10             #
11             # AUTHOR
12             # Andy Wardley
13             #
14             # COPYRIGHT
15             # Copyright (C) 2001 Canon Research Centre Europe Ltd.
16             # All Rights Reserved.
17             #
18             # This module is free software; you can redistribute it and/or
19             # modify it under the same terms as Perl itself.
20             #
21             # REVISION
22             # $Id: Group.pm,v 1.2 2001/12/20 13:26:27 abw Exp $
23             #
24             #========================================================================
25              
26             package XML::Schema::Attribute::Group;
27              
28 11     11   1448 use strict;
  11         26  
  11         452  
29              
30 11     11   64 use XML::Schema::Scope;
  11         18  
  11         316  
31 11     11   5622 use XML::Schema::Attribute;
  11         30  
  11         382  
32 11     11   73 use XML::Schema::Constants qw( :attribs );
  11         21  
  11         1392  
33              
34 11     11   60 use base qw( XML::Schema::Scope );
  11         20  
  11         854  
35 11     11   58 use vars qw( $VERSION $DEBUG $ERROR @MANDATORY @OPTIONAL );
  11         22  
  11         35930  
36              
37             $VERSION = sprintf("%d.%02d", q$Revision: 1.2 $ =~ /(\d+)\.(\d+)/);
38             $DEBUG = 0 unless defined $DEBUG;
39             $ERROR = '';
40              
41             @MANDATORY = qw( name );
42             @OPTIONAL = qw( namespace annotation );
43              
44              
45             #------------------------------------------------------------------------
46             # build regexen to match valid attribute usage and constraints values
47             #------------------------------------------------------------------------
48              
49             my @USE_OPTS = ( OPTIONAL, REQUIRED, PROHIBITED );
50             my $USE_REGEX = join('|', @USE_OPTS);
51             $USE_REGEX = qr/^$USE_REGEX$/;
52              
53              
54              
55             #------------------------------------------------------------------------
56             # init(\%config)
57             #
58             # Initiliasation method called by base class new() constructor.
59             # A reference to a hash array of configuration options can be specified
60             # as shown in this example:
61             #
62             # my $group = XML::Schema::Attribute::Group->new({
63             # attributes => {
64             # foo => XML::Schema::Attribute->new(...),
65             # bar => { name => 'bar', type => 'string' }
66             # baz => { type => 'string' }, # name implied by key 'baz'
67             # baz => { type => 'string', use => REQUIRED },
68             # boz => { type => 'string', required => 1 },
69             # wiz => 'string',
70             # },
71             # default_use => OPTIONAL,
72             # use => { # either specify use for each
73             # foo => REQUIRED,
74             # },
75             # required => [ qw( bar baz ) ], # or list each type
76             # }
77             #
78             #------------------------------------------------------------------------
79              
80             sub init {
81 55     55 1 98 my ($self, $config) = @_;
82 55         93 my ($name, $value);
83              
84 55 100       277 $self->SUPER::init($config)
85             || return;
86              
87 53   50     216 my $factory = $self->factory() || return;
88              
89 53   100     211 my $attribs = $config->{ attributes } || { };
90              
91             # first look for a default_use option, otherwise go with OPTIONAL
92 53   100     272 my $default_use = $self->{ default_use } = $config->{ default_use } || OPTIONAL;
93              
94 53 50       367 return $self->error_value('default_use', $default_use, @USE_OPTS)
95             unless $default_use =~ $USE_REGEX;
96              
97 53 50       129 $self->DEBUG("set default_use to $default_use\n") if $DEBUG;
98              
99             # then look for a 'use' hash array...
100 53         93 my $use = $config->{ use };
101 53 50       163 $use = { } unless ref $use eq 'HASH';
102            
103             # ...and check each entry is valid
104 53         166 foreach $name (keys %$use) {
105 0 0       0 return $self->error("unknown attribute in use hash: '$name'")
106             unless $attribs->{ $name };
107              
108 0         0 $value = $use->{ $name };
109              
110             # allow 0 and 1 as shorthand for OPTIONAL and REQUIRED
111 0 0       0 $value = ($value eq '1') ? REQUIRED
    0          
112             : ($value eq '0') ? OPTIONAL : $value;
113              
114 0 0       0 return $self->error_value("use for attribute '$name'", $value, @USE_OPTS)
115             unless $value =~ $USE_REGEX;
116              
117 0         0 $use->{ name } = $value;
118             }
119              
120             # check for required => { }, optional => { } and prohibited => { } options
121 53         115 foreach my $value (@USE_OPTS) {
122 159   100     393 my $list = $config->{ $value } || next;
123 18 100       57 $list = [ $list ] unless ref $list eq 'ARRAY';
124              
125 18         31 foreach $name (@$list) {
126 22 50       45 return $self->error("unknown attribute in $value list: '$name'")
127             unless $attribs->{ $name };
128 22         60 $use->{ $name } = $value;
129             }
130             }
131              
132 53         120 $self->{ use } = $use;
133              
134             # coerce attributes into objects if not already so
135             $self->{ attributes } = {
136 63         101 map {
137 53         149 my $a = $attribs->{ $_ };
138              
139 63 100       194 if ($factory->isa( attribute => $a )) {
140 3         14 $name = $a->name();
141             }
142             else {
143             # if it's not already an attribute object then make it so
144 60 100       191 $a = { type => $a } unless ref $a eq 'HASH';
145 60 100       161 $a->{ name } = $_ unless defined $a->{ name };
146 60 50       141 $a->{ scope } = $self unless defined $a->{ scope };
147              
148 60         79 $name = $a->{ name };
149              
150             # allow 'required => 1' to alias for 'use => REQUIRED'
151 60         84 foreach $value (@USE_OPTS) {
152 180 100       380 $a->{ use } = $value if $a->{ $value };
153             }
154              
155             # look for 'use' option
156 60 100       146 if (defined ($value = $a->{ use })) {
157 11 50       68 return $self->error_value("attribute '$name' use", $value, @USE_OPTS)
158             unless $value =~ $USE_REGEX;
159 11         19 $use->{ $name } = $value;
160             }
161              
162 60         184 $a = $factory->create( attribute => $a );
163             }
164 63 100       214 $use->{ $name } = $default_use
165             unless defined $use->{ $name };
166 63 50       121 $self->DEBUG("set use($name) to $use->{ $name }\n") if $DEBUG;
167 63         168 ($_, $a);
168             } keys %$attribs
169             };
170              
171             # look for attribute group(s)
172 53         121 $self->{ groups } = [ ];
173 53   100     231 my $groups = $config->{ groups } || [ ];
174 53 100       158 push(@$groups, $config->{ group }) if $config->{ group };
175              
176 53         113 foreach my $group (@$groups) {
177 5 50       15 $self->group($group)
178             || return;
179             }
180              
181             # see if a wildcard is defined or something that can be coerced into one
182 53         96 foreach my $item (qw( any not )) {
183 106 100 50     310 $config->{ wildcard } ||= { $item => $config->{ $item } }
184             if $config->{ $item };
185             }
186              
187 53         95 my $wildcard = $config->{ wildcard };
188 53 100       131 if ($wildcard) {
189 14 100 50     43 $wildcard = $factory->create( wildcard => $wildcard )
190             || return $self->error( $factory->error() )
191             unless $factory->isa( wildcard => $wildcard );
192 14         31 $self->{ wildcard } = $wildcard;
193             }
194              
195 53         452 return $self;
196             }
197              
198              
199             #------------------------------------------------------------------------
200             # attribute( $name ) # return named attributed
201             # attribute( $attr ) # add attribute
202             # attribute( name => $name, type => $type, ... ) # create and add
203             #
204             # Used to retrieve an existing attribute when called with a single
205             # non-reference argument. Used to define a new attribute when passed
206             # with a single reference to an attribute object or a hash reference
207             # or list of arguments which are used to create a new argument via the
208             # factory module.
209             #
210             #
211             #
212             # ...
213             #
214             #------------------------------------------------------------------------
215              
216             sub attribute {
217 23     23 0 32 my $self = shift;
218 23         39 my ($name, $args, $attr, $required);
219              
220 23   50     63 my $factory = $self->factory()
221             || return;
222              
223 23 100       89 if (ref $_[0]) {
    100          
224             # hash array or attribute object
225 1         3 $args = shift;
226             }
227             elsif (scalar @_ == 1) {
228             # name requesting specific attribute
229 11         26 $name = shift;
230 11   66     93 return $self->{ attributes }->{ $name }
231             || $self->error("no such attribute: $name");
232             }
233             else {
234 11         38 $args = { @_ };
235             }
236              
237 12 100       51 if ($factory->isa( attribute => $args )) {
238 1         4 $attr = $args;
239 1 50       5 $args = ref $_[0] eq 'HASH' ? shift : { @_ };
240             # define scope of attribute unless already set
241 1 50       4 $attr->scope($self)
242             unless defined $attr->scope();
243             }
244             else {
245             # define scope of attribute unless already set
246             $args->{ scope } = $self
247             if UNIVERSAL::isa($args, 'HASH')
248 11 50 33     94 && ! exists $args->{ scope };
249              
250 11   100     39 $attr = $factory->create( attribute => $args )
251             || return $self->error( $factory->error() );
252             }
253 10 50       37 defined ($name = $attr->name())
254             || return $self->error('no name specified for attribute');
255              
256 10 50       64 $self->DEBUG($self->ID, "->attribute( $name => ", $attr->ID, " )\n")
257             if $DEBUG;
258              
259 10         27 $self->{ attributes }->{ $name } = $attr;
260            
261 10 50       38 $self->DEBUG("setting use for $name\n") if $DEBUG;
262              
263             # allow 'required => 1' to alias for 'use => REQUIRED'
264 10         16 foreach my $usage (@USE_OPTS) {
265 30 100       85 $args->{ use } = $usage if $args->{ $usage };
266             }
267              
268             # now set usage
269             $self->use( $name => $args->{ use } || $self->{ default_use } )
270 10 50 66     70 || return;
271              
272 10         61 return $attr;
273             }
274              
275              
276             #------------------------------------------------------------------------
277             # attributes()
278             #
279             # Returns reference to a hash containing all current attributes defined,
280             # indexed by name.
281             #------------------------------------------------------------------------
282              
283             sub attributes {
284 1     1 0 2 my $self = shift;
285 1         4 return $self->{ attributes };
286             }
287              
288              
289             #------------------------------------------------------------------------
290             # group($group)
291             #
292             # Add a new attribute group as a sub-group of this group.
293             #------------------------------------------------------------------------
294              
295             sub group {
296 8     8 0 16 my ($self, $group) = @_;
297 8         12 my $name;
298              
299 8 50       19 if (ref $group) {
300             # looks like a new attribute group definition so create and
301             # register it in the current scope
302 8   50     47 $group = $self->attribute_group($group)
303             || return;
304              
305 8         14 $name = $group->name();
306             }
307             else {
308             # it's the name of an attribute group
309 0         0 $name = $group;
310             }
311              
312             # add group name to list of sub-groups defined
313 8         10 push(@{ $self->{ groups } }, $name);
  8         16  
314              
315             # return reference to new group or group name
316 8         38 return $group;
317             }
318            
319              
320              
321             #------------------------------------------------------------------------
322             # groups()
323             #
324             # Returns a reference to a list containing the names of all attribute
325             # groups defined as sub-groups of this group. To fetch a reference to
326             # a hash of all attribute groups defined within the current scope,
327             # call attribute_group() (inherited from XML::Schema::Scope) with no
328             # arguments. Call same method to define new attribute groups within
329             # this scope, but not directly attach them to this group.
330             #------------------------------------------------------------------------
331              
332             sub groups {
333 0     0 0 0 my $self = shift;
334 0         0 return $self->{ groups };
335             }
336              
337              
338              
339             #------------------------------------------------------------------------
340             # validate(\%attributes)
341             #
342             #------------------------------------------------------------------------
343              
344             sub validate {
345 47     47 0 442 my ($self, $inbound, $outbound, $scope) = @_;
346 47         55 my ($name, $attr, $value, $magic, $usage, $wildcard);
347              
348             # if $outbound is undefined then we're the parent group
349 47 100       84 my $parent = $outbound ? 0 : 1;
350 47   100     147 $outbound ||= { };
351              
352             # if we've been called as the sub-group of some higher attribute
353             # group validation then we need to bind our scope to that which
354             # it passed us
355 47 100       114 $self->{ scope } = $scope if $scope;
356              
357 47         84 my $use = $self->{ use };
358             my $attributes = $self->{ attributes }
359 47   50     111 || return $self->error("no attributes defined");
360              
361              
362             # walk through each of our defined attributes seeing if it
363             # appears in $inbound, validate and instantiate it and copy
364             # into $outbound
365              
366 47         65 keys %$attributes; # reset iterator
367              
368 47         190 while (($name, $attr) = each %$attributes) {
369              
370             # check usage contraints
371 103   33     257 $usage = $use->{ $name } || $self->{ default_use };
372              
373 103 50       168 $self->TRACE("testing attribute $name usage $usage\n") if $DEBUG;
374              
375 103 100       214 if (defined ($value = $inbound->{ $name })) {
376 84 100       161 return $self->error("attribute '$name' is prohibited")
377             if $usage eq PROHIBITED;
378             }
379             else {
380 19 100       45 return $self->error("required attribute '$name' not defined")
381             if $usage eq REQUIRED;
382              
383             # don't give PROHIBITED attributes a chance to provide defaults
384 18 100       47 next if $usage eq PROHIBITED;
385              
386             # must be OPTIONAL, so it's OK that the attribute is missing
387             # next;
388             }
389              
390             # instantiate attribute
391 100         262 ($value, $magic) = $attr->instance($value);
392              
393 100 100       217 if (defined $value) {
394 85         185 $outbound->{ $name } = $value;
395             }
396             else {
397 15         39 my $error = $attr->error();
398 15 100 66     87 return $self->error("$name attribute: $error")
399             unless $usage eq OPTIONAL && $error eq 'no value provided';
400             }
401              
402 97 50       170 if ($magic) {
403 0   0     0 my $list = $outbound->{ _MAGIC }->{ $magic->[0] } ||= [ ];
404 0         0 push(@$list, [ attribute => $name, $magic->[1] ]);
405 0 0       0 $self->DEBUG("detected '$magic->[0]' magic valued '$magic->[1]' in $name attribute\n")
406             if $DEBUG;
407             }
408              
409 97 50       176 $self->TRACE("attribute $name => ", $outbound->{ $name }) if $DEBUG;
410              
411             # all is well so delete entry from inbound hash
412 97         346 delete $inbound->{ $name };
413             }
414              
415             # any attributes left in the $inbound hash are those that don't
416             # correspond to an attribute defined within this group.
417              
418 42 100       100 if (%$inbound) {
419             # try delegating to any defined sub-groups
420 34         48 foreach $name (@{ $self->{ groups } }) {
  34         81  
421 17 50       36 $self->TRACE("testing sub-group: $name\n") if $DEBUG;
422              
423             # fetch attribute object from group name
424 17   50     58 my $group = $self->attribute_group($name)
425             || return;
426 17 100       54 $group->validate($inbound, $outbound, $self)
427             || return $self->error($group->error());
428             }
429            
430              
431             # look for a wildcard
432 33 100       102 if ($wildcard = $self->{ wildcard }) {
433 28 50       66 $self->TRACE("testing wildcard: $wildcard\n") if $DEBUG;
434 28         47 keys %$inbound; # reset iterator
435              
436 28         96 while (($name, $value) = each %$inbound) {
437 86 100       213 if ($wildcard->accept($name)) {
438 36 50       60 $self->TRACE("wildcard accepted $name => $value\n") if $DEBUG;
439 36         72 $outbound->{ $name } = $value;
440 36         134 delete $inbound->{ $name };
441             }
442             }
443             }
444             }
445            
446             # raise error for any attributes we don't know about
447 41         156 my @badguys = sort keys %$inbound;
448 41 100 100     147 if ($parent && @badguys) {
449 6 100       46 return $self->error("unexpected attribute",
450             @badguys > 1 ? 's: ' : ': ',
451             join(', ', @badguys));
452             }
453              
454 35         141 return $outbound;
455             }
456              
457              
458              
459             #------------------------------------------------------------------------
460             # wildcard()
461             # wildcard($new_wildcard)
462             #------------------------------------------------------------------------
463              
464             sub wildcard {
465 1     1 0 2 my $self = shift;
466 1         2 my $wildcard;
467              
468             return $self->{ wildcard }
469 1 50 0     4 || $self->error("no wildcard defined")
470             unless @_;
471              
472 1         6 my $factory = $self->factory();
473              
474 1 50       6 if ($factory->isa( wildcard => $_[0] )) {
475 0         0 $wildcard = shift;
476             }
477             else {
478 1   50     6 $wildcard = $factory->create( wildcard => @_ )
479             || return $self->error( $factory->error() );
480             }
481              
482 1         2 $self->{ wildcard } = $wildcard;
483              
484 1         15 return $wildcard;
485             }
486              
487              
488              
489             #------------------------------------------------------------------------
490             # name()
491             #
492             # Simple accessor method to return name value.
493             #------------------------------------------------------------------------
494              
495             sub name {
496 18     18 0 32 my $self = shift;
497 18         40 return $self->{ name };
498             }
499              
500              
501             #------------------------------------------------------------------------
502             # namespace( $namespace )
503             #
504             # Simple accessor method to return existing namespace value or set new
505             # namespace when called with an argument.
506             #------------------------------------------------------------------------
507              
508             sub namespace {
509 0     0 0 0 my $self = shift;
510 0 0       0 return @_ ? ($self->{ namespace } = shift) : $self->{ namespace };
511             }
512              
513              
514             #------------------------------------------------------------------------
515             # default_use($new_default)
516             #
517             # Accessor method to get (when called without arguments) or set (when
518             # called with a single true/false value) the default usage value as
519             # one of the strings 'optional', 'required' or 'prohibited'.
520             #------------------------------------------------------------------------
521              
522             sub default_use {
523 3     3 0 11 my $self = shift;
524              
525 3 100       12 return $self->{ default_use } unless @_;
526              
527 1         2 my $use = shift;
528 1 50       5 return $self->error_value('default_use() argument', $use, @USE_OPTS)
529             unless $use =~ $USE_REGEX;
530              
531 1         3 $self->{ default_use } = $use;
532             }
533              
534              
535             #------------------------------------------------------------------------
536             # use($name, $new_use)
537             #
538             # Accessor method to get (when called without arguments) or set (when
539             # called with a single true/false value) the default usage value as
540             # one of the strings 'optional', 'required' or 'prohibited'.
541             #------------------------------------------------------------------------
542              
543             sub use {
544 15     15 0 29 my ($self, $name, $use) = @_;
545              
546 15 50 0     53 $self->DEBUG("use($name, ", $use || '', ")\n") if $DEBUG;
547              
548 15 50       36 if (defined $name) {
549             return $self->error("no such attribute: '$name'")
550 15 100       49 unless defined $self->{ attributes }->{ $name };
551              
552 14 100       35 if (defined $use) {
553 13 50       100 return $self->error_value("use for attribute '$name'", $use, @USE_OPTS)
554             unless $use =~ $USE_REGEX;
555 13 50       89 return ($self->{ use }->{ $name } = $use) ? 1 : 0;
556             }
557             else {
558 1   33     6 return $self->{ use }->{ $name } || $self->error("no use");
559             }
560             }
561             else {
562 0 0       0 return $self->{ use } unless defined $name;
563             }
564             }
565              
566              
567             #------------------------------------------------------------------------
568             # required($name, $value)
569             #
570             # When called without any arguments, this method returns a reference
571             # to the internal hash table indicating which attributes are required.
572             # When called with a single argument, $name, it returns a boolean
573             # value to indicate if the named argument is required or not. When
574             # called with an additional argument, $value, the flag for the
575             # attribute is updated to the new value. Returns undef with an error
576             # set if the attribute name is not recognised.
577             #------------------------------------------------------------------------
578              
579             sub required {
580 30     30 0 48 my ($self, $name, $value) = @_;
581              
582 30 50 0     60 $self->DEBUG("required(", $name || '', ", ", $value || '', ")\n") if $DEBUG;
      0        
583              
584 30 100       45 if (defined $name) {
585             return $self->error("no such attribute: '$name'")
586 28 50       60 unless defined $self->{ attributes }->{ $name };
587              
588 28 100       37 if (defined $value) {
589 1 50       5 return $self->use( $name => $value ? REQUIRED : OPTIONAL );
590             }
591             else {
592 27 100       137 return $self->{ use }->{ $name } eq REQUIRED ? 1 : 0;
593             }
594             }
595             else {
596 2         3 my $use = $self->{ use };
597             return [
598 2 100       5 map { $use->{ $_ } eq REQUIRED ? $_ : () }
  10         24  
599             keys %$use
600             ];
601             }
602              
603             # not reached
604             }
605              
606              
607             #------------------------------------------------------------------------
608             # optional($name, $value)
609             #
610             # As per required() above, for OPTIONAL attributes.
611             #------------------------------------------------------------------------
612              
613             sub optional {
614 6     6 0 9 my ($self, $name, $value) = @_;
615              
616 6 50 0     14 $self->DEBUG("optional(", $name || '', ", ", $value || '', ")\n") if $DEBUG;
      0        
617              
618 6 100       9 if (defined $name) {
619             return $self->error("no such attribute: '$name'")
620 5 50       22 unless defined $self->{ attributes }->{ $name };
621              
622 5 50       7 if (defined $value) {
623 0 0       0 return $self->use( $name => $value ? OPTIONAL : REQUIRED );
624             }
625             else {
626 5 100       24 return $self->{ use }->{ $name } eq OPTIONAL ? 1 : 0;
627             }
628             }
629             else {
630 1         3 my $use = $self->{ use };
631             return [
632 1 100       3 map { $use->{ $_ } eq OPTIONAL ? $_ : () }
  5         15  
633             keys %$use
634             ];
635             }
636              
637             # not reached
638             }
639              
640              
641             #------------------------------------------------------------------------
642             # prohibited($name, $value)
643             #
644             # As per required() above, for PROHIBITED attributes.
645             #------------------------------------------------------------------------
646              
647             sub prohibited {
648 3     3 0 9 my ($self, $name, $value) = @_;
649              
650 3 50 0     7 $self->DEBUG("prohibited(", $name || '', ", ", $value || '', ")\n") if $DEBUG;
      0        
651              
652 3 100       7 if (defined $name) {
653             return $self->error("no such attribute: '$name'")
654 1 50       4 unless defined $self->{ attributes }->{ $name };
655              
656 1 50       3 if (defined $value) {
657 1 50       8 return $self->use( $name => $value ? PROHIBITED : OPTIONAL );
658             }
659             else {
660 0 0       0 return $self->{ use }->{ $name } eq PROHIBITED ? 1 : 0;
661             }
662             }
663             else {
664 2         3 my $use = $self->{ use };
665             return [
666 2 100       5 map { $use->{ $_ } eq PROHIBITED ? $_ : () }
  10         27  
667             keys %$use
668             ];
669             }
670              
671             # not reached
672             }
673              
674              
675              
676             sub ID {
677 0     0 0   my $self = shift;
678 0           return $self->{ name };
679             }
680              
681              
682             1;
683              
684             __END__