File Coverage

lib/XML/Compile/Translate.pm
Criterion Covered Total %
statement 672 718 93.5
branch 373 528 70.6
condition 133 192 69.2
subroutine 62 66 93.9
pod 3 46 6.5
total 1243 1550 80.1


line stmt bran cond sub pod time code
1             # Copyrights 2006-2019 by [Mark Overmeer ].
2             # For other contributors see ChangeLog.
3             # See the manual pages for details on the licensing terms.
4             # Pod stripped from pm file by OODoc 2.02.
5             # This code is part of distribution XML-Compile. Meta-POD processed with
6             # OODoc into POD and HTML manual-pages. See README.md
7             # Copyright Mark Overmeer. Licensed under the same terms as Perl itself.
8              
9             package XML::Compile::Translate;
10 50     50   300 use vars '$VERSION';
  50         86  
  50         2293  
11             $VERSION = '1.63';
12              
13              
14 50     50   237 use warnings;
  50         79  
  50         1025  
15 50     50   198 use strict;
  50         99  
  50         866  
16 50     50   195 no warnings 'recursion'; # trees can be quite deep
  50         77  
  50         1903  
17              
18             # Errors are either in _class 'usage': called with request
19             # or 'schema': syntax error in schema
20              
21 50     50   237 use Log::Report 'xml-compile', syntax => 'SHORT';
  50         84  
  50         286  
22 50     50   11703 use List::Util qw/first max/;
  50         81  
  50         3100  
23              
24 50     50   284 use XML::Compile::Schema::Specs;
  50         82  
  50         1242  
25 50     50   16786 use XML::Compile::Schema::BuiltInFacets;
  50         120  
  50         2647  
26 50     50   318 use XML::Compile::Schema::BuiltInTypes qw/%builtin_types/;
  50         1977  
  50         4134  
27 50         2250 use XML::Compile::Util qw/pack_type unpack_type type_of_node SCHEMA2001
28 50     50   292 unpack_id/;
  50         74  
29 50     50   16672 use XML::Compile::Iterator ();
  50         97  
  50         438785  
30              
31             my %translators =
32             ( READER => 'XML::Compile::Translate::Reader'
33             , WRITER => 'XML::Compile::Translate::Writer'
34             , TEMPLATE => 'XML::Compile::Translate::Template'
35             );
36              
37             # Elements from the schema to ignore: remember, we are collecting data
38             # from the schema, but only use selective items to produce processors.
39             # All the sub-elements of these will be ignored automatically
40             # Don't known whether we ever need the notation... maybe
41             my $assertions = qr/assert|report/;
42             my $id_constraints = qr/unique|key|keyref/;
43             my $ignore_elements = qr/^(?:notation|annotation|$id_constraints|$assertions)$/;
44              
45             my $particle_blocks = qr/^(?:sequence|choice|all|group)$/;
46             my $attribute_defs = qr/^(?:attribute|attributeGroup|anyAttribute)$/;
47              
48              
49             sub new($@)
50 771     771 1 1450 { my ($baseclass, $trans) = (shift, shift);
51 771 50       1806 my $class = $translators{$trans}
52             or error __x"translator back-end {name} not defined", name => $trans;
53              
54 771         36867 eval "require $class";
55 771 50       8727 fault $@ if $@;
56              
57 771         4021 (bless {}, $class)->init( {@_} );
58             }
59              
60             sub init($)
61 771     771 0 1543 { my ($self, $args) = @_;
62 771 50       2581 $self->{nss} = $args->{nss} or panic "no namespace tables";
63 771   50     2860 $self->{prefixes} = $args->{prefixes} || {};
64 771         2088 $self;
65             }
66              
67              
68             sub register($)
69 0     0 1 0 { my ($class, $name) = @_;
70 0 0       0 UNIVERSAL::isa($class, __PACKAGE__)
71             or error __x"back-end {class} does not extend {base}"
72             , class => $class, base => __PACKAGE__;
73 0         0 $translators{$name} = $class;
74             }
75              
76              
77             # may disappear, so not documented publicly (yet)
78 0     0 0 0 sub actsAs($) { panic "not implemented" }
79              
80             #--------
81              
82             sub compile($@)
83 771     771 1 5008 { my ($self, $item, %args) = @_;
84 771         6374 @$self{keys %args} = values %args; # dirty. Always all the same fields
85              
86 771   66     2911 my $path = $self->prefixed($item, 1) || $item;
87 771 50       1641 ref $item
88             and panic "expecting an item as point to start at $path";
89              
90 771   100     1710 my $hooks = $self->{hooks} ||= [];
91 771   100     1534 my $typemap = $self->{typemap} ||= {};
92 771         2456 $self->typemapToHooks($hooks, $typemap);
93              
94             $self->{blocked_nss}
95 771         2213 = $self->decodeBlocked(delete $self->{block_namespace});
96              
97 771         1987 my $nsp = $self->namespaces;
98 771         1370 foreach my $t (keys %$typemap)
99 9 50 33     23 { $nsp->find(complexType => $t) || $nsp->find(simpleType => $t)
100             or error __x"complex or simpleType {type} for typemap unknown"
101             , type => $t;
102             }
103              
104 771 50       1214 if(my $def = $self->namespaces->findID($item))
105 0         0 { my $node = $def->{node};
106 0         0 my $name = $node->localName;
107 0         0 $item = $def->{full};
108             }
109              
110 771         1335 delete $self->{_created};
111 771         2108 my $produce = $self->topLevel($path, $item, 1);
112 769         5746 delete $self->{_created};
113              
114             my $in = $self->{include_namespaces}
115 769 100       8006 or return $produce;
116              
117 121         375 $self->makeWrapperNs($path, $produce, $self->{prefixes}, $in);
118             }
119              
120             sub assertType($$$$)
121 2134     2134 0 3688 { my ($self, $where, $field, $type, $value) = @_;
122 2134         4092 my $checker = $builtin_types{$type}{check};
123 2134 50       3324 unless(defined $checker)
124 0         0 { mistake "useless assert for type $type";
125 0         0 return;
126             }
127              
128 2134 50       4156 return if $checker->($value);
129              
130 0         0 error __x"field {field} contains '{value}' which is not a valid {type} at {where}"
131             , field => $field, value => $value, type => $type, where => $where
132             , _class => 'usage';
133              
134             }
135              
136             sub extendAttrs($@)
137 54     54 0 105 { my ($self, $in, $add) = @_;
138              
139 54 50       156 if(my $a = $add->{attrs})
140             { # new attrs overrule old definitions (restrictions)
141 54         87 my (@attrs, %code);
142 54 100       80 my @all = (@{$in->{attrs} || []}, @{$add->{attrs} || []});
  54 50       160  
  54         165  
143 54         137 while(@all)
144 80         141 { my ($type, $code) = (shift @all, shift @all);
145 80 50       180 if($code{$type})
146 0         0 { $attrs[$code{$type}] = $code;
147             }
148             else
149 80         166 { push @attrs, $type => $code;
150 80         1833 $code{$type} = $#attrs;
151             }
152             }
153 54         135 $in->{attrs} = \@attrs;
154             }
155              
156             # doing this correctly is too complex for now
157 54 50       419 unshift @{$in->{attrs_any}}, @{$add->{attrs_any}} if $add->{attrs_any};
  54         106  
  54         85  
158 54         73 $in;
159             }
160              
161 2452 50   2452 0 30090 sub isTrue($) { $_[1] eq '1' || $_[1] eq 'true' }
162              
163             # Find the namespace use details of a certain top-level element or
164             # attribute.
165             sub nsContext($)
166 2055     2055 0 2944 { my ($self, $def) = @_;
167 2055 50       3057 $def or return {};
168              
169 2055         2782 my $tns = $def->{ns};
170              
171             # top elements are to be qualified unless there is no targetNamespace
172 2055 100       5650 my %context = (tns => $tns, qual_top => ($tns ? 1 : 0));
173              
174 2055         3273 my $el_qual = $def->{efd} eq 'qualified';
175 2055 100       3383 if(exists $self->{elements_qualified})
176 1817   50     3241 { my $qual = $self->{elements_qualified} || 0;
177 1817 50       2626 if($qual eq 'TOP')
178             { $tns or error __x"application requires that element `{name}' has a targetNamespace"
179 0 0       0 , name => $def->{full};
180             }
181             else
182 1817 100       3391 { $el_qual = $qual eq 'ALL' ? 1 : $qual eq 'NONE' ? 0 : $qual;
    100          
183             }
184 1817 100       3329 $context{qual_top} = 0 if $qual eq 'NONE';
185             }
186 2055         2677 $context{qual_elem} = $el_qual;
187              
188 2055         2553 my $at_qual = $def->{afd} eq 'qualified';
189 2055 100       3005 if(exists $self->{attributes_qualified})
190 22   50     33 { my $qual = $self->{attributes_qualified} || 0;
191 22 50       28 if($qual eq 'TOP')
192 0 0       0 { $tns or error __x"application requires that attibute `{name}' has a targetNamespace", name => $def->{full};
193             }
194             else
195 22 50       48 { $at_qual = $qual eq 'ALL' ? 1 : $qual eq 'NONE' ? 0 : $qual;
    50          
196             }
197             }
198 2055         2705 $context{qual_attr} = $at_qual;
199              
200 2055         4117 \%context;
201             }
202              
203 4959     4959 0 11958 sub namespaces() { $_[0]->{nss} }
204              
205             sub topLevel($$;$)
206 771     771 0 1626 { my ($self, $path, $fullname, $is_root) = @_;
207              
208             # built-in types have to be handled differently.
209             my $internal = XML::Compile::Schema::Specs->builtInType(undef, $fullname
210             , sloppy_integers => $self->{sloppy_integers}
211             , sloppy_floats => $self->{sloppy_floats}
212             , json_friendly => $self->{json_friendly}
213 771         3790 );
214              
215 771 100       1513 if($internal)
216             { my $builtin = $self->makeBuiltin($fullname, undef
217 2         7 , $fullname, $internal, $self->{check_values});
218             my $builder = $self->actsAs('WRITER')
219 1     1   3 ? sub { $_[0]->createTextNode($builtin->(@_)) }
220 2 100       7 : $builtin;
221 2         7 return $self->makeElementWrapper($path, $builder);
222             }
223              
224 769         1396 my $nss = $self->namespaces;
225 769 0 66     1940 my $top = $nss->find(element => $fullname)
    50          
226             || $nss->find(attribute => $fullname)
227             or error __x(( $fullname eq $path
228             ? N__"cannot find element or attribute `{name}'"
229             : N__"cannot find element or attribute `{name}' at {where}"
230             ), name => $fullname, where => $path, _class => 'usage');
231              
232             # filter the nodes in the schema which are to be processed
233 769         1211 my $node = $top->{node};
234 769         3317 my $schemans = $node->namespaceURI;
235             my $tree = XML::Compile::Iterator->new($node, $path, sub
236 8973     8973   8338 { my $n = shift;
237 8973 100 66     57930 $n->isa('XML::LibXML::Element')
238             && $n->namespaceURI eq $schemans
239             && $n->localName !~ $ignore_elements
240 769         5288 });
241              
242 769         1324 delete $self->{_nest}; # reset recursion administration
243              
244 769         2014 local $self->{_context} = $self->nsContext($top);
245 769         2663 my $name = $node->localName;
246 769         1002 my $data;
247 769 100       1463 if($name eq 'element')
    50          
248 761         2043 { my ($label, $make) = $self->element($tree, $is_root);
249 759 50       10460 $data = $self->makeElementWrapper($path, $make) if $make;
250             }
251             elsif($name eq 'attribute')
252 8         23 { my $make = $self->attribute($tree);
253 8 50       114 $data = $self->makeAttributeWrapper($path, $make) if $make;
254             }
255             else
256 0         0 { error __x"top-level `{full}' is not an element or attribute but {name} at {where}"
257             , full => $fullname, name => $name, where => $tree->path
258             , _class => 'usage';
259             }
260              
261 767         4344 $data;
262             }
263              
264             sub typeByName($$$)
265 1969     1969 0 3189 { my ($self, $where, $tree, $typename) = @_;
266              
267 1969         3522 my $node = $tree->node;
268              
269             #
270             # Try to detect a built-in type
271             #
272              
273             my $def = XML::Compile::Schema::Specs->builtInType($node, $typename
274             , sloppy_integers => $self->{sloppy_integers}
275             , sloppy_floats => $self->{sloppy_floats}
276             , json_friendly => $self->{json_friendly}
277 1969         6747 );
278              
279 1969 100       3460 if($def)
280             { # Is built-in
281 1589         4481 my $st = $self->makeBuiltin($where, $node, $typename, $def, $self->{check_values});
282              
283 1589         6889 return +{ st => $st, is_list => $def->{is_list} };
284             }
285              
286             #
287             # not a schema standard type
288             #
289 380 50 66     770 my $top = $self->namespaces->find(complexType => $typename)
290             || $self->namespaces->find(simpleType => $typename)
291             or error __x"cannot find type {type} at {where}"
292             , type => $typename, where => $where, _class => 'usage';
293              
294 380         828 local $self->{_context} = $self->nsContext($top);
295 380         988 my $typeimpl = $tree->descend($top->{node});
296              
297 380         627 my $typedef = $top->{type};
298 380 50       1483 $typedef eq 'simpleType' ? $self->simpleType($typeimpl)
    100          
299             : $typedef eq 'complexType' ? $self->complexType($typeimpl)
300             : error __x"expecting simple- or complexType, not '{type}' at {where}"
301             , type => $typedef, where => $tree->path, _class => 'schema';
302             }
303              
304             sub simpleType($;$)
305 372     372 0 570 { my ($self, $tree, $in_list) = @_;
306              
307 372 50       728 $tree->nrChildren==1
308             or error __x"simpleType must have exactly one child at {where}"
309             , where => $tree->path, _class => 'schema';
310              
311 372         1199 my $child = $tree->firstChild;
312 372         934 my $name = $child->localName;
313 372         675 my $nest = $tree->descend($child);
314              
315             # Full content:
316             # annotation?
317             # , (restriction | list | union)
318              
319 372 50       1367 my $type
    100          
    100          
320             = $name eq 'restriction' ? $self->simpleRestriction($nest, $in_list)
321             : $name eq 'list' ? $self->simpleList($nest)
322             : $name eq 'union' ? $self->simpleUnion($nest)
323             : error __x"simpleType contains '{local}', must be restriction, list, or union at {where}"
324             , local => $name, where => $tree->path, _class => 'schema';
325              
326 372         665 delete @$type{'attrs','attrs_any'}; # spec says ignore attrs
327 372         1290 $type;
328             }
329              
330             sub simpleList($)
331 56     56 0 99 { my ($self, $tree) = @_;
332              
333             # attributes: id, itemType = QName
334             # content: annotation?, simpleType?
335              
336 56         104 my $per_item;
337 56         86 my $node = $tree->node;
338 56         84 my $where = $tree->path . '#list';
339              
340 56 100       138 if(my $type = $node->getAttribute('itemType'))
341 40 50       382 { $tree->nrChildren==0
342             or error __x"list with both itemType and content at {where}"
343             , where => $where, _class => 'schema';
344              
345 40         95 my $typename = $self->rel2abs($where, $node, $type);
346 40   33     97 $per_item = $self->blocked($where, simpleType => $typename)
347             || $self->typeByName($where, $tree, $typename);
348             }
349             else
350 16 50       141 { $tree->nrChildren==1
351             or error __x"list expects one simpleType child at {where}"
352             , where => $where, _class => 'schema';
353              
354 16 50       30 $tree->currentLocal eq 'simpleType'
355             or error __x"list can only have a simpleType child at {where}"
356             , where => $where, _class => 'schema';
357              
358 16         32 $per_item = $self->simpleType($tree->descend, 1);
359             }
360              
361             my $st = $per_item->{st}
362 56 50       283 or panic "list did not produce a simple type at $where";
363              
364 56         321 $per_item->{st} = $self->makeList($where, $st);
365 56         83 $per_item->{is_list} = 1;
366 56         80 $per_item;
367             }
368              
369             sub simpleUnion($)
370 37     37 0 60 { my ($self, $tree) = @_;
371              
372             # attributes: id, memberTypes = List of QName
373             # content: annotation?, simpleType*
374              
375 37         112 my $node = $tree->node;
376 37         61 my $where = $tree->path . '#union';
377              
378             # Normal error handling switched off, and check_values must be on
379             # When check_values is off, we may decide later to treat that as
380             # string, which is faster but not 100% safe, where int 2 may be
381             # formatted as float 1.999
382              
383 37         68 local $self->{check_values} = 1;
384              
385 37         43 my @types;
386 37 100       85 if(my $members = $node->getAttribute('memberTypes'))
387 27         274 { foreach my $union (split " ", $members)
388 43         79 { my $typename = $self->rel2abs($where, $node, $union);
389 43   33     94 my $type = $self->blocked($where, simpleType => $typename)
390             || $self->typeByName($where, $tree, $typename);
391             my $st = $type->{st}
392 43 50       244 or error __x"union only of simpleTypes, but {type} is complex at {where}"
393             , type => $typename, where => $where, _class => 'schema';
394              
395 43         96 push @types, $st;
396             }
397             }
398              
399 37         154 foreach my $child ($tree->childs)
400 39         214 { my $name = $child->localName;
401 39 50       78 $name eq 'simpleType'
402             or error __x"only simpleType's within union, found {local} at {where}"
403             , local => $name, where => $where, _class => 'schema';
404              
405 39         80 my $ctype = $self->simpleType($tree->descend($child), 0);
406 39         401 push @types, $ctype->{st};
407             }
408              
409 37         326 my $do = $self->makeUnion($where, @types);
410 37         114 { st => $do, is_union => 1 };
411             }
412              
413             sub simpleRestriction($$)
414 279     279 0 408 { my ($self, $tree, $in_list) = @_;
415              
416             # attributes: id, base = QName
417             # content: annotation?, simpleType?, facet*
418              
419 279         632 my $node = $tree->node;
420 279         458 my $where = $tree->path . '#sres';
421              
422 279         361 my ($base, $typename);
423 279 50       554 if(my $basename = $node->getAttribute('base'))
424 279         2706 { $typename = $self->rel2abs($where, $node, $basename);
425 279   66     691 $base = $self->blocked($where, simpleType => $typename)
426             || $self->typeByName($where, $tree, $typename);
427             }
428             else
429 0 0       0 { my $simple = $tree->firstChild
430             or error __x"no base in simple-restriction, so simpleType required at {where}"
431             , where => $where, _class => 'schema';
432              
433 0 0       0 $simple->localName eq 'simpleType'
434             or error __x"simpleType expected, because there is no base attribute at {where}"
435             , where => $where, _class => 'schema';
436              
437 0         0 $base = $self->simpleType($tree->descend($simple, 'st'));
438              
439 0 0       0 if((my $r) = $simple->getChildrenByLocalName('restriction')) {
440             # @facets
441 0         0 my $basename = $r->getAttribute('base');
442 0 0       0 $typename = $self->rel2abs($where, $r, $basename) if $r;
443             }
444              
445 0         0 $tree->nextChild;
446             }
447              
448             my $st = $base->{st}
449 279 50       1166 or error __x"simple-restriction is not a simpleType at {where}"
450             , where => $where, _class => 'schema';
451              
452             my $do = $self->applySimpleFacets($tree, $st
453 279   100     1105 , $in_list || $base->{is_list}, $typename);
454              
455 279 50       783 $tree->currentChild
456             and error __x"elements left at tail at {where}"
457             , where => $tree->path, _class => 'schema';
458              
459 279         865 +{ st => $do };
460             }
461              
462             # Early=lexical space, Late=value space
463             my %facets_early = map +($_ => 1), qw/whiteSpace pattern/;
464             #my %facets_late = map +($_ => 1), qw/totalDigits maxScale minScale enumeration
465             # maxInclusive maxExclusive minInclusive minExclusive fractionDigits
466             # length minLength maxLength/;
467              
468             my $qname_type = pack_type SCHEMA2001, 'QName';
469              
470             sub applySimpleFacets($$$$)
471 287     287 0 568 { my ($self, $tree, $st, $is_list, $type) = @_;
472 287         361 my $nss = $self->{nss};
473              
474             # partial
475             # content: facet*
476             # facet = minExclusive | minInclusive | maxExclusive | maxInclusive
477             # | totalDigits | fractionDigits | maxScale | minScale | length
478             # | minLength | maxLength | enumeration | whiteSpace | pattern
479              
480 287         530 my $where = $tree->path . '#facet';
481 287         374 my (%facets, $is_qname);
482 287         506 for(my $child = $tree->currentChild; $child; $child = $tree->nextChild)
483 280         1552 { my $facet = $child->localName;
484 280 100       1218 last if $facet =~ $attribute_defs;
485              
486 272         551 my $value = $child->getAttribute('value');
487 272 50       2651 defined $value
488             or error __x"no value for facet `{facet}' at {where}"
489             , facet => $facet, where => $where, _class => 'schema';
490              
491 272 100       701 if($facet eq 'enumeration')
    100          
    50          
492 88 100       226 { $is_qname = $nss->doesExtend($type, $qname_type)
493             unless defined $is_qname;
494              
495 88 100       136 if($is_qname)
496             { # rewrite prefixed values into "{ns}local"
497 10 50       34 my ($prefix, $local)
498             = $value =~ m/\:/ ? split(/\:/, $value, 2) : ('', $value);
499 10         30 my $ns = $child->lookupNamespaceURI($prefix);
500 10         19 $value = pack_type $ns, $local;
501 10         16 $self->_registerNSprefix($prefix, $ns, 1);
502             }
503              
504 88         87 push @{$facets{enumeration}}, $value;
  88         318  
505             }
506 30         37 elsif($facet eq 'pattern') { push @{$facets{pattern}}, $value }
  30         109  
507 154         486 elsif(!exists $facets{$facet}) { $facets{$facet} = $value }
508             else
509 0         0 { error __x"facet `{facet}' defined twice at {where}"
510             , facet => $facet, where => $where, _class => 'schema';
511             }
512             }
513              
514             return $st
515 287 100 66     1154 if $self->{ignore_facets} || !keys %facets;
516              
517 210         636 my %facets_info = %facets;
518              
519             #
520             # new facets overrule all of the base-class
521             #
522              
523 210 100 100     501 if(defined $facets{totalDigits} && defined $facets{fractionDigits})
524 3         5 { my $td = delete $facets{totalDigits};
525 3         5 my $fd = delete $facets{fractionDigits};
526 3         6 $facets{_totalFracDigits} = [$td, $fd];
527             }
528              
529 210         294 my (@early, @late);
530 210 100       514 my $action = $self->actsAs('WRITER') ? 'WRITER' : 'READER';
531 210         450 foreach my $facet (keys %facets)
532             { my $h = builtin_facet($where, $self, $facet
533 243 100       679 , $facets{$facet}, $is_list, $type, $nss, $action) or next;
534              
535 241 100       518 if($facets_early{$facet})
536 34         71 { push @early, $h }
537 207         408 else { push @late, $h }
538             }
539              
540             $is_list
541 210 100       816 ? $self->makeFacetsList($where, $st, \%facets_info, \@early, \@late)
542             : $self->makeFacets($where, $st, \%facets_info, \@early, \@late);
543             }
544              
545             sub element($;$)
546 1880     1880 0 2903 { my ($self, $tree, $is_root) = @_;
547              
548             # attributes: abstract, default, fixed, form, id, maxOccurs, minOccurs
549             # , name, nillable, ref, substitutionGroup, targetNamespace, type
550             # ignored: block, final, targetNamespace additional restrictions
551             # content: annotation?
552             # , (simpleType | complexType)?
553             # , (unique | key | keyref)*
554              
555 1880         3396 my $node = $tree->node;
556 1880         5820 my $parent = $node->parentNode;
557 1880   100     3371 my $is_global= $parent
558             && $parent->isa('XML::LibXML::Element')
559             && $parent->localname eq 'schema';
560              
561 1880         17387 my $where = $tree->path;
562              
563 1880 50       3523 my $name = $node->getAttribute('name')
564             or error __x"element has no name nor ref at {where}"
565             , where => $where, _class => 'schema';
566 1880         18234 $self->assertType($where, name => NCName => $name);
567              
568             # Full name based on current context. This might be a global name
569             # or a local name.
570              
571 1880         3110 my $context = $self->{_context};
572              
573             # Determine the context of this element. When it is a global, we need
574             # to set-up a new context until end-of-function.
575              
576 1880         2001 my $abstract = 0;
577 1880         2300 my ($qual, $ns, $fullname);
578              
579 1880 100       2675 if($is_global)
580 860   66     1624 { $ns = $node->getAttribute('targetNamespace')
581             || $parent->getAttribute('targetNamespace');
582 860         14535 $fullname= pack_type $ns, $name;
583 860         1868 my $def = $self->namespaces->find(element => $fullname);
584 860         1618 $context = $self->nsContext($def);
585 860         1269 $qual = $context->{qual_top};
586              
587             # abstract elements are not to be used in messages.
588 860 50       2009 $abstract = $self->{abstract_types} eq 'ACCEPT' ? 0 : $def->{abstract};
589             }
590             else
591 1020         1695 { $qual = $context->{qual_elem};
592 1020   66     1854 $ns = $node->getAttribute('targetNamespace') || $context->{tns};
593 1020         9979 $fullname = pack_type $ns, $name;
594             }
595              
596 1880 100       3883 if(my $form = $node->getAttribute('form'))
597 8 50       73 { $qual
    100          
598             = $form eq 'qualified' ? 1
599             : $form eq 'unqualified' ? 0
600             : error __x"form must be (un)qualified, not `{form}' at {where}"
601             , form => $form, where => $where, _class => 'schema';
602             }
603              
604 1880 100       15909 local $self->{_context} = $context if $is_global;
605 1880 100       2963 my $nodetype = $qual ? $fullname : $name;
606              
607             # SubstitionGroups
608             # We know the type of the message root, so do not need to look for
609             # alternative sgs (and it wouldn't work anyway)
610              
611 1880         1927 my @sgs;
612 1880 100       3578 @sgs = $self->namespaces->findSgMembers($node->localName, $fullname)
613             unless $is_root;
614              
615             # Handle re-usable fragments, fight against combinatorial explosions
616              
617 1880         4537 my $nodeid = $node->unique_key; #$node->nodePath.'#'.$fullname;
618 1880 100       4106 if(my $already = $self->{_created}{$nodeid})
619             { # We cannot cache compile subst-group handlers, because sgs using
620             # elements which were already compiled into sgs does not work.
621 14 50       25 $already = $self->substitutionGroup($tree, $fullname, $nodetype
622             , $already, \@sgs) if @sgs;
623 14         50 return ($nodetype, $already);
624             }
625              
626             # Detect recursion
627             # Very complicated: recursively nested structures. It is less of a
628             # problem when you handle in run-time what you see... but we here
629             # have to be prepared for everything.
630              
631 1866 100       3348 if(exists $self->{_nest}{$nodeid})
632 12         15 { my $outer = \$self->{_nest}{$nodeid};
633 12     13   45 my $nested = sub { $$outer->(@_) };
  13         34  
634              
635             # The code must be blessed in the right class, to be compiled
636             # correctly inside its parent.
637 12 50       21 bless $nested, 'BLOCK' if @sgs;
638              
639 12         55 return ($nodetype, $nested);
640             }
641 1854         3332 $self->{_nest}{$nodeid} = undef;
642              
643             # Construct XML tag to use
644              
645 1854 100       2814 my $trans = $qual ? 'makeTagQualified' : 'makeTagUnqualified';
646 1854         5094 my $tag = $self->$trans($where, $node, $name, $ns);
647              
648             # Construct type processor
649              
650 1854         2344 my ($comptype, $comps);
651 1854         4668 my $nr_childs = $tree->nrChildren;
652 1854 100       3232 if(my $isa = $node->getAttribute('type'))
    100          
    50          
653             { # explicitly names type
654 1347 50       10795 $nr_childs==0
655             or error __x"no childs expected with attribute `type' at {where}"
656             , where => $where, _class => 'schema';
657              
658 1347         2842 $comptype = $self->rel2abs($where, $node, $isa);
659 1347   66     3241 $comps = $self->blocked($where, anyType => $comptype)
660             || $self->typeByName($where, $tree, $comptype);
661             }
662             elsif($nr_childs==0)
663             { # default type for substGroups is type of base-class
664 17         141 my $base_node = $node;
665 17         60 local $self->{_context};
666 17         35 while(my $subst = $base_node->getAttribute('substitutionGroup'))
667 5         56 { my $subst_elem = $self->rel2abs($where, $base_node, $subst);
668 5         13 my $base_elem = $self->namespaces->find(element => $subst_elem);
669 5         13 $self->{_context} = $self->nsContext($base_elem);
670 5         5 $base_node = $base_elem->{node};
671 5 50       15 my $isa = $base_node->getAttribute('type')
672             or next;
673              
674 5         52 $comptype = $self->rel2abs($where, $base_node, $isa);
675 5   33     16 $comps = $self->blocked($where, complexType => $comptype)
676             || $self->typeByName($where, $tree, $comptype);
677 5         12 last;
678             }
679 17 100       117 unless($comptype)
680             { # no type found, so anyType
681 12         33 $comptype = $self->anyType($node);
682 12         42 $comps = $self->typeByName($where, $tree, $comptype);
683             }
684             }
685             elsif($nr_childs!=1)
686 0         0 { error __x"expected is only one child node at {where}"
687             , where => $where, _class => 'schema';
688             }
689             else # nameless types
690 490         5334 { my $child = $tree->firstChild;
691 490         1262 my $local = $child->localname;
692 490         1187 my $nest = $tree->descend($child);
693              
694 490 50       2224 ($comps, $comptype)
    100          
695             = $local eq 'simpleType'
696             ? ($self->simpleType($nest, 0), 'unnamed simple')
697             : $local eq 'complexType'
698             ? ($self->complexType($nest), 'unnamed complex')
699             : error __x"illegal element child `{name}' at {where}"
700             , name => $local, where => $where, _class => 'schema';
701             }
702              
703             my ($st, $elems, $attrs, $attrs_any)
704 1852         15449 = @$comps{ qw/st elems attrs attrs_any/ };
705 1852   100     9402 $_ ||= [] for $elems, $attrs, $attrs_any;
706              
707             # Construct basic element handler
708              
709 1852         2517 my $is_simple = defined $st;
710 1852   100     4094 my $nillable = $self->isTrue($node->getAttribute('nillable') || 'false');
711              
712             my $elem_handler
713 1852 100 66     6888 = $comps->{mixed} ? 'makeMixedElement'
    100          
    100          
714             : ! $is_simple ? 'makeComplexElement' # other complexType
715             : (@$attrs || @$attrs_any) ? 'makeTaggedElement' # complex/simpleContent
716             : 'makeSimpleElement';
717              
718 1852   66     6630 my $r = $self->$elem_handler
719             ( $where, $tag, ($st||$elems), $attrs, $attrs_any, $comptype, $nillable);
720              
721             # Add defaults and stuff
722 1852         5938 my $default = $node->getAttributeNode('default');
723 1852         3670 my $fixed = $node->getAttributeNode('fixed');
724              
725 1852 50 66     3740 $default && $fixed
726             and error __x"element can not have default and fixed at {where}"
727             , where => $tree->path, _class => 'schema';
728              
729 1852 100       3416 my $value
    100          
730             = $default ? $default->textContent
731             : $fixed ? $fixed->textContent
732             : undef;
733              
734 1852 100       3907 my $generate
    100          
    100          
735             = $abstract ? 'makeElementAbstract'
736             : $default ? 'makeElementDefault'
737             : $fixed ? 'makeElementFixed'
738             : 'makeElement';
739              
740 1852         4613 my $do = $self->$generate($where, $ns, $nodetype, $r, $value, $tag);
741              
742             # hrefs are used by SOAP-RPC
743             $do = $self->makeElementHref($where, $ns, $nodetype, $do)
744 1852 50 33     4158 if $self->{permit_href} && $self->actsAs('READER');
745              
746             # Implement hooks
747 1852         3772 my ($before, $replace, $after)
748             = $self->findHooks($where, $comptype, $node);
749              
750 1852 100 100     7159 $do = $self->makeHook($where,$do,$tag,$before,$replace,$after,$comptype)
      100        
751             if $before || $replace || $after;
752              
753             $do = $self->xsiType($tree, $node, $name, $comptype, $do)
754 1852 100 66     5238 if $comptype && $self->{xsi_type}{$comptype};
755              
756             $do = $self->addTypeAttribute($comptype, $do)
757 1852 100 100     3261 if $self->{xsi_type_everywhere} && $comptype !~ /^unnamed /;
758              
759 1852         3342 $self->{_created}{$nodeid} = $do;
760              
761 1852 100       3080 $do = $self->substitutionGroup($tree, $fullname, $nodetype, $do, \@sgs)
762             if @sgs;
763              
764             # handle recursion
765             # this must look very silly to you... however, this is resolving
766             # recursive schemas: this way nested use of the same element
767             # definition will catch the code reference of the outer definition.
768 1852         2425 $self->{_nest}{$nodeid} = $do;
769 1852         2765 delete $self->{_nest}{$nodeid}; # clean the outer definition
770              
771 1852         10671 ($nodetype, $do);
772             }
773              
774             sub particle($)
775 1692     1692 0 2618 { my ($self, $tree) = @_;
776              
777 1692         2664 my $node = $tree->node;
778 1692         4660 my $local = $node->localName;
779 1692         2639 my $where = $tree->path;
780              
781 1692         3353 my $min = $node->getAttribute('minOccurs');
782 1692         13602 my $max = $node->getAttribute('maxOccurs');
783              
784 1692 100       11359 unless(defined $min)
785 1430 100 100     2836 { $min = ($self->actsAs('WRITER') || $self->{default_values} ne 'EXTEND')
786             && ($node->getAttribute('default') || $node->getAttribute('fixed'))
787             ? 0 : 1;
788             }
789              
790             $min = 0 if $self->{interpret_nillable_as_optional}
791 1692 100 100     12019 && $self->isTrue($node->getAttribute('nillable') || 'false');
      100        
792              
793             # default attribute in writer means optional, but we want to see
794             # them in the reader, to see the value.
795            
796 1692 100       2792 defined $max or $max = 1;
797             $max = 'unbounded'
798 1692 100 100     5159 if $max ne 'unbounded' && $max > 1 && !$self->{check_occurs};
      100        
799              
800             $min = 0
801 1692 100 100     2779 if $max eq 'unbounded' && !$self->{check_occurs};
802              
803 1692 100       2412 return $self->anyElement($tree, $min, $max)
804             if $local eq 'any';
805              
806 1682 50       7978 my ($label, $process)
    100          
    100          
807             = $local eq 'element' ? $self->particleElement($tree)
808             : $local eq 'group' ? $self->particleGroup($tree)
809             : $local =~ $particle_blocks ? $self->particleBlock($tree)
810             : error __x"unknown particle type '{name}' at {where}"
811             , name => $local, where => $tree->path, _class => 'schema';
812              
813 1682 100       17382 defined $label
814             or return ();
815              
816 1670 100       3426 if(ref $process eq 'BLOCK')
817 611         1053 { my $key = $self->keyRewrite($label);
818 611         1446 my $multi = $self->blockLabel($local, $key);
819 611         3224 return $self->makeBlockHandler($where, $label, $min, $max
820             , $process, $local, $multi);
821             }
822              
823             # only elements left
824 1059         994 my $required;
825 1059         2178 my $key = $self->keyRewrite($label);
826 1059 100       2984 $required = $self->makeRequired($where, $key, $process) if $min!=0;
827              
828 1059 100       2055 ($self->actsAs('READER') ? $label : $key) =>
829             $self->makeElementHandler($where, $key, $min,$max, $required, $process);
830             }
831              
832             sub particleElement($)
833 1088     1088 0 3340 { my ($self, $tree) = @_;
834              
835 1088         1675 my $node = $tree->node;
836 1088 100       1994 if(my $ref = $node->getAttribute('ref'))
837 75         651 { my $where = $tree->path . "/$ref";
838 75         280 my $refname = $self->rel2abs($tree, $node, $ref);
839 75 100       229 return () if $self->blocked($where, ref => $refname);
840              
841 63 50       138 my $def = $self->namespaces->find(element => $refname)
842             or error __x"cannot find ref element '{name}' at {where}"
843             , name => $refname, where => $where, _class => 'schema';
844              
845             return $self->element($tree->descend($def->{node}
846 63         185 , $self->prefixed($refname, 1)));
847             }
848              
849 1013         8039 my $name = $node->getAttribute('name');
850 1013         7446 $self->element($tree->descend($node, $name));
851             }
852              
853             # blockLabel KIND, LABEL
854             # Particle blocks, like `sequence' and `choice', which have a maxOccurs
855             # (maximum occurrence) which is 2 of more, are represented by an ARRAY
856             # of HASHs. The label with such a block is derived from its first element.
857             # This function determines how.
858             # seq_address sequence get seq_ prepended
859             # cho_gender choices get cho_ before them
860             # all_money an all block can also be repreated in spec >1.1
861             # gr_people group refers to a block of above type, but
862             # that type is not reflected in the name
863              
864             my %block_abbrev = qw/sequence seq_ choice cho_ all all_ group gr_/;
865             sub blockLabel($$)
866 611     611 0 1008 { my ($self, $kind, $label) = @_;
867 611 100       1121 return $label if $kind eq 'element';
868              
869 594         1289 $label =~ s/^(?:seq|cho|all|gr)_//;
870 594         1630 $block_abbrev{$kind} . (unpack_type $label)[1];
871             }
872              
873             sub particleGroup($)
874 16     16 0 32 { my ($self, $tree) = @_;
875              
876             # attributes: id, maxOccurs, minOccurs, name, ref
877             # content: annotation?, (all|choice|sequence)?
878             # apparently, a group can not refer to a group... well..
879              
880 16         35 my $node = $tree->node;
881 16         27 my $where = $tree->path . '#group';
882 16 50       31 my $ref = $node->getAttribute('ref')
883             or error __x"group without ref at {where}"
884             , where => $where, _class => 'schema';
885              
886 16         151 my $typename = $self->rel2abs($where, $node, $ref);
887 16 50       47 if(my $blocked = $self->blocked($where, ref => $typename))
888 0         0 { return ($typename, $blocked);
889             }
890              
891 16 50       36 my $dest = $self->namespaces->find(group => $typename)
892             or error __x"cannot find group `{name}' at {where}"
893             , name => $typename, where => $where, _class => 'schema';
894              
895 16         48 my $group = $tree->descend($dest->{node}, $self->prefixed($typename, 1));
896 16 50       39 return () if $group->nrChildren==0;
897              
898 16 50       32 $group->nrChildren==1
899             or error __x"only one particle block expected in group `{name}' at {where}"
900             , name => $typename, where => $where, _class => 'schema';
901              
902 16         30 my $local = $group->currentLocal;
903 16 50       69 $local =~ m/^(?:all|choice|sequence)$/
904             or error __x"illegal group member `{name}' at {where}"
905             , name => $local, where => $where, _class => 'schema';
906              
907 16         35 my ($blocklabel, $code) = $self->particleBlock($group->descend);
908 16         53 ($typename, $code);
909             }
910              
911             sub particleBlock($)
912 594     594 0 1141 { my ($self, $tree) = @_;
913              
914 594         1208 my $node = $tree->node;
915 594         1925 my @pairs = map $self->particle($tree->descend($_)), $tree->childs;
916 594 50       1743 @pairs or return ();
917              
918             # label is name of first component, only needed when maxOcc > 1
919 594         833 my $label = $pairs[0];
920 594         1940 my $blocktype = $node->localName;
921              
922 594         1733 my $call = 'make'.ucfirst $blocktype;
923 594         1375 ($label => $self->$call($tree->path, @pairs));
924             }
925              
926             sub xsiType($$$$$)
927 7     7 0 22 { my ($self, $tree, $node, $name, $type, $base) = @_;
928              
929 7         18 my %alt = ($type => $base);
930              
931 7         11 foreach my $alttype (@{$self->{xsi_type}{$type}})
  7         21  
932 11 100       25 { next if $alttype eq $type;
933              
934 7         18 my ($ns, $local) = unpack_type $alttype;
935 7         46 my $prefix = $node->lookupNamespacePrefix($ns);
936 7 50       18 defined $prefix
937             or $prefix = $self->_registerNSprefix(undef, $ns, 1);
938              
939 7 50       32 my $type = length $prefix ? "$prefix:$local" : $local;
940              
941             # do not accidentally use the default namespace, when there
942             # may also be namespace-less types used.
943 7         36 my $doc = $node->ownerDocument;
944 7         47 my $altnode = $doc->createElement('element');
945 7         22 $altnode->setNamespace(SCHEMA2001, 'temp1234', 1);
946 7         144 $altnode->setNamespace($ns, $prefix);
947 7         86 $altnode->setAttribute(name => $name);
948 7         76 $altnode->setAttribute(type => $type);
949              
950 7         57 delete $self->{_created}{$altnode->unique_key}; # clean nesting cache
951 7         20 (undef, $alt{$alttype}) = $self->element($tree->descend($altnode));
952             }
953 7         102 $self->makeXsiTypeSwitch($tree->path, $name, $type, \%alt);
954             }
955              
956             sub substitutionGroup($$$$$)
957 19     19 0 48 { my ($self, $tree, $fullname, $label, $base, $sgs) = @_;
958              
959 19 50       99 if(Log::Report->needs('TRACE')) # dump table of substgroup alternatives
960 0         0 { my $labelrw = $self->keyRewrite($label);
961 0         0 my @full = sort map $_->{full}, @$sgs;
962 0         0 my $longest = max map length, @full;
963 0         0 my @c = map sprintf("%-${longest}s %s",$_,$self->keyRewrite($_)), @full;
964 0         0 local $" = "\n ";
965 0         0 trace "substitutionGroup $fullname$\"BASE=$label ($labelrw)$\"@c";
966             }
967              
968 19         327 my @elems;
969 19 50       77 push @elems, $label => [$self->keyRewrite($label), $base] if $base;
970              
971 19         43 foreach my $subst (@$sgs)
972 36         102 { my ($l, $d) = $self->element($tree->descend($subst->{node}), 1);
973 36 50       494 push @elems, $l => [$self->keyRewrite($l), $d] if defined $d;
974             }
975              
976 19         50 $self->makeSubstgroup($tree->path.'#subst', $fullname, @elems);
977             }
978              
979             sub keyRewrite($;$)
980 2067     2067 0 2523 { my $self = shift;
981 2067 100       5410 my ($ns, $key) = @_==1 ? unpack_type($_[0]) : @_;
982 2067         2727 my $oldkey = $key;
983              
984 2067         1879 foreach my $r ( @{$self->{rewrite}} )
  2067         3589  
985 115 100       329 { if(ref $r eq 'HASH')
    100          
    50          
    100          
    50          
    0          
986 14         25 { my $full = pack_type $ns, $key;
987 14 100       32 $key = $r->{$full} if defined $r->{$full};
988 14 50       27 $key = $r->{$key} if defined $r->{$key};
989             }
990             elsif(ref $r eq 'CODE')
991 51         107 { $key = $r->($ns, $key);
992             }
993             elsif($r eq 'UNDERSCORES')
994 0         0 { $key =~ s/-/_/g;
995             }
996             elsif($r eq 'SIMPLIFIED')
997 21         57 { $key =~ s/-/_/g;
998 21         38 $key =~ s/\W//g;
999 21         41 $key = lc $key;
1000             }
1001             elsif($r eq 'PREFIXED')
1002 29         40 { my $p = $self->{prefixes};
1003 29 100       69 my $prefix = $p->{$ns} ? $p->{$ns}{prefix} : '';
1004 29 100       85 $key = $prefix . '_' . $key if $prefix ne '';
1005             }
1006             elsif($r =~ m/^PREFIXED\(\s*(.*?)\s*\)$/)
1007 0         0 { my @l = split /\s*\,\s*/, $1;
1008 0         0 my $p = $self->{prefixes};
1009 0 0       0 my $prefix = $p->{$ns} ? $p->{$ns}{prefix} : '';
1010 0 0       0 $key = $prefix . '_' . $key if grep {$prefix eq $_} @l;
  0         0  
1011             }
1012             else
1013 0         0 { error __x"key rewrite `{got}' not understood", got => $r;
1014             }
1015             }
1016              
1017 2067 100       3720 trace "rewrote type @_ to $key"
1018             if $key ne $oldkey;
1019              
1020 2067         4872 $key;
1021             }
1022              
1023             sub prefixed($;$)
1024 988     988 0 1681 { my ($self, $qname, $hide) = @_;
1025 988         2661 my ($ns, $local) = unpack_type $qname;
1026 988 50       2375 defined $ns or return $qname;
1027              
1028 988 100       3879 my $pn = $self->{prefixes}{$ns} or return;
1029 261 100       474 $pn->{used}++ unless $hide ;
1030 261 100       1062 length $pn->{prefix} ? "$pn->{prefix}:$local" : $local;
1031             }
1032              
1033             sub prefixForNamespace($)
1034 8     8 0 13 { my ($self, $ns) = @_;
1035 8 50       18 my $def = $self->{prefixes}{$ns} or return;
1036 8         40 $def->{prefix};
1037             }
1038              
1039             sub attribute($)
1040 259     259 0 449 { my ($self, $tree) = @_;
1041              
1042             # attributes: default, fixed, form, id, name, ref, type, use
1043             # content: annotation?, simpleType?
1044              
1045 259         436 my $node = $tree->node;
1046 259         757 my $parent = $node->parentNode;
1047 259   66     453 my $is_global= $parent && $parent->localname eq 'schema';
1048 259         1879 my $where = $tree->path;
1049              
1050 259         368 my $context = $self->{_context};
1051              
1052 259 100       518 if(my $refattr = $node->getAttribute('ref'))
1053             {
1054 5         58 my $refname = $self->rel2abs($tree, $node, $refattr);
1055 5 50       13 return () if $self->blocked($where, ref => $refname);
1056              
1057 5 50       11 my $def = $self->namespaces->find(attribute => $refname)
1058             or error __x"cannot find attribute {name} at {where}"
1059             , name => $refname, where => $where, _class => 'schema';
1060              
1061 5         10 local $self->{_context} = $def;
1062 5         14 return $self->attribute($tree->descend($def->{node}));
1063             }
1064              
1065             # Not a ref to attribute
1066 254 50       2337 my $name = $node->getAttribute('name')
1067             or error __x"attribute without name at {where}", where => $where;
1068 254         2183 $where .= '/@'.$name;
1069 254         569 $self->assertType($where, name => NCName => $name);
1070              
1071 254         405 my ($qual, $ns, $fullname);
1072 254 100       392 if($is_global)
1073 13   33     27 { $ns = $node->getAttribute('targetNamespace')
1074             || $parent->getAttribute('targetNamespace');
1075 13         212 $fullname= pack_type $ns, $name;
1076 13         27 my $def = $self->namespaces->find(attribute => $fullname);
1077 13         27 $context = $self->nsContext($def);
1078 13         20 $qual = $context->{qual_top};
1079             }
1080             else
1081 241         328 { $qual = $context->{qual_attr};
1082 241         305 $ns = $context->{tns};
1083 241         595 $fullname= pack_type $ns, $name;
1084             }
1085 254 100       515 local $self->{_context} = $context if $is_global;
1086              
1087 254 50       530 if(my $form = $node->getAttribute('form'))
1088 0 0       0 { $qual
    0          
1089             = $form eq 'qualified' ? 1
1090             : $form eq 'unqualified' ? 0
1091             : error __x"form must be (un)qualified, not `{form}' at {where}"
1092             , form => $form, where => $where, _class => 'schema';
1093             }
1094              
1095             # no default prefixes for attributes
1096 254 50 66     2239 error __x"attribute namespace {ns} cannot be the default namespace"
      66        
1097             , ns => $ns
1098             if $qual && $ns && $self->prefixForNamespace($ns) eq '';
1099            
1100 254         314 my ($type, $typeattr);
1101 254 100       485 if($tree->nrChildren==1)
1102 7 50       15 { $tree->currentLocal eq 'simpleType'
1103             or error __x"attribute child can only be `simpleType', not `{found}' at {where}"
1104             , found => $tree->currentLocal, where => $where
1105             , _class => 'schema';
1106              
1107 7         17 $type = $self->simpleType($tree->descend);
1108             }
1109             else
1110 247 50       448 { $name = $node->getAttribute('name')
1111             or error __x"attribute without name or ref at {where}"
1112             , where => $where, _class => 'schema';
1113              
1114 247         2011 $typeattr = $node->getAttribute('type');
1115             }
1116              
1117 254 100       1875 unless($type)
1118 247 100       669 { my $typename = defined $typeattr
1119             ? $self->rel2abs($where, $node, $typeattr)
1120             : $self->anyType($node);
1121              
1122 247   66     592 $type = $self->blocked($where, simpleType => $typename)
1123             || $self->typeByName($where, $tree, $typename);
1124             }
1125              
1126             my $st = $type->{st}
1127 254 50       663 or error __x"attribute not based in simple value type at {where}"
1128             , where => $where, _class => 'schema';
1129              
1130 254 100       469 my $trans = $qual ? 'makeTagQualified' : 'makeTagUnqualified';
1131 254 100       365 my $qns = $qual ? $context->{tns} : '';
1132 254         657 my $tag = $self->$trans($where, $node, $name, $qns);
1133              
1134 254   100     630 my $use = $node->getAttribute('use') || '';
1135 254 50       3065 $use =~ m/^(?:optional|required|prohibited|)$/
1136             or error __x"attribute use is required, optional or prohibited (not '{use}') at {where}"
1137             , use => $use, where => $where, _class => 'schema';
1138              
1139 254         734 my $default = $node->getAttributeNode('default');
1140 254         588 my $fixed = $node->getAttributeNode('fixed');
1141              
1142 254 100       797 my $generate
    100          
    100          
    100          
1143             = defined $default ? 'makeAttributeDefault'
1144             : defined $fixed ? 'makeAttributeFixed'
1145             : $use eq 'required' ? 'makeAttributeRequired'
1146             : $use eq 'prohibited'? 'makeAttributeProhibited'
1147             : 'makeAttribute';
1148              
1149 254 100       1864 my $value = defined $default ? $default : $fixed;
1150 254         562 my $label = $self->keyRewrite($qns, $name);
1151 254         803 my $do = $self->$generate($where, $qns, $tag, $label, $st, $value);
1152 254 50       1429 defined $do ? ($label => $do) : ();
1153             }
1154              
1155             sub attributeGroup($)
1156 6     6 0 17 { my ($self, $tree) = @_;
1157              
1158             # attributes: id, ref = QName
1159             # content: annotation?
1160              
1161 6         14 my $node = $tree->node;
1162 6         13 my $where = $tree->path;
1163 6 50       13 my $ref = $node->getAttribute('ref')
1164             or error __x"attributeGroup use without ref at {where}"
1165             , where => $tree->path, _class => 'schema';
1166              
1167 6         68 my $typename = $self->rel2abs($where, $node, $ref);
1168 6 50       16 return () if $self->blocked($where, ref => $typename);
1169              
1170 6 50       15 my $def = $self->namespaces->find(attributeGroup => $typename)
1171             or error __x"cannot find attributeGroup {name} at {where}"
1172             , name => $typename, where => $where, _class => 'schema';
1173              
1174 6         20 local $self->{tns} = $def->{ns};
1175 6         20 $self->attributeList($tree->descend($def->{node}));
1176             }
1177              
1178             # Don't known how to handle notQName
1179             sub anyAttribute($)
1180 10     10 0 19 { my ($self, $tree) = @_;
1181              
1182             # attributes: id
1183             # , namespace = ##any|##other| List of (anyURI|##targetNamespace|##local)
1184             # , notNamespace = List of (anyURI|##targetNamespace|##local)
1185             # ignored attributes
1186             # , notQName = List of QName
1187             # , processContents = lax|skip|strict
1188             # content: annotation?
1189              
1190 10         26 my $node = $tree->node;
1191 10         18 my $where = $tree->path . '@any';
1192              
1193 10         15 my $handler = $self->{any_attribute};
1194 10   50     20 my $namespace = $node->getAttribute('namespace') || '##any';
1195 10         98 my $not_ns = $node->getAttribute('notNamespace');
1196 10   50     84 my $process = $node->getAttribute('processContents') || 'strict';
1197              
1198 10 50 33     87 warn "HELP: please explain me how to handle notQName"
1199             if $^W && $node->getAttribute('notQName');
1200              
1201 10         19 my ($yes, $no) = $self->translateNsLimits($namespace, $not_ns);
1202 10         29 my $do = $self->makeAnyAttribute($where, $handler, $yes, $no, $process);
1203 10 100       44 defined $do ? $do : ();
1204             }
1205              
1206             sub anyElement($$$)
1207 10     10 0 20 { my ($self, $tree, $min, $max) = @_;
1208              
1209             # attributes: id, maxOccurs, minOccurs,
1210             # , namespace = ##any|##other| List of (anyURI|##targetNamespace|##local)
1211             # , notNamespace = List of (anyURI|##targetNamespace|##local)
1212             # ignored attributes
1213             # , notQName = List of QName
1214             # , processContents = lax|skip|strict
1215             # content: annotation?
1216              
1217 10         17 my $node = $tree->node;
1218 10         25 my $where = $tree->path . '#any';
1219 10         17 my $handler = $self->{any_element};
1220              
1221 10   50     17 my $namespace = $node->getAttribute('namespace') || '##any';
1222 10         84 my $not_ns = $node->getAttribute('notNamespace');
1223 10   50     69 my $process = $node->getAttribute('processContents') || 'strict';
1224              
1225 10 50 33     93 info "HELP: please explain me how to handle notQName"
1226             if $^W && $node->getAttribute('notQName');
1227              
1228 10         28 my ($yes, $no) = $self->translateNsLimits($namespace, $not_ns);
1229 10         37 (any => $self->makeAnyElement($where, $handler, $yes, $no
1230             , $process, $min, $max));
1231             }
1232              
1233             sub translateNsLimits($$)
1234 20     20 0 33 { my ($self, $include, $exclude) = @_;
1235              
1236             # namespace = ##any|##other| List of (anyURI|##targetNamespace|##local)
1237             # notNamespace = List of (anyURI |##targetNamespace|##local)
1238             # handling of ##local ignored: only full namespaces are supported for now
1239              
1240 20 100       40 return (undef, []) if $include eq '##any';
1241              
1242 12         17 my $tns = $self->{_context}{tns};
1243 12 100       27 return (undef, [$tns]) if $include eq '##other';
1244              
1245 8         35 my @return;
1246 8         13 foreach my $list ($include, $exclude)
1247 16         17 { my @list;
1248 16 100 66     44 if(defined $list && length $list)
1249 8         21 { foreach my $uri (split " ", $list)
1250 8 0       21 { push @list
    50          
1251             , $uri eq '##targetNamespace' ? $tns
1252             : $uri eq '##local' ? ()
1253             : $uri;
1254             }
1255             }
1256 16 100       42 push @return, @list ? \@list : undef;
1257             }
1258              
1259 8         18 @return;
1260             }
1261              
1262             sub complexType($)
1263 592     592 0 924 { my ($self, $tree) = @_;
1264              
1265             # abstract, block, final, id, mixed, name, defaultAttributesApply
1266             # Full content:
1267             # annotation?
1268             # , ( simpleContent
1269             # | complexContent
1270             # | ( (group|all|choice|sequence)?
1271             # , (attribute|attributeGroup)*
1272             # , anyAttribute?
1273             # )
1274             # )
1275             # , (assert | report)*
1276              
1277 592         1093 my $node = $tree->node;
1278 592   100     1234 my $mixed = $self->isTrue($node->getAttribute('mixed') || 'false');
1279             undef $mixed
1280 592 100       1448 if $self->{mixed_elements} eq 'STRUCTURAL';
1281              
1282 592 100       1143 my $first = $tree->firstChild
1283             or return {elems => [], mixed => $mixed};
1284              
1285 568         4118 my $name = $first->localName;
1286 568 100 100     5196 return $self->complexBody($tree, $mixed)
1287             if $name =~ $particle_blocks || $name =~ $attribute_defs;
1288              
1289 56 50       139 $tree->nrChildren==1
1290             or error __x"expected is single simpleContent or complexContent at {where}"
1291             , where => $tree->path, _class => 'schema';
1292              
1293 56 100       164 return $self->simpleContent($tree->descend($first))
1294             if $name eq 'simpleContent';
1295              
1296 34 100       120 return $self->complexContent($tree->descend($first), $mixed)
1297             if $name eq 'complexContent';
1298              
1299 1         5 error __x"complexType contains particles, simpleContent or complexContent, not `{name}' at {where}"
1300             , name => $name, where => $tree->path, _class => 'schema';
1301             }
1302              
1303             sub complexBody($$)
1304 544     544 0 1613 { my ($self, $tree, $mixed) = @_;
1305              
1306 544 50       1275 $tree->currentChild
1307             or return ();
1308              
1309             # partial
1310             # (group|all|choice|sequence)?
1311             # , ((attribute|attributeGroup)*
1312             # , anyAttribute?
1313              
1314 544         2447 my @elems;
1315 544 100       1336 if($tree->currentLocal =~ $particle_blocks)
1316 512         1261 { push @elems, $self->particle($tree->descend); # unless $mixed;
1317 512         1856 $tree->nextChild;
1318             }
1319              
1320 544         1626 my @attrs = $self->attributeList($tree);
1321              
1322 544 50       1019 defined $tree->currentChild
1323             and error __x"trailing non-attribute `{name}' at {where}"
1324             , name => $tree->currentChild->localName, where => $tree->path
1325             , _class => 'schema';
1326              
1327 544         3450 {elems => \@elems, mixed => $mixed, @attrs};
1328             }
1329              
1330             sub attributeList($)
1331 572     572 0 955 { my ($self, $tree) = @_;
1332              
1333             # partial content
1334             # ((attribute|attributeGroup)*
1335             # , anyAttribute?
1336              
1337 572         970 my $where = $tree->path;
1338              
1339 572         792 my (@attrs, @any);
1340 572         973 for(my $attr = $tree->currentChild; defined $attr; $attr = $tree->nextChild)
1341 262         783 { my $name = $attr->localName;
1342 262 100       533 if($name eq 'attribute')
    100          
1343 246         613 { push @attrs, $self->attribute($tree->descend);
1344             }
1345             elsif($name eq 'attributeGroup')
1346 6         14 { my %group = $self->attributeGroup($tree->descend);
1347 6         16 push @attrs, @{$group{attrs}};
  6         137  
1348 6         9 push @any, @{$group{attrs_any}};
  6         21  
1349             }
1350 10         16 else { last }
1351             }
1352              
1353             # officially only one: don't believe that
1354 572         1213 while($tree->currentLocal eq 'anyAttribute')
1355 10         21 { push @any, $self->anyAttribute($tree->descend);
1356 10         28 $tree->nextChild;
1357             }
1358              
1359 572         1860 (attrs => \@attrs, attrs_any => \@any);
1360             }
1361              
1362             sub simpleContent($)
1363 22     22 0 36 { my ($self, $tree) = @_;
1364              
1365             # attributes: id
1366             # content: annotation?, (restriction | extension)
1367              
1368 22 50       41 $tree->nrChildren==1
1369             or error __x"need one simpleContent child at {where}"
1370             , where => $tree->path, _class => 'schema';
1371              
1372 22         163 my $name = $tree->currentLocal;
1373 22 100       59 return $self->simpleContentExtension($tree->descend)
1374             if $name eq 'extension';
1375              
1376 8 50       23 return $self->simpleContentRestriction($tree->descend)
1377             if $name eq 'restriction';
1378              
1379 0         0 error __x"simpleContent needs extension or restriction, not `{name}' at {where}"
1380             , name => $name, where => $tree->path, _class => 'schema';
1381             }
1382              
1383             sub simpleContentExtension($)
1384 14     14 0 21 { my ($self, $tree) = @_;
1385              
1386             # attributes: id, base = QName
1387             # content: annotation?
1388             # , (attribute | attributeGroup)*
1389             # , anyAttribute?
1390             # , (assert | report)*
1391              
1392 14         25 my $node = $tree->node;
1393 14         23 my $where = $tree->path . '#sext';
1394              
1395 14         33 my $base = $node->getAttribute('base');
1396 14 50       138 my $typename = defined $base ? $self->rel2abs($where, $node, $base)
1397             : $self->anyType($node);
1398              
1399 14   33     35 my $basetype = $self->blocked($where, simpleType => $typename)
1400             || $self->typeByName($where, $tree, $typename);
1401             defined $basetype->{st}
1402 14 50       130 or error __x"base of simpleContent not simple at {where}"
1403             , where => $where, _class => 'schema';
1404            
1405 14         38 $self->extendAttrs($basetype, {$self->attributeList($tree)});
1406              
1407 14 50       42 $tree->currentChild
1408             and error __x"elements left at tail at {where}"
1409             , where => $tree->path, _class => 'schema';
1410              
1411 14         60 $basetype;
1412             }
1413              
1414             sub simpleContentRestriction($$)
1415 8     8 0 17 { my ($self, $tree) = @_;
1416              
1417             # attributes id, base = QName
1418             # content: annotation?
1419             # , (simpleType?, facet*)?
1420             # , (attribute | attributeGroup)*, anyAttribute?
1421             # , (assert | report)*
1422              
1423 8         14 my $node = $tree->node;
1424 8         13 my $where = $tree->path . '#cres';
1425              
1426 8         13 my ($type, $typename);
1427 8   50     16 my $first = $tree->currentLocal || '';
1428 8 100       25 if($first eq 'simpleType')
    50          
1429 4         11 { $type = $self->simpleType($tree->descend);
1430 4         32 $tree->nextChild;
1431             }
1432             elsif(my $basename = $node->getAttribute('base'))
1433 4         44 { $typename = $self->rel2abs($where, $node, $basename);
1434 4   33     9 $type = $self->blocked($where, simpleType => $type)
1435             || $self->typeByName($where, $tree, $typename);
1436             }
1437             else
1438 0         0 { error __x"no base in complex-restriction, so simpleType required at {where}"
1439             , where => $where, _class => 'schema';
1440             }
1441              
1442             my $st = $type->{st}
1443 8 50       22 or error __x"not a simpleType in simpleContent/restriction at {where}"
1444             , where => $where, _class => 'schema';
1445              
1446 8         33 $type->{st} = $self->applySimpleFacets($tree, $st, 0, $typename);
1447              
1448 8         32 $self->extendAttrs($type, {$self->attributeList($tree)});
1449              
1450 8 50       23 $tree->currentChild
1451             and error __x"elements left at tail at {where}"
1452             , where => $where, _class => 'schema';
1453              
1454 8         34 $type;
1455             }
1456              
1457             sub complexContent($$)
1458 33     33 0 71 { my ($self, $tree, $mixed) = @_;
1459              
1460             # attributes: id, mixed = boolean
1461             # content: annotation?, (restriction | extension)
1462              
1463 33         266 my $node = $tree->node;
1464 33 50       82 if(my $m = $node->getAttribute('mixed'))
1465             { $mixed = $self->isTrue($m)
1466 0 0       0 if $self->{mixed_elements} ne 'STRUCTURAL';
1467             }
1468              
1469 33 50       335 $tree->nrChildren == 1
1470             or error __x"only one complexContent child expected at {where}"
1471             , where => $tree->path, _class => 'schema';
1472              
1473 33         88 my $name = $tree->currentLocal;
1474 33 100 100     124 error __x"complexContent needs extension or restriction, not `{name}' at {where}"
1475             , name => $name, where => $tree->path, _class => 'schema'
1476             if $name ne 'extension' && $name ne 'restriction';
1477              
1478 32         71 $tree = $tree->descend;
1479 32         71 $node = $tree->node;
1480 32   33     73 my $base = $node->getAttribute('base') || $self->anyType($node);
1481 32         289 my $type = {};
1482 32         61 my $where = $tree->path . '#cce';
1483              
1484 32 50       102 if($base !~ m/\banyType$/)
1485 32         72 { my $typename = $self->rel2abs($where, $node, $base);
1486 32 100       81 if($type = $self->blocked($where, complexType => $typename))
1487             { # blocked base type
1488             }
1489             else
1490 28 50       64 { my $typedef = $self->namespaces->find(complexType => $typename)
1491             or error __x"unknown base type '{type}' at {where}"
1492             , type => $typename, where => $tree->path, _class => 'schema';
1493              
1494 28         68 local $self->{_context} = $self->nsContext($typedef);
1495 28         92 $type = $self->complexType($tree->descend($typedef->{node}));
1496             }
1497             }
1498              
1499 32         92 my $own = $self->complexBody($tree, $mixed);
1500 32         156 $self->extendAttrs($type, $own);
1501              
1502 32 100       75 if($name eq 'extension')
1503 28 50       1581 { push @{$type->{elems}}, @{$own->{elems} || []};
  28         46  
  28         102  
1504             }
1505             else # restriction
1506 4         29 { $type->{elems} = $own->{elems};
1507             }
1508              
1509 32   33     143 $type->{mixed} ||= $own->{mixed};
1510 32         145 $type;
1511             }
1512              
1513             #
1514             # Helper routines
1515             #
1516              
1517             # print $self->rel2abs($path, $node, '{ns}type') -> '{ns}type'
1518             # print $self->rel2abs($path, $node, 'prefix:type') -> '{ns-of-prefix}type'
1519              
1520             sub rel2abs($$$)
1521 2115     2115 0 3728 { my ($self, $where, $node, $type) = @_;
1522 2115 50       5734 return $type if substr($type, 0, 1) eq '{';
1523              
1524 2115 100       7646 my ($prefix, $local) = $type =~ m/^(.+?)\:(.*)/ ? ($1, $2) : ('', $type);
1525 2115         7456 my $uri = $node->lookupNamespaceURI($prefix);
1526 2115 100       5978 $self->_registerNSprefix($prefix, $uri, 0) if $uri;
1527              
1528 2115 50 66     4938 error __x"No namespace for prefix `{prefix}' in `{type}' at {where}"
1529             , prefix => $prefix, type => $type, where => $where, _class => 'schema'
1530             if length $prefix && !defined $uri;
1531              
1532 2115         4315 pack_type $uri, $local;
1533             }
1534              
1535             sub _registerNSprefix($$$)
1536 2353     2353   3549 { my ($self, $prefix, $uri, $used) = @_;
1537 2353         2945 my $table = $self->{prefixes};
1538              
1539 2353 100       4795 if(my $u = $table->{$uri}) # namespace already has a prefix
1540 1212         1636 { $u->{used} += $used;
1541 1212         2023 return $u->{prefix};
1542             }
1543              
1544 1141         5715 my %prefs = map +($_->{prefix} => 1), values %$table;
1545 1141         1530 my $take;
1546 1141 100 66     4211 if(defined $prefix && !$prefs{$prefix}) { $take = $prefix }
  724 50       986  
1547 0         0 elsif(!$prefs{''}) { $take = '' }
1548             else
1549             { # prefix already in use; create a new x\d+ prefix
1550 417         572 my $count = 0;
1551 417         1181 $count++ while exists $prefs{"x$count"};
1552 417         612 $take = 'x'.$count;
1553             }
1554 1141         7030 $table->{$uri} = {prefix => $take, uri => $uri, used => $used};
1555 1141         2250 $take;
1556             }
1557              
1558             sub anyType($)
1559 15     15 0 31 { my ($self, $node) = @_;
1560 15         72 pack_type $node->namespaceURI, 'anyType';
1561             }
1562              
1563             sub findHooks($$$)
1564 1852     1852 0 2924 { my ($self, $path, $type, $node) = @_;
1565             # where is before, replace, after
1566              
1567 1852         1984 my %hooks;
1568 1852         1829 foreach my $hook (@{$self->{hooks}})
  1852         3619  
1569 61         60 { my $match;
1570              
1571             $match++
1572             if !$hook->{path} && !$hook->{id}
1573 61 0 100     216 && !$hook->{type} && !$hook->{extends};
      66        
      33        
1574              
1575 61 100 66     167 if(!$match && $hook->{path})
1576 8         11 { my $p = $hook->{path};
1577             $match++
1578 8 50   8   59 if first {ref $_ eq 'Regexp' ? $path =~ $_ : $path eq $_}
1579 8 50       44 ref $p eq 'ARRAY' ? @$p : $p;
    100          
1580             }
1581              
1582 61   100     198 my $id = !$match && $hook->{id} && $node->getAttribute('id');
1583 61 100       293 if($id)
1584 15         24 { my $i = $hook->{id};
1585             $match++
1586 15 50   15   50 if first {ref $_ eq 'Regexp' ? $id =~ $_ : $id eq $_}
1587 15 50       91 ref $i eq 'ARRAY' ? @$i : $i;
    100          
1588             }
1589              
1590 61 100 66     236 if(!$match && defined $type && $hook->{type})
      100        
1591 36         41 { my $t = $hook->{type};
1592 36         74 my ($ns, $local) = unpack_type $type;
1593             $match++
1594 36 100   36   154 if first {ref $_ eq 'Regexp' ? $type =~ $_
    50          
1595             : substr($_,0,1) eq '{' ? $type eq $_
1596             : $local eq $_
1597 36 50       167 } ref $t eq 'ARRAY' ? @$t : $t;
    100          
1598             }
1599              
1600 61 50 66     297 if(!$match && defined $type && $hook->{extends})
      66        
1601 0 0       0 { $match++ if $self->{nss}->doesExtend($type, $hook->{extends});
1602             }
1603              
1604 61 100       111 $match or next;
1605              
1606 23         38 foreach my $where ( qw/before replace after/ )
1607 69 100       123 { my $w = $hook->{$where} or next;
1608 29 100       30 push @{$hooks{$where}}, ref $w eq 'ARRAY' ? @$w : $w;
  29         115  
1609             }
1610             }
1611              
1612 1852         4317 @hooks{ qw/before replace after/ };
1613             }
1614              
1615             # Namespace blocks, in most cases because the schema refers to an
1616             # older version of itself, which is deprecated.
1617             # performance is important, because it is called increadably often.
1618              
1619             sub decodeBlocked($)
1620 771     771 0 1254 { my ($self, $what) = @_;
1621 771 50       1531 defined $what or return;
1622 771         960 my @blocked; # code-refs called with ($type, $ns, $local, $path)
1623 771 50       2436 foreach my $w (ref $what eq 'ARRAY' ? @$what : $what)
1624             { push @blocked,
1625 86 50   86   241 !ref $w ? sub { $_[0] eq $w || $_[1] eq $w }
1626             : ref $w eq 'HASH'
1627 0 0   0   0 ? sub { defined $w->{$_[0]} ? $w->{$_[0]} : $w->{$_[1]} }
1628 30 0       130 : ref $what eq 'CODE' ? $w
    0          
    50          
1629             : error __x"blocking rule with {what} not supported", what => $w;
1630             }
1631 771         1586 \@blocked;
1632             }
1633              
1634             sub blocked($$$)
1635 2113     2113 0 3419 { my ($self, $path, $class, $type) = @_;
1636             # $class = simpleType, complexType, or ref
1637 2113 100       2152 @{$self->{blocked_nss}} or return ();
  2113         8539  
1638              
1639 86         130 my ($ns, $local) = unpack_type $type;
1640 86         107 my $is_blocked;
1641 86         99 foreach my $blocked ( @{$self->{blocked_nss}} )
  86         140  
1642 86         142 { $is_blocked = $blocked->($type, $ns, $local, $path);
1643 86 50       154 last if defined $is_blocked;
1644             }
1645 86 100       224 $is_blocked or return;
1646              
1647 38         151 trace "$type of $class is blocked";
1648 38         838 $self->makeBlocked($path, $class, $type);
1649             }
1650              
1651             sub addTypeAttribute($$)
1652 0     0 0   { my ($self, $type, $call) = @_;
1653 0           $call;
1654             }
1655              
1656             #------------
1657              
1658             1;