File Coverage

lib/XML/Schema/Handler/Complex.pm
Criterion Covered Total %
statement 44 106 41.5
branch 7 72 9.7
condition 6 19 31.5
subroutine 7 14 50.0
pod 1 10 10.0
total 65 221 29.4


line stmt bran cond sub pod time code
1             #============================================================= -*-perl-*-
2             #
3             # XML::Schema::Handler::Complex.pm
4             #
5             # DESCRIPTION
6             # Module implementing a parser handler for complex content.
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: Complex.pm,v 1.2 2001/12/20 13:26:27 abw Exp $
20             #
21             #========================================================================
22              
23             package XML::Schema::Handler::Complex;
24              
25 2     2   1185 use strict;
  2         5  
  2         78  
26 2     2   410 use XML::Schema::Handler;
  2         6  
  2         55  
27 2     2   11 use base qw( XML::Schema::Handler );
  2         7  
  2         161  
28 2     2   11 use vars qw( $VERSION $DEBUG $ERROR @SCHEDULES @MANDATORY );
  2         5  
  2         3451  
29              
30             $VERSION = sprintf("%d.%02d", q$Revision: 1.2 $ =~ /(\d+)\.(\d+)/);
31             $DEBUG = 0 unless defined $DEBUG;
32             $ERROR = '';
33              
34             @MANDATORY = qw( type element );
35             @SCHEDULES = qw( start_element start_child end_child end_element text );
36              
37              
38             #------------------------------------------------------------------------
39             # init(\%config)
40             #
41             # Initialiser method called by base class new() constructor.
42             #------------------------------------------------------------------------
43              
44             sub init {
45 2     2 1 6 my ($self, $config) = @_;
46            
47 2 100       13 $self->SUPER::init($config)
48             || return;
49              
50 1         4 my ($type, $element) = @$self{ qw( type element ) };
51 1         2 foreach my $schedule (@SCHEDULES) {
52 5         9 my $name = "_SCHEDULE_$schedule";
53 5         5 my @data = ( @{ $type->{ $name } }, @{ $element->{ $name } } );
  5         10  
  5         8  
54 5 50       14 $self->{ $name } = \@data if @data;
55             }
56              
57 1         7 $self->{ attributes } = $type->attributes();
58              
59 1         6 my $model = $self->{ model } = $type->content();
60              
61 1 50       4 if ($model) {
62 1         6 $self->{ empty } = $model->empty();
63 1         21 $self->{ mixed } = $model->mixed();
64             }
65             else {
66 0         0 $self->{ empty } = 1;
67 0         0 $self->{ mixed } = 0;
68             }
69              
70 1         11 return $self;
71             }
72              
73              
74             #------------------------------------------------------------------------
75             # start_element($instance, $name, \%attr)
76             #
77             # Called at the start tag of a complex element. The first argument
78             # is a reference to the XML::Schema::Instance in effect. The second
79             # argument provides the element name and the third, a reference to a
80             # hash array of attributes. The attributes are validated according to
81             # any attributes defined for the complex type of this element and then
82             # the content model is initialised ready for parsing element content
83             # by calling the start() method on the content particle. Finally,
84             # any actions scheduled on the start_element list are activated.
85             #------------------------------------------------------------------------
86              
87             sub start_element {
88 6     6 0 10 my ($self, $instance, $name, $attribs) = @_;
89              
90 6 50       136 $self->TRACE("instance => $instance, name => $name, attribs => ", $attribs)
91             if $DEBUG;
92              
93             my $attrgrp = $self->{ attributes }
94 6   50     19 || return $self->error("no attribute group");
95              
96 6   100     21 my $attributes = $attrgrp->validate($attribs)
97             || return $self->error($attrgrp->error());
98              
99 4         6 my @ids;
100             my @idrefs;
101              
102 4   50     38 my $magic = $attributes->{ _MAGIC } || { };
103 4         7 delete $attributes->{ _MAGIC };
104              
105 4 50       11 if (%$magic) {
106 0         0 local $" = ', ';
107 0 0       0 $self->DEBUG("zoweee! some magic!\n") if $DEBUG;
108 0         0 foreach my $mkey (keys %$magic) {
109 0 0       0 $self->DEBUG("magic: $mkey:\n") if $DEBUG;
110 0         0 foreach my $mitem (@{ $magic->{ $mkey } }) {
  0         0  
111 0 0       0 $self->DEBUG(" [ @$mitem ]\n") if $DEBUG;
112             }
113             }
114             }
115             #
116             # if ($tname eq 'ID') {
117             # $self->TRACE("found an ID attribute: ", $name) if $DEBUG;
118             # push(@ids, $attributes->{ $name });
119             # }
120             # elsif ($tname eq 'IDREF') {
121             # $self->TRACE("found an IDREF attribute: ", $attr) if $DEBUG;
122             # push(@idrefs, $name);
123             # }
124              
125             $self->{ element } = {
126 4         15 name => $name,
127             attributes => $attributes,
128             content => [ ],
129             };
130 4         28 $self->{ id_fix } = $magic->{ ID };
131 4         8 $self->{ idref_fix } = $magic->{ IDREF };
132              
133 4         10 my $model = $self->{ model };
134 4         4 my $particle;
135              
136             # fire up the content particle
137 4 0 33     13 if (! $self->{ empty } && $model && ($particle = $model->particle())) {
      33        
138 0         0 $self->{ particle } = $particle;
139 0 0       0 $particle->start()
140             || return $self->error($particle->error());
141             }
142              
143             # activate any scheduled actions for start of element
144             return $self->{ _SCHEDULE_start_element }
145 4 50       16 ? $self->activate_start_element($self)
146             : 1;
147             }
148              
149              
150             #------------------------------------------------------------------------
151             # end_element($instance, $name)
152             #
153             # Called at the end tag of a complex element. The $instance and $name
154             # arguments are as per start_element() above. Triggers validation of
155             # the intervening content model by calling end() on the active particle
156             # and then activates any actions scheduled on the end_element list.
157             # The $self blessed hash object acts as the infoset for collecting
158             # attributes and content for the complex element instance. It passes
159             # itself between the schedule callbacks, each of which is free to
160             # modify and/or supplement the internal data stored within it. The
161             # possibly modified $self is then return to the the caller of
162             # end_element() to indicate success.
163             #------------------------------------------------------------------------
164              
165             sub end_element {
166 0     0 0 0 my ($self, $instance, $name) = @_;
167              
168 0         0 my $element = $self->{ element };
169              
170             $self->throw($self->ID . " caught end of '$name' (expected $self->{ _NAME })")
171 0 0       0 unless $name eq $element->{ name };
172              
173 0 0       0 $self->TRACE("instance => $instance, name => $name") if $DEBUG;
174              
175 0 0       0 if (my $particle = $self->{ particle }) {
176 0 0       0 $particle->end($instance, $name)
177             || return $self->error($particle->error());
178             }
179              
180             # $self->{ result } = {
181             # name => $self->{ name },
182             # attributes => $self->{ attributes },
183             # content => $self->{ content },
184             # };
185              
186             my $result = $self->{ _SCHEDULE_end_element }
187 0 0       0 ? $self->activate_end_element($element)
188             : $element;
189              
190             # $element = $self->{ _SCHEDULE_end_element }
191             # ? $self->activate_end_element($element)
192             # : $element;
193              
194             # fixup ID
195 0         0 foreach my $id (@{ $self->{ id_fix } }) {
  0         0  
196 0         0 my ($type, $name, $value) = @$id;
197 0 0       0 $self->DEBUG("fixup ID for $type $name => $value\n") if $DEBUG;
198 0 0       0 $instance->id($value, $result)
199             || return $self->error($instance->error());
200             }
201              
202             # fixup IDREF (note, this doesn't lookahead - need to schedule at end
203              
204 0         0 foreach my $idref (@{ $self->{ idref_fix } }) {
  0         0  
205 0         0 my ($type, $name, $value) = @$idref;
206 0 0       0 $self->DEBUG("fixup IDREF for $type $name => $value\n") if $DEBUG;
207 0   0     0 my $ref = $instance->idref($value)
208             || return $self->error($instance->error());
209 0 0       0 if ($type eq 'attribute') {
210 0 0       0 $self->DEBUG("fixup IDREF attribute $name => ", $ref, "\n") if $DEBUG;
211 0         0 $result->{ attributes }->{ $name } = $ref;
212             }
213             }
214              
215             # $self->DEBUG("retuning element [$element] => { ", $self->_inspect($element), " }\n");
216 0         0 return $result;
217             }
218              
219              
220             #------------------------------------------------------------------------
221             # start_child($instance, $name, $attr)
222             #
223             # Called against an outer (parent) element handler when an inner (child)
224             # element is detected. Delegates the call to the current particle
225             # representing the content model and then activates any actions scheduled
226             # for this point.
227             #------------------------------------------------------------------------
228              
229             sub start_child {
230 0     0 0 0 my ($self, $instance, $name, $attr) = @_;
231 0         0 my ($particle, $element, $handler);
232              
233 0 0       0 $self->TRACE("instance => $instance, name => $name") if $DEBUG;
234              
235             return $self->error("empty content model cannot contain elements")
236 0 0       0 if $self->{ empty };
237              
238             ($particle = $self->{ particle })
239 0 0       0 || return $self->error("no particle");
240              
241 0 0       0 ($element = $particle->element($name))
242             || return $self->error($particle->error());
243              
244 0 0       0 ($handler = $element->handler($instance))
245             || return $self->error($element->error());
246              
247 0         0 my $child = {
248             name => $name,
249             attributes => $attr,
250             element => $element,
251             handler => $handler,
252             skip => 0, # TODO
253             error => undef,
254             };
255              
256             return $self->{ _SCHEDULE_start_child }
257 0 0       0 ? $self->activate_start_child($child)
258             : $child;
259             }
260              
261              
262             #------------------------------------------------------------------------
263             # end_child()
264             #------------------------------------------------------------------------
265              
266             sub end_child {
267 0     0 0 0 my ($self, $instance, $name, $child) = @_;
268              
269 0 0       0 $self->TRACE("instance => $instance, name => $name, child => ", $child) if $DEBUG;
270              
271             return $self->error("empty content model cannot contain elements")
272 0 0       0 if $self->{ empty };
273              
274             $child = $self->activate_end_child($child)
275             || return
276 0 0 0     0 if $self->{ _SCHEDULE_end_child };
277              
278             # use 'result' entry in child or child as it is
279 0 0       0 my $result = exists $child->{ result } ? $child->{ result } : $child;
280              
281 0 0       0 push(@{ $self->{ element }->{ content } }, $result)
  0         0  
282             if defined $result;
283              
284 0         0 return $child;
285             }
286              
287              
288             #------------------------------------------------------------------------
289             # text($instance, $text)
290             #
291             # Called to store character content.
292             #------------------------------------------------------------------------
293              
294             sub text {
295 0     0 0 0 my ($self, $instance, $text) = @_;
296              
297 0 0       0 $self->TRACE($self->_text_snippet($text))
298             if $DEBUG;
299              
300             return $self->error("empty content model cannot contain text")
301 0 0       0 if $self->{ empty };
302              
303             return $self->error('non-mixed content model cannot contain text')
304 0 0 0     0 unless $self->{ mixed } or $text =~ /^\s*$/;
305              
306 0 0       0 push(@{ $self->{ element }->{ content } }, $text) if $self->{ mixed };
  0         0  
307              
308 0         0 return 1
309             }
310              
311              
312             sub attributes {
313 3     3 0 6 my $self = shift;
314 3         9 return $self->{ element }->{ attributes };
315             }
316              
317             sub attribute_group {
318 0     0 0   my $self = shift;
319 0           return $self->{ attributes };
320             }
321              
322             sub content {
323 0     0 0   my $self = shift;
324 0           return $self->{ element }->{ content };
325             }
326              
327              
328              
329             sub ID {
330 0     0 0   my $self = shift;
331 0           return "Complex_Handler[$self->{ name }]";
332             }
333              
334             1;
335              
336             __END__