File Coverage

lib/XML/Schema/Type/Simple.pm
Criterion Covered Total %
statement 107 118 90.6
branch 44 58 75.8
condition 13 19 68.4
subroutine 20 25 80.0
pod 1 15 6.6
total 185 235 78.7


line stmt bran cond sub pod time code
1             #============================================================= -*-perl-*-
2             #
3             # XML::Schema::Type::Simple
4             #
5             # DESCRIPTION
6             # Module implementing a base class for simple XML Schema datatypes.
7             # Simple types are those that cannot contain other elements and
8             # cannot carry attributes.
9             #
10             # AUTHOR
11             # Andy Wardley
12             #
13             # COPYRIGHT
14             # Copyright (C) 2001 Canon Research Centre Europe Ltd.
15             # All Rights Reserved.
16             #
17             # This module is free software; you can redistribute it and/or
18             # modify it under the same terms as Perl itself.
19             #
20             # TODO
21             # * constrain() should accept lists of constraints, e.g.
22             # constrain(minInclusive => 2, scale => 4);
23             #
24             # * Fix strategy wrt defining 'name' and/or 'type' attributes. 'name'
25             # should define name of type within schema (e.g. myMoneyType), and
26             # 'type' or 'base' should define base?
27             #
28             # REVISION
29             # $Id: Simple.pm,v 1.2 2001/12/20 13:26:28 abw Exp $
30             #
31             #========================================================================
32              
33             package XML::Schema::Type::Simple;
34              
35 28     28   231 use strict;
  28         51  
  28         928  
36 28     28   133 use XML::Schema;
  28         51  
  28         477  
37 28     28   132 use XML::Schema::Type;
  28         50  
  28         613  
38 28     28   12414 use XML::Schema::Type::List;
  28         77  
  28         778  
39 28     28   13895 use XML::Schema::Type::Union;
  28         70  
  28         826  
40 28     28   16366 use XML::Schema::Type::Builtin;
  28         77  
  28         3001  
41 28     28   17307 use XML::Schema::Facet::Builtin;
  28         90  
  28         11977  
42 28     28   202 use XML::Schema::Scheduler;
  28         56  
  28         1927  
43              
44 28     28   301 use base qw( XML::Schema::Type XML::Schema::Scheduler );
  28         57  
  28         19568  
45 28     28   171 use vars qw( $VERSION $DEBUG $ERROR @OPTIONAL @SCHEDULES );
  28         55  
  28         66276  
46              
47             $VERSION = sprintf("%d.%02d", q$Revision: 1.2 $ =~ /(\d+)\.(\d+)/);
48             $DEBUG = 0 unless defined $DEBUG;
49             $ERROR = '';
50              
51             @OPTIONAL = qw( annotation );
52             @SCHEDULES = qw( instance );
53              
54              
55            
56             #------------------------------------------------------------------------
57             # init()
58             #
59             # TODO: fundamentals(), merging user-supplied facets/actions into lists.
60             #------------------------------------------------------------------------
61              
62             sub init {
63 213     213 1 309 my ($self, $config) = @_;
64 213         244 my ($base, $name, $value);
65 213         332 my $class = ref $self;
66              
67             # if called as base class constructor method then look for
68             # 'base' item in config and delegate to that constructor.
69             # e.g. XML::Schema::Type::Simple->new( base => 'string', ... );
70             # => XML::Schema::Type::string->new( ... );
71              
72 213 100 100     654 if ($class eq __PACKAGE__ && ($base = $config->{ base })) {
73 13 100       64 if ($class = $self->builtin($base)) {
74 12 50       45 $self->DEBUG("base package, delegating to $base ($class)\n")
75             if $DEBUG;
76 12         68 return $class->new($config);
77             }
78             else {
79 1         5 return $self->error("invalid base type: $base");
80             }
81             }
82              
83 200         1074 my ($mand, $option, $facets)
84 200         256 = @{ $self->_baseargs( qw( @MANDATORY %OPTIONAL @FACETS ) ) };
85              
86 200 100 100     580 $self->_mandatory($mand, $config)
87             || return if @$mand;
88              
89 198 50       1205 $self->_optional($option, $config)
90             || return;
91              
92             # default name to last element of package name
93 198   66     1167 $self->{ name } = $config->{ name } || $self->type();
94              
95 198         414 $self->{ _VARIETY } = 'atomic';
96              
97             # install facets
98 198         549 $self->{ _FACET_LIST } = [ ];
99 198         370 $self->{ _FACET_HASH } = { };
100              
101 198         504 while (@$facets) {
102 376         573 $name = shift(@$facets);
103 376 100       841 $value = ref $name ? undef : shift(@$facets);
104 376 100       1190 $self->constrain($name, $value)
105             || return undef;
106             }
107              
108             # need to know which facets were installed as inbuilt facets
109             # and which get added subsequently by user
110 196         244 $self->{ _FACET_ORIGIN } = @{ $self->{ _FACET_LIST } };
  196         447  
111              
112             # initialise scheduler
113 196 50       1294 $self->init_scheduler($config)
114             || return undef;
115              
116 196         2111 return $self;
117             }
118              
119              
120             #------------------------------------------------------------------------
121             # type()
122             #
123             # Return a string giving the name of the type, e.g. 'string', 'date'.
124             # If called on the base class, 'anyType' is returned, otherwise the
125             # type name is taken as the last element in the class name, e.g.
126             # XML::Schema::Type::string => 'string'.
127             #------------------------------------------------------------------------
128              
129             sub type {
130 193     193 0 259 my $self = shift;
131 193         283 my $class = ref $self;
132              
133 193 100       393 if ($class eq __PACKAGE__) {
134 2         8 return 'anyType';
135             }
136             else {
137 191         933 $class =~ /::(\w+)$/;
138 191         1008 return $1;
139             }
140             }
141              
142              
143             #------------------------------------------------------------------------
144             # builtin($type)
145             #
146             # Returns a class name against which new() can be called if the
147             # $type specified equates to a builtin type, e.g. string =>
148             # XML::Schema::Type::string, etc. Otherwise returns undef.
149             #------------------------------------------------------------------------
150              
151             sub builtin {
152 113     113 0 162 my ($self, $type) = @_;
153 113   66     371 my $class = ref $self || $self;
154              
155             # strip 'Simple' last element of XML::Schema::Type::Simple and
156             # replace with "$type" to get XML::Schema::Type::$type
157 113         836 $class =~ s/::\w+$/::$type/;
158              
159 113 100       1092 return UNIVERSAL::can($class, 'new') ? $class : undef;
160             }
161              
162              
163             #------------------------------------------------------------------------
164             # constrain($facet, $value)
165             #
166             # Add a new validation facet to the internal list.
167             #------------------------------------------------------------------------
168              
169             sub constrain {
170 387     387 0 616 my ($self, $name, $value) = @_;
171 387         729 my ($flist, $fhash) = @$self{ qw( _FACET_LIST _FACET_HASH ) };
172 387         407 my $facet;
173              
174             # ($name, $value) can be:
175             # 'name' => $facet_ref
176             # 'name' => $code_ref
177             # $facet_ref,
178             # $code_ref
179              
180 387 100       636 if (ref ($name)) {
181 116         150 $facet = $name;
182 116         177 $name = '';
183             }
184             else {
185 271         359 $facet = $value;
186             }
187              
188 387 100       1609 if (ref $facet eq 'CODE') {
    100          
189 114 50       248 $self->TRACE("CODE facet") if $DEBUG;
190 114         179 push(@$flist, $facet);
191 114 50       217 $fhash->{ $name } = $facet if $name;
192 114         511 return $facet; # return if facet is a coderef
193             }
194             elsif (UNIVERSAL::isa($facet, 'XML::Schema::Facet')) {
195 2 50       20 $name = $facet->name() unless $name;
196 2 50       6 $self->TRACE("OBJECT facet") if $DEBUG;
197             }
198             else {
199 271         502 my $pkg = "XML::Schema::Facet::$name";
200 271 100       527 $self->TRACE("NEW $pkg facet") if $DEBUG;
201 271 100       745 $value = { value => $value } unless ref $value;
202 271   100     1476 $facet = $pkg->new($value)
203             || return $self->error($pkg->error());
204             }
205              
206             # at this point, we can assume $facet is a XML::Schema::Facet or
207             # subclass; we call its install method to let it inspect the
208             # existing facet list/table to check for conflicts
209             # NOTE: facets don't do this yet, but should eventualy
210              
211 271 50       1068 $facet->install($flist, $fhash)
212             || return $self->error($facet->error());
213              
214 271         1147 return $facet;
215             }
216              
217              
218             #------------------------------------------------------------------------
219             # instance($text)
220             # instance($text, $xml_instance)
221             #
222             # Create a new instance of this type from a basic starting value (i.e.
223             # the input text read from the XML instance element). Creates a
224             # scratchpad $infoset hash which is passed first to the
225             # validate_instance() method and then to the activate_instance() method
226             # implemented by the XML::Schema::Scheduler base class. If called in
227             # the second form shown above then the second argument is assumed to
228             # be a reference to an XML instance represented by an XML::Schema::Instance
229             # object.
230             #------------------------------------------------------------------------
231              
232             sub instance {
233 428     428 0 1178 my ($self, $text, $instance) = @_;
234 428         700 $self->{ _ERROR } = '';
235              
236             # $infoset captures 3 stages in the life of an instance:
237             #
238             # text - unmodified input text
239             # value - post-validated value
240             # result - post-scheduling result (default: value)
241             #
242             # validating facets modify 'value'
243             # scheduled actions modify 'result'
244              
245 428 50       10418 my $infoset = ref $text ? $text : {
246             instance => $instance,
247             text => $text,
248             value => $text,
249             };
250              
251 428 50       885 $self->TRACE("infoset => ", $infoset) if $DEBUG;
252              
253             # if validation is successful then the 'value' is copied
254             # to 'result', the instance schedule is activated and the
255             # infoset returned.
256              
257             return $self->validate_instance($infoset)
258 428   66     7144 && do { $infoset->{ result } = $infoset->{ value } }
259             && $self->activate_instance($infoset)
260             && $infoset;
261             }
262              
263              
264              
265             #------------------------------------------------------------------------
266             # validate_instance(\%infoset)
267             #
268             # Calls the valid() method on all the validation facets for this type,
269             # passing the $infoset scratchpad hash and a self reference against
270             # which the facet can make callbacks. Returns true (1) if all facets
271             # validate the candidate instance data, or undef if not.
272             #------------------------------------------------------------------------
273              
274             sub validate_instance {
275 431     431 0 540 my ($self, $infoset) = @_;
276              
277 431 50       775 $self->TRACE("infoset => ", $infoset) if $DEBUG;
278              
279 431         461 foreach my $facet (@{ $self->{ _FACET_LIST } }) {
  431         921  
280 1019 100       2158 if (ref $facet eq 'CODE') {
281 336 100       867 &$facet($infoset, $self)
282             || return undef;
283             }
284             else {
285 683 100       1983 $facet->valid($infoset, $self)
286             || return $self->error($facet->error());
287             }
288             }
289              
290 327         1086 return 1;
291             }
292              
293              
294             #------------------------------------------------------------------------
295             # accessor methods
296             #------------------------------------------------------------------------
297              
298             sub facet {
299 40     40 0 69 my ($self, $name) = @_;
300 40         187 return $self->{ _FACET_HASH }->{ $name };
301             }
302              
303             sub variety {
304 3     3 0 12 my ($self, $name) = @_;
305 3         15 return $self->{ _VARIETY };
306             }
307              
308             sub annotation {
309 4     4 0 22 my $self = shift;
310 4 100       21 return @_ ? ($self->{ annotation } = shift) : $self->{ annotation };
311             }
312              
313             sub simple {
314 0     0 0 0 return 1;
315             }
316              
317             sub complex {
318 0     0 0 0 return 0;
319             }
320              
321             #------------------------------------------------------------------------
322             # visitor methods
323             #------------------------------------------------------------------------
324              
325             sub visit_facets {
326 1     1 0 2 my ($self, $visitor) = @_;
327 1         4 my ($facets, $origin) = @$self{ qw( _FACET_LIST _FACET_ORIGIN ) };
328              
329             # we skip over the first n facets as determined by
330             # _FACET_ORIGIN because they're the builtin ones
331              
332 1         5 foreach my $facet (@$facets[$origin..$#$facets]) {
333 1 50       9 $facet->accept($visitor)
334             || return $self->error($facet->error());
335             }
336              
337 0           return 1;
338             }
339            
340              
341              
342             #------------------------------------------------------------------------
343             # handler($instance, $element)
344             #
345             # Calls the simple_handler($self, $element) method on the $instance
346             # reference.
347             #
348             # TODO: we could optimise away this chain of method calls by having the
349             # instance Start() method unwrap the calls.
350             #------------------------------------------------------------------------
351              
352             sub handler {
353 0     0 0   my ($self, $instance, $element) = @_;
354 0   0       return $instance->simple_handler($self, $element)
355             || $self->error($instance->error());
356             }
357              
358             sub present {
359 0     0 0   my ($self, $view) = @_;
360 0           $view->view( simple => $self );
361             }
362              
363              
364             sub ID {
365 0     0 0   my $self = shift;
366 0           my $base = $self->{ base };
367 0 0         $base = "-|>-$base" if $base;
368 0           return "simpleType[$self->{ name }$base]";
369             }
370              
371             1;
372              
373             __END__