File Coverage

lib/XML/Schema/Instance.pm
Criterion Covered Total %
statement 35 112 31.2
branch 8 50 16.0
condition 5 46 10.8
subroutine 8 20 40.0
pod 1 15 6.6
total 57 243 23.4


line stmt bran cond sub pod time code
1             #============================================================= -*-perl-*-
2             #
3             # XML::Schema::Instance
4             #
5             # DESCRIPTION
6             # Module implementing an object for representing instance documents.
7             #
8             # AUTHOR
9             # Andy Wardley
10             #
11             # COPYRIGHT
12             # Copyright (C) 2001 Canon Research Centre Europe Ltd.
13             # All Rights Reserved.
14             #
15             # This module is free software; you can redistribute it and/or
16             # modify it under the same terms as Perl itself.
17             #
18             # REVISION
19             # $Id: Instance.pm,v 1.2 2001/12/20 13:26:27 abw Exp $
20             #
21             #========================================================================
22              
23             package XML::Schema::Instance;
24              
25 2     2   1142 use strict;
  2         6  
  2         83  
26 2     2   12 use XML::Schema;
  2         4  
  2         51  
27 2     2   10 use vars qw( $VERSION $DEBUG $ERROR $ETYPE @MANDATORY );
  2         5  
  2         154  
28 2     2   10 use base qw( XML::Schema::Base );
  2         5  
  2         4079  
29              
30             $VERSION = sprintf("%d.%02d", q$Revision: 1.2 $ =~ /(\d+)\.(\d+)/);
31             $DEBUG = 0 unless defined $DEBUG;
32             $ERROR = '';
33             $ETYPE = 'Instance';
34              
35             @MANDATORY = qw( schema );
36              
37              
38             #------------------------------------------------------------------------
39             # init(\%config)
40             #
41             # Initialiser method called by the base class new() method.
42             #------------------------------------------------------------------------
43              
44             sub init {
45 3     3 1 8 my ($self, $config) = @_;
46              
47 3 50       13 $self->TRACE("config => ", $config) if $DEBUG;
48              
49 3         5 my ($mand) = @{ $self->_baseargs( qw( @MANDATORY ) ) };
  3         18  
50 3 50       30 $self->_mandatory($mand, $config)
51             || return;
52              
53 3         10 $self->{ _ID } = { };
54 3         9 $self->{ _SCHEMA_STACK } = [ ];
55 3   33     21 $self->{ _FACTORY } = $config->{ FACTORY } || $XML::Schema::FACTORY;
56 3         7 $self->{ content } = [ ];
57            
58              
59 3         25 return $self;
60             }
61              
62              
63             #------------------------------------------------------------------------
64             # id($id, $value)
65             #
66             # Stores the specified value as an ID within the instance document,
67             # indexed by $id.
68             #------------------------------------------------------------------------
69              
70             sub id {
71 2     2 0 6 my ($self, $id, $ref) = @_;
72              
73 2 50       7 $self->TRACE("ID: ", $id, " => ", $ref) if $DEBUG;
74              
75 2 100       8 return $self->error("no value defined, did you mean to call idref()?")
76             unless defined $ref;
77              
78             return $self->error("an element is already defined with id '$id'")
79 1 50       5 if defined $self->{ _ID }->{ $id };
80              
81 1         4 $self->{ _ID }->{ $id } = $ref;
82              
83 1         4 return 1;
84             }
85              
86              
87             #------------------------------------------------------------------------
88             # idref($id)
89             #
90             # Returns the value of an ID previously specified via a call to id()
91             # or undef if the ID isn't defined, with an appropriate error message
92             # being set.
93             #------------------------------------------------------------------------
94              
95             sub idref {
96 2     2 0 5 my ($self, $idref) = @_;
97 2 50       7 $self->TRACE("IDREF: ", $idref) if $DEBUG;
98 2   66     23 return $self->{ _ID }->{ $idref }
99             || $self->error("no such id: $idref");
100             }
101              
102             #------------------------------------------------------------------------
103             # TODO:
104             # also need to implement entity and notation handlers...
105             # (see comments in XML::Schema::Type::Builtin header)
106             #------------------------------------------------------------------------
107              
108              
109             #------------------------------------------------------------------------
110             # schema_handler(...)
111             #
112             # Return a parser handler for parsing the top-level of the schema.
113             #------------------------------------------------------------------------
114              
115             sub schema_handler {
116 0     0 0 0 my $self = shift;
117 0 0       0 $self->TRACE() if $DEBUG;
118 0         0 my $schema = $self->{ schema };
119 0   0     0 return $schema->handler(@_)
120             || $self->error($schema->error());
121             }
122              
123             #------------------------------------------------------------------------
124             # simple_handler(...)
125             #
126             # Return a parser handler for parsing a simple element.
127             #------------------------------------------------------------------------
128              
129             sub simple_handler {
130 1     1 0 3 my ($self, $type, $element) = @_;
131              
132 1 50       4 if ($DEBUG) {
133 0 0 0     0 my $tid = ref($type) && UNIVERSAL::can($type, 'ID')
      0        
134             ? $type->ID : ($type || '');
135 0 0 0     0 my $eid = ref($element) && UNIVERSAL::can($element, 'ID')
      0        
136             ? $element->ID : ($element || '');
137 0         0 $self->TRACE("type => $tid, element => $eid");
138             }
139              
140             my $factory = $self->{ _FACTORY }
141 1   50     5 || return $self->error("no factory defined");
142              
143 1   33     19 return $factory->create( simple_handler => {
144             type => $type,
145             element => $element,
146             }) || $self->error($factory->error());
147             }
148              
149              
150             #------------------------------------------------------------------------
151             # complex_handler($type, $element)
152             #
153             # Return a parser handler for parsing a complex element.
154             #------------------------------------------------------------------------
155              
156             sub complex_handler {
157 0     0 0   my ($self, $type, $element) = @_;
158              
159 0 0         $self->TRACE("type => ", $type->ID, ", element => ", $element->ID) if $DEBUG;
160              
161             my $factory = $self->{ _FACTORY }
162 0   0       || return $self->error("no factory defined");
163              
164 0   0       return $factory->create( complex_handler => {
165             type => $type,
166             element => $element,
167             }) || $self->error($factory->error());
168             }
169              
170              
171             #------------------------------------------------------------------------
172             # schema_push($handler)
173             #
174             # Push a parser handler onto the top of the internal schema stack,
175             # making it the target for all subsequent parse events until masked
176             # by another handler pushed on top of it, or popped off the stack
177             # by a call to schema_pop() (e.g. at the element end tag)
178             #------------------------------------------------------------------------
179              
180             sub schema_push {
181 0     0 0   my ($self, $node) = @_;
182 0           push(@{ $self->{ _SCHEMA_STACK } }, $node);
  0            
183             }
184              
185              
186             #------------------------------------------------------------------------
187             # schema_pop()
188             #
189             # Pop the top parser handler from the internal schema stack and return it.
190             #------------------------------------------------------------------------
191              
192             sub schema_pop {
193 0     0 0   pop(@{ $_[0]->{ _SCHEMA_STACK } });
  0            
194             }
195              
196              
197             #------------------------------------------------------------------------
198             # schema_top()
199             #
200             # Return the top item on the internal schema stack.
201             #------------------------------------------------------------------------
202              
203             sub schema_top {
204 0     0 0   $_[0]->{ _SCHEMA_STACK }->[-1];
205             }
206              
207              
208             #------------------------------------------------------------------------
209             # expat_handlers()
210             #
211             # Returns a hash array for configuring XML::Parser to correctly use
212             # this schema instance as a recipient of parse events. May return a
213             # hash ref as { Init => ..., Start => ..., etc. } in which case the
214             # instance class is automatically used by the caller as the 'Style'
215             # value leading to this class receiving parse events. Alternately, a
216             # hash of the form { Style => 'MyClass', Handlers => { Start => ... } }
217             # may be passed to explicitly denote the intended recipient.
218             #------------------------------------------------------------------------
219              
220             sub expat_handlers {
221 0     0 0   my $self = shift;
222 0           my $schema = $self->{ schema };
223              
224 0   0       my $handler = $self->schema_handler()
225             || return;
226              
227 0 0         $handler->start_element($self)
228             || return $self->error($handler->error());
229              
230             return {
231             Init => sub {
232 0 0   0     $self->DEBUG($self->ID, "->[Init] $self\n") if $DEBUG;
233 0           my $expat = shift;
234 0           $expat->{ _SCHEMA_INSTANCE } = $self;
235 0           $expat->{ _SCHEMA_TEXT } = '';
236 0           $self->{ _SCHEMA_STACK } = [ $handler ];
237 0           $self->{ _SCHEMA_EXPAT } = $expat;
238             },
239 0           };
240             }
241              
242              
243             #========================================================================
244             # XML::Parser::Expat callbacks
245             #========================================================================
246              
247             #------------------------------------------------------------------------
248             # Start($expat, $name, %attr)
249             #------------------------------------------------------------------------
250              
251             sub Start {
252 0     0 0   my ($expat, $name, %attr) = @_;
253 0           my $self = $expat->{ _SCHEMA_INSTANCE };
254 0           my $stack = $self->{ _SCHEMA_STACK };
255 0           my $parent = $stack->[-1];
256 0           my $text;
257              
258 0 0         if ($DEBUG) {
259 0           my $attr = join(' ', map { "$_=\"$attr{$_}\"" } keys %attr);
  0            
260 0 0         $attr = " $attr" if $attr;
261 0           $self->TRACE("[Start] <$name$attr>");
262             }
263              
264             # flush any character content
265 0 0         if (length ($text = $expat->{ _SCHEMA_TEXT })) {
266 0 0         $self->TRACE("flushing text: '", $self->_text_snippet($text), "'") if $DEBUG;
267 0 0         $parent->text($self, $text)
268             || $self->parse_error($parent->error());
269 0           $expat->{ _SCHEMA_TEXT } = '';
270             }
271              
272 0   0       my $child = $parent->start_child($self, $name, \%attr)
273             || return $self->parse_error($parent->error());
274              
275             my $handler = $child->{ handler }
276             || return $self->parse_error($child->{ error } ||
277 0   0       "no child handler defined");
278              
279 0 0         $handler->start_element($self, @$child{ qw( name attributes ) })
280             || $self->parse_error($handler->error());
281            
282 0           push(@$stack, $handler);
283             }
284              
285              
286             #------------------------------------------------------------------------
287             # End($expat, $name)
288             #------------------------------------------------------------------------
289              
290             sub End {
291 0     0 0   my ($expat, $name) = @_;
292 0           my $self = $expat->{ _SCHEMA_INSTANCE };
293 0           my $stack = $self->{ _SCHEMA_STACK };
294 0           my $element = pop( @$stack );
295 0           my $text;
296              
297 0 0         $self->TRACE("[End] ") if $DEBUG;
298              
299             # flush any character content
300 0 0         if (length ($text = $expat->{ _SCHEMA_TEXT })) {
301 0 0         $self->TRACE("flushing text: '", $self->_text_snippet($text), "'") if $DEBUG;
302 0 0         $element->text($self, $text)
303             || $self->parse_error($element->error());
304 0           $expat->{ _SCHEMA_TEXT } = '';
305             }
306              
307 0   0       my $child = $element->end_element($self, $name)
308             || return $self->parse_error($element->error());
309              
310 0   0       my $parent = $stack->[-1]
311             || $self->parse_error("no parent element for $name");
312              
313 0   0       return $parent->end_child($self, $name, $child)
314             || $self->error($parent->error());
315             }
316              
317              
318             #------------------------------------------------------------------------
319             # Char($expat, $char)
320             #------------------------------------------------------------------------
321              
322             sub Char {
323 0     0 0   my ($expat, $char) = @_;
324              
325             # $self->TRACE("[Char] '$char'") if $DEBUG;
326              
327             # push character content onto buffer
328 0           $expat->{ _SCHEMA_TEXT } .= $char;
329             }
330              
331              
332             #------------------------------------------------------------------------
333             # Final($expat)
334             #------------------------------------------------------------------------
335              
336             sub Final {
337 0     0 0   my $expat = shift;
338 0           my $self = $expat->{ _SCHEMA_INSTANCE };
339 0           my $stack = $self->{ _SCHEMA_STACK };
340 0           my $element = pop( @$stack );
341              
342 0 0         $self->TRACE("[Final] calling $element->end()\n") if $DEBUG;
343              
344             # TODO: may need to flush text?
345              
346 0           delete $expat->{ _SCHEMA_INSTANCE };
347 0           delete $expat->{ _SCHEMA_TEXT };
348 0           delete $self->{ _SCHEMA_EXPAT };
349 0           delete $self->{ _SCHEMA_STACK };
350              
351              
352 0   0       my $result = $element->end_element($self)
353             || $self->parse_error($element->error());
354              
355             # $self->throw("instance finally popped off foreign handler (got $element not $self")
356             # unless $element == $self;
357              
358 0           return $result;
359             }
360              
361              
362             sub parse_error {
363 0     0 0   my $self = shift;
364 0           my $msg = join('', @_);
365 0           my $expat = $self->{ _SCHEMA_EXPAT };
366 0 0         die "?? lost expat instance ??\n" unless $expat;
367 0           die $expat->position_in_context(4), "\n$msg\n";
368             # $expat->xpcroak($msg);
369             }
370              
371             1;
372