File Coverage

lib/XML/Compile/Translate/Writer.pm
Criterion Covered Total %
statement 561 634 88.4
branch 298 458 65.0
condition 85 130 65.3
subroutine 111 119 93.2
pod 0 38 0.0
total 1055 1379 76.5


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::Writer;
10 47     47   1606 use vars '$VERSION';
  47         78  
  47         2632  
11             $VERSION = '1.63';
12              
13 47     47   247 use base 'XML::Compile::Translate';
  47         68  
  47         4969  
14              
15 47     47   266 use strict;
  47         73  
  47         1010  
16 47     47   215 use warnings;
  47         86  
  47         2015  
17 47     47   242 no warnings 'once', 'recursion';
  47         2884  
  47         1976  
18              
19 47     47   252 use Log::Report 'xml-compile';
  47         97  
  47         323  
20              
21 47     47   14167 use List::Util qw/first/;
  47         88  
  47         3176  
22 47     47   257 use Scalar::Util qw/blessed weaken/;
  47         84  
  47         2273  
23 47     47   269 use Encode qw/encode/;
  47         102  
  47         1830  
24              
25 47     47   263 use XML::LibXML;
  47         95  
  47         294  
26 47         11357 use XML::Compile::Util qw/pack_type unpack_type type_of_node SCHEMA2001i
27 47     47   8305 SCHEMA2001 odd_elements even_elements/;
  47         82  
28              
29              
30             # Each action implementation returns a code reference, which will be
31             # used to do the run-time work. The principle of closures is used to
32             # keep the important information. Be sure that you understand closures
33             # before you attempt to change anything.
34             #
35             # The returned writer subroutines will always be called
36             # $writer->($doc, $value)
37              
38 1157     1157 0 4029 sub actsAs($) { $_[1] eq 'WRITER' }
39              
40             sub makeTagQualified
41             { # my ($self, $path, $node, $local, $ns) = @_;
42             # my $prefix = $self->_registerNSprefix('', $ns, 1);
43             # length($prefix) ? "$prefix:$local" : $local;
44 132     132 0 327 my $prefix = $_[0]->_registerNSprefix('', $_[4], 1);
45 132 100       399 length($prefix) ? "$prefix:$_[3]" : $_[3];
46             }
47              
48             sub makeTagUnqualified
49             { # my ($self, $path, $node, $local, $ns) = @_;
50             # $local;
51 823     823 0 1542 $_[3];
52             }
53              
54             sub _typemapClass($$)
55 1     1   2 { my ($self, $type, $class) = @_;
56              
57 47     47   291 no strict 'refs';
  47         81  
  47         358914  
58 1 50       1 keys %{$class.'::'}
  1         5  
59             or error __x"class {pkg} for typemap {type} is not loaded"
60             , pkg => $class, type => $type;
61              
62 1 50       7 $class->can('toXML')
63             or error __x"class {pkg} does not implement toXML(), required for typemap {type}"
64             , pkg => $class, type => $type;
65              
66             sub {
67 1     1   3 my ($doc, $values, $path) = @_;
68 1 50       6 UNIVERSAL::isa($values, $class) ? $values->toXML($type, $doc) : $values;
69 1         5 };
70             }
71              
72             sub _typemapObject($$)
73 1     1   3 { my ($self, $type, $object) = @_;
74              
75 1 50       6 $object->can('toXML')
76             or error __x"object of class {pkg} does not implement toXML(), required for typemap {type}"
77             , pkg => ref($object), type => $type;
78              
79             sub {
80 1     1   3 my ($doc, $values, $path) = @_;
81 1 50       7 blessed($values) ? $object->toXML($values, $type, $doc) : $values;
82 1         5 };
83             }
84              
85             sub typemapToHooks($$)
86 356     356 0 704 { my ($self, $hooks, $typemap) = @_;
87 356         1702 while(my($type, $action) = each %$typemap)
88 3 50       7 { defined $action or next;
89 3         4 my $hook;
90 3 100       10 if(!ref $action)
    100          
91 1         4 { $hook = $self->_typemapClass($type, $action);
92 1         5 trace "created writer hook for type $type to class $action";
93             }
94             elsif(ref $action eq 'CODE')
95             { $hook = sub {
96 1     1   3 my ($doc, $values, $path) = @_;
97 1 50       6 blessed($values)
98             ? $action->(WRITER => $values, $type, $doc)
99             : $values;
100 1         5 };
101 1         5 trace "created writer hook for type $type to CODE";
102             }
103             else
104 1         3 { $hook = $self->_typemapObject($type, $action);
105 1         6 trace "created reader hook for type $type to object";
106              
107             }
108              
109 3         68 push @$hooks, +{action => 'WRITER', type => $type, before => $hook};
110             }
111 356         652 $hooks;
112             }
113              
114             sub makeElementWrapper
115 356     356 0 713 { my ($self, $path, $processor) = @_;
116             sub {
117 358     358   288052 my ($doc, $data) = @_;
118 358 50       1546 UNIVERSAL::isa($doc, 'XML::LibXML::Document')
119             or error __x"first argument of call to writer must be an XML::LibXML::Document";
120              
121              
122 358         777 my $top = $processor->(@_);
123 315         4885 $doc->indexElements;
124 315         676 $top;
125 356         1362 };
126             }
127             *makeAttributeWrapper = \&makeElementWrapper;
128              
129             sub makeWrapperNs
130 55     55 0 134 { my ($self, $path, $processor, $index, $filter) = @_;
131 55         81 my @entries;
132 55 50   184   254 $filter = sub {$_[2]} if ref $filter ne 'CODE'; # only the used
  184         327  
133              
134 55         305 foreach my $entry (sort {$a->{prefix} cmp $b->{prefix}} values %$index)
  195         412  
135             { # ANY components are frustrating this
136 184 100       308 $filter->($entry->{uri}, $entry->{prefix}, $entry->{used}) or next;
137 73         192 push @entries, [ $entry->{uri}, $entry->{prefix} ];
138 73         125 $entry->{used} = 0;
139             }
140              
141 55 100       136 @entries or return $processor;
142              
143             sub {
144 55 50   55   52088 my $node = $processor->(@_) or return ();
145 52 50       323 UNIVERSAL::isa($node, 'XML::LibXML::Element')
146             or return $node;
147 52         208 $node->setNamespace(@$_, 0) for @entries;
148 52         939 $node;
149 54         793 };
150             }
151              
152             sub makeSequence($@)
153 207     207 0 570 { my ($self, $path, @pairs) = @_;
154              
155 207 100       523 if(@pairs==2)
156 80         158 { my ($take, $do) = @pairs;
157              
158             return
159 0     0   0 ref $do eq 'ANY' ? bless(sub { $do->(@_) }, 'BLOCK')
160             : ref $do eq 'BLOCK' ? $do
161             : bless sub {
162 69     69   120 my ($doc, $values) = @_;
163 69 50       143 defined $values or return;
164 69         175 $do->($doc, delete $values->{$take});
165 80 100       717 }, 'BLOCK';
    50          
166             }
167            
168             bless sub {
169 133     133   236 my ($doc, $values) = @_;
170 133 50       302 defined $values or return;
171              
172 133         155 my @res;
173 133         309 my @do = @pairs;
174 133         315 while(@do)
175 303         590 { my ($take, $do) = (shift @do, shift @do);
176             push @res
177             , ref $do eq 'BLOCK' ? $do->($doc, $values)
178             : ref $do eq 'ANY' ? $do->($doc, $values)
179 303 100       2896 : $do->($doc, delete $values->{$take});
    100          
180             }
181 126         1889 @res;
182 127         959 }, 'BLOCK';
183             }
184              
185             sub makeChoice($@)
186 38     38 0 72 { my ($self, $path) = (shift, shift);
187 38         46 my (%do, @specials);
188 38         92 while(@_) # protect order of specials
189 89         126 { my ($el, $do) = (shift, shift);
190 89 100 66     262 if(ref $do eq 'BLOCK' || ref $do eq 'ANY')
191 12         30 { push @specials, $do }
192 77         213 else { $do{$el} = $do }
193             }
194            
195 38 100 100     156 if(!@specials && keys %do==1)
196 2         5 { my ($take, $do) = %do;
197             return bless sub {
198 2     2   4 my ($doc, $values) = @_;
199             defined $values && defined $values->{$take}
200 2 50 33     16 ? $do->($doc, delete $values->{$take}) : ();
201 2         16 }, 'BLOCK';
202             }
203              
204             bless sub {
205 43     43   77 my ($doc, $values) = @_;
206 43 50       96 defined $values or return ();
207 43         101 foreach my $take (keys %do)
208             {
209             #warn "TAKE($take) = ", (defined $values->{$take} ? 'defined' : "undef");
210             return $do{$take}->($doc, delete $values->{$take})
211 68 100       194 if defined $values->{$take};
212             }
213              
214             #warn "TRY SPECIALS";
215 9         23 my $starter = keys %$values;
216 9         16 foreach (@specials)
217 6         32 { my @d = try { $_->($doc, $values) };
  6         1363  
218 6 50       104 if(my $f = $@->wasFatal(class => 'misfit'))
    50          
219             { # misfit error is ok, if nothing consumed
220 0         0 my $err = $@;
221 0         0 trace "misfit $path ".$@->wasFatal->message;
222 0 0       0 $err->reportAll if $starter != keys %$values;
223 0         0 next;
224             }
225 6         67 elsif(defined $@) {$@->reportAll}
226              
227 6         58 return @d;
228             }
229              
230             #warn "BLURK!";
231             # blurk... any element with minOccurs=0 or default?
232 3         8 foreach (values %do)
233 5         33 { my @d = try { $_->($doc, undef) };
  5         1067  
234 5 100 66     858 return @d if !$@ && @d;
235             }
236 2         15 foreach (@specials)
237 0         0 { my @d = try { $_->($doc, undef) };
  0         0  
238 0 0       0 if($@->wasFatal(class => 'misfit'))
    0          
239 0 0       0 { $@->reportAll if $starter != keys %$values;
240 0         0 next;
241             }
242 0         0 elsif(defined $@) {$@->reportAll}
243 0         0 return @d;
244             }
245              
246 2         5 ();
247 36         286 }, 'BLOCK';
248             }
249              
250             sub makeAll($@)
251 14     14 0 43 { my ($self, $path, @pairs) = @_;
252              
253 14 50 66     108 if(@pairs==2 && !ref $pairs[1])
254 0         0 { my ($take, $do) = @pairs;
255             return bless sub {
256 0     0   0 my ($doc, $values) = @_;
257 0         0 $do->($doc, delete $values->{$take});
258 0         0 }, 'BLOCK';
259             }
260              
261             return bless sub {
262 13     13   21 my ($doc, $values) = @_;
263              
264 13         17 my @res;
265 13         32 my @do = @pairs;
266 13         37 while(@do)
267 35         57 { my ($take, $do) = (shift @do, shift @do);
268             push @res
269             , ref $do eq 'BLOCK' || ref $do eq 'ANY'
270             ? $do->($doc, $values)
271 35 100 66     166 : $do->($doc, delete $values->{$take});
272             }
273 13         33 @res;
274 14         97 }, 'BLOCK';
275             }
276            
277             #
278             ## Element
279             #
280              
281             # see comment BlockHandler: undef means zero but success
282             sub makeElementHandler
283 452     452 0 1053 { my ($self, $path, $label, $min,$max, $required, $optional) = @_;
284 452 100   1   869 $max eq "0" and return sub {};
285              
286 451 100 100     1749 if($min==0 && $max eq 'unbounded')
287             { return
288 12     12   22 sub { my ($doc, $values) = @_;
289 12 50       31 my @values = ref $values eq 'ARRAY' ? @$values
    100          
290             : defined $values ? $values : ();
291 12 100       30 @values ? map {$optional->($doc,$_)} @values : (undef);
  15         23  
292 11         87 };
293             }
294              
295 440 50       758 if($max eq 'unbounded')
296             { return
297 0     0   0 sub { my ($doc, $values) = @_;
298 0 0       0 my @values = ref $values eq 'ARRAY' ? @$values
    0          
299             : defined $values ? $values : ();
300 0         0 my @d = ( (map { $required->($doc, shift @values) } 1..$min)
301 0         0 , (map { $optional->($doc, $_) } @values) );
  0         0  
302 0 0       0 @d ? @d : (undef);
303 0         0 };
304             }
305              
306 94 100   94   214 return sub { my @d = $optional->(@_); @d ? @d : undef }
  92         357  
307 440 100 100     1602 if $min==0 && $max==1;
308              
309 352 100 100     2493 return $required
310             if $min==1 && $max==1;
311              
312             sub {
313 12     12   21 my ($doc, $values) = @_;
314             my @values
315 12 50       46 = ref $values eq 'ARRAY' ? @$values : defined $values ? $values : ();
    100          
316              
317 12 50       24 @values <= $max
318             or error "too many elements for `{tag}', max {max} found {nr} at {path}"
319             , tag => $label, max => $max, nr => (scalar @values)
320             , path => $path;
321              
322 12         23 my @d = map { $required->($doc, shift @values) } 1..$min;
  3         4  
323 12   66     49 push @d, $optional->($doc, shift @values)
324             while @values && @d < $max;
325              
326 12 100       38 @d ? @d : (undef);
327 11         110 };
328             }
329              
330             # To reflect the difference between a block which did not "succeed hence
331             # produced nothing", and "did succeed by producing nothing" (minOccurs=0)
332             # the later is represented by an undef value.
333             sub makeBlockHandler
334 267     267 0 2353 { my ($self, $path, $label, $min, $max, $process, $kind, $multi) = @_;
335              
336 267 100 100     823 if($min==0 && $max eq 'unbounded')
337             { my $code = sub {
338 14     14   25 my $doc = shift;
339 14 50       42 my $values = $_[0] ? delete shift->{$multi} : undef;
340 14 50       55 ref $values eq 'ARRAY' ? (map {$process->($doc, {%$_})} @$values)
  23 100       72  
341             : defined $values ? $process->($doc, $values)
342             : (undef);
343 14         51 };
344 14         66 return ($multi, bless($code, 'BLOCK'));
345             }
346              
347 253 100       2303 if($max eq 'unbounded')
348             { my $code = sub {
349 4     4   7 my $doc = shift;
350 4 50       15 my $values = $_[0] ? delete shift->{$multi} : undef;
351 4 0       15 my @values = ref $values eq 'ARRAY' ? @$values
    50          
352             : defined $values ? $values : ();
353              
354 4 50       13 @values >= $min
355             or error __x"too few blocks for `{tag}' specified, got {found} need {min} at {path}"
356             , tag => $multi, found => scalar @values
357             , min => $min, path => $path, _class => 'misfit';
358              
359 4         6 map {$process->($doc, {%$_}) } @values;
  13         42  
360 5         21 };
361 5         30 return ($multi, bless($code, 'BLOCK'));
362             }
363              
364 248 100 100     658 if($min==0 && $max==1)
365             { my $code = sub {
366 5     5   9 my ($doc, $values) = @_;
367 5 50       59 my @values = ref $values eq 'ARRAY' ? @$values
    50          
368             : defined $values ? $values : ();
369 5 50       15 @values <= 1
370             or error __x"only one block value for `{tag}', not {count} at {path}"
371             , tag => $multi, count => scalar @values
372             , path => $path, _class => 'misfit';
373              
374             # @values ? $process->($doc, $values[0]) : undef;
375 5 50       14 @values or return undef;
376              
377 5         14 my $starter = keys %$values;
378 5         33 my @d = try { $process->($doc, $values[0]) };
  5         1108  
379 5 100 100     692 $@->wasFatal(class => 'misfit') && $starter==keys %$values
380             or $@->reportAll;
381 4         158 @d;
382 5         25 };
383 5         30 return ($label, bless($code, 'BLOCK'));
384             }
385              
386 243 100 100     915 if($min==1 && $max==1)
387             { my $code = sub {
388 225     225   474 my @d = $process->(@_);
389 216 100       512 @d or error __x"no match for required block `{tag}' at {path}"
390             , tag => $multi, path => $path, _class => 'misfit';
391 215         573 @d;
392 232         2565 };
393 232         1065 return ($label, bless($code, 'BLOCK'));
394             }
395              
396 11         25 my $opt = $max - $min;
397             my $code = sub {
398 10     10   21 my $doc = shift;
399 10         25 my $values = delete shift->{$multi};
400 10 50       43 my @values = ref $values eq 'ARRAY' ? @$values
    100          
401             : defined $values ? $values : ();
402              
403 10 100 100     58 @values >= $min && @values <= $max
404             or error __x"found {found} blocks for `{tag}', must be between {min} and {max} inclusive at {path}"
405             , tag => $multi, min => $min, max => $max, path => $path
406             , found => scalar @values, _class => 'misfit';
407              
408 7         15 map { $process->($doc, {%$_}) } @values;
  10         60  
409 11         45 };
410              
411 11         60 ($multi, bless($code, 'BLOCK'));
412             }
413              
414             sub makeRequired
415 344     344 0 692 { my ($self, $path, $label, $do) = @_;
416             my $req = sub {
417 299     299   533 my @nodes = $do->(@_);
418 295 100       1065 return @nodes if @nodes;
419              
420 8 50       28 error __x"required data for block (starts with `{tag}') missing at {path}"
421             , tag => $label, path => $path, _class => 'misfit'
422             if ref $do eq 'BLOCK';
423              
424 8         34 error __x"required value for element `{tag}' missing at {path}"
425             , tag => $label, path => $path, _class => 'misfit';
426 344         1201 };
427 344 50       793 bless $req, 'BLOCK' if ref $do eq 'BLOCK';
428 344         583 $req;
429             }
430              
431             sub makeElement
432 791     791 0 3207 { my ($self, $path, $ns, $childname, $do) = @_;
433 791 100   767   2383 sub { defined $_[1] ? $do->(@_) : () };
  767         1900  
434             }
435              
436             sub makeElementFixed
437 4     4 0 13 { my ($self, $path, $ns, $childname, $do, $fixed) = @_;
438 4 50       13 $fixed = $fixed->value if ref $fixed;
439              
440             sub {
441 4     4   8 my ($doc, $value) = @_;
442 4 100       11 my $ret = defined $value ? $do->($doc, $value) : return;
443 3 100 66     34 return $ret
444             if defined $ret && $ret->textContent eq $fixed;
445              
446 1 50       4 defined $ret
447             or error __x"required element `{name}' with fixed value `{fixed}' missing at {path}"
448             , name => $childname, fixed => $fixed, path => $path,
449             , _class => 'misfit';
450              
451 1         9 error __x"element `{name}' has value fixed to `{fixed}', got `{value}' at {path}"
452             , name => $childname, fixed => $fixed
453             , value => $ret->textContent, path => $path, _class => 'misfit';
454 4         18 };
455             }
456              
457             sub makeElementDefault
458 18     18 0 44 { my ($self, $path, $ns, $childname, $do, $default) = @_;
459 18         30 my $mode = $self->{default_values};
460              
461             $mode eq 'IGNORE'
462 18 100   14   58 and return sub { defined $_[1] ? $do->(@_) : () };
  14 100       29  
463              
464             $mode eq 'EXTEND'
465 4 100   2   17 and return sub { $do->($_[0], (defined $_[1] ? $_[1] : $default)) };
  2 100       7  
466              
467             $mode eq 'MINIMAL'
468 2 100 66 2   10 and return sub { defined $_[1] && $_[1] ne $default ? $do->(@_) : () };
  2 50       13  
469              
470 0         0 error __x"illegal default_values mode `{mode}'", mode => $mode;
471             }
472              
473             sub makeElementAbstract
474 7     7 0 21 { my ($self, $path, $ns, $childname, $do, $default) = @_;
475 1 50   1   3 sub { defined $_[1] or return ();
476 1         4 error __x"attempt to instantiate abstract element `{name}' at {where}"
477             , name => $childname, where => $path;
478 7         24 };
479             }
480              
481             #
482             # complexType/ComplexContent
483             #
484              
485             sub nil($)
486 20     20 0 41 { my ($self, $path) = @_;
487 20         57 $self->makeTagQualified($path, undef, 'nil', SCHEMA2001i);
488             }
489              
490             sub makeComplexElement
491 235     235 0 772 { my ($self, $path, $tag, $elems, $attrs, $any_attr,undef, $is_nillable) = @_;
492 235         676 my @elems = odd_elements @$elems;
493 235         432 my @attrs = @$attrs;
494 235         644 my $tags = join ', ', grep defined
495             , even_elements(@$elems), even_elements(@attrs);
496 235         458 my @anya = @$any_attr;
497 235         370 my $iut = $self->{ignore_unused_tags};
498 235 100       473 my $nilattr = $is_nillable ? $self->nil($path) : undef;
499              
500             return
501             sub
502 236     236   420 { my ($doc, $data) = @_;
503 236 50       637 $data = { _ => 'NIL' } if $data eq 'NIL';
504              
505 236 50       643 return $doc->importNode($data)
506             if UNIVERSAL::isa($data, 'XML::LibXML::Element');
507              
508 236 50       520 unless(UNIVERSAL::isa($data, 'HASH'))
509 0 0       0 { defined $data
510             or error __x"complex `{tag}' requires data at {path}"
511             , tag => $tag, path => $path, _class => 'misfit';
512              
513 0   0     0 error __x"complex `{tag}' requires a HASH of input data, not `{got}' at {path}"
514             , tag => $tag, got => (ref $data || $data), path => $path;
515             }
516              
517 236         745 my $copy = { %$data }; # do not destroy callers hash
518              
519 236 100 100     1167 my @childs = ($is_nillable && (delete $copy->{_} || '') eq 'NIL')
520             ? $doc->createAttribute($nilattr => 'true')
521             : map($_->($doc, $copy), @elems);
522              
523 223         634 for(my $i=0; $i<@attrs; $i+=2)
524 90         287 { push @childs, $attrs[$i+1]->($doc, delete $copy->{$attrs[$i]});
525             }
526              
527             push @childs, $_->($doc, $copy)
528 221         447 for @anya;
529              
530 221 100       438 if(%$copy)
531             { my @not_used
532 3 50       11 = defined $iut ? (grep $_ !~ $iut, keys %$copy) : keys %$copy;
533              
534 3 50       6 if(@not_used)
535 3         14 { trace "available tags are: $tags";
536 3         160 mistake __xn "tag `{tags}' not used at {path}"
537             , "unused tags {tags} at {path}"
538             , scalar @not_used, tags => [sort @not_used], path => $path;
539             }
540             }
541              
542 221         1523 my $node = $doc->createElement($tag);
543              
544 221         418 foreach my $child (@childs)
545 508 100       2773 { defined $child or next;
546 460 50       778 if(ref $child)
547 460 50 33     1268 { next if UNIVERSAL::isa($child, 'XML::LibXML::Text')
548             && $child->data eq '';
549             }
550             else
551 0 0       0 { length $child or next;
552 0         0 $child = XML::LibXML::Text->new($child);
553             }
554 460         1619 $node->addChild($child);
555             }
556              
557 221         1755 $node;
558 235         1669 };
559             }
560              
561             #
562             # complexType/simpleContent
563             #
564              
565             sub makeTaggedElement
566 9     9 0 28 { my ($self, $path, $tag, $st, $attrs, $attrs_any,undef, $is_nillable) = @_;
567 9         23 my @attrs = @$attrs;
568 9         16 my @anya = @$attrs_any;
569 9 100       22 my $nilattr = $is_nillable ? $self->nil($path) : undef;
570              
571             return sub {
572 9     9   18 my ($doc, $data) = @_;
573 9 50       26 return $doc->importNode($data)
574             if UNIVERSAL::isa($data, 'XML::LibXML::Element');
575              
576 9 50       44 my $copy = UNIVERSAL::isa($data,'HASH') ? {%$data} : {_ => $data};
577 9         20 my $content = delete $copy->{_};
578              
579 9         14 my ($node, @childs);
580 9 50 66     51 if(UNIVERSAL::isa($content, 'XML::LibXML::Node'))
    100          
    50          
581 0         0 { $node = $doc->importNode($content);
582             }
583             elsif($is_nillable && $content eq 'NIL')
584 1         12 { push @childs, $doc->createAttribute($nilattr => 'true');
585             }
586             elsif(defined $content)
587 8         17 { push @childs, $st->($doc, $content);
588             }
589              
590 9         29 for(my $i=0; $i<@attrs; $i+=2)
591 10         37 { push @childs, $attrs[$i+1]->($doc, delete $copy->{$attrs[$i]});
592             }
593              
594             push @childs, $_->($doc, $copy)
595 9         20 for @anya;
596              
597 9 50       38 if(my @not_used = sort keys %$copy)
598 0         0 { error __xn "tag `{tags}' not processed at {path}"
599             , "unprocessed tags {tags} at {path}"
600             , scalar @not_used, tags => \@not_used, path => $path;
601             }
602              
603 9 50 33     37 $node or @childs or return ();
604 9   33     99 $node ||= $doc->createElement($tag);
605             $node->addChild
606             ( UNIVERSAL::isa($_, 'XML::LibXML::Node') ? $_
607             : $doc->createTextNode(defined $_ ? $_ : ''))
608 9 50       172 for @childs;
    100          
609 9         33 $node;
610 9         50 };
611             }
612              
613             #
614             # complexType mixed or complexContent mixed
615             #
616              
617             sub makeMixedElement
618 1     1 0 3 { my ($self, $path, $tag, $elems, $attrs, $attrs_any,undef, $is_nillable) =@_;
619 1         3 my @attrs = @$attrs;
620 1         1 my @anya = @$attrs_any;
621 1 50       3 my $nilattr = $is_nillable ? $self->nil($path) : undef;
622 1         5 (my $locname = $tag) =~ s/.*\://;
623              
624 1         2 my $mixed = $self->{mixed_elements};
625 1 50       3 if($mixed eq 'ATTRIBUTES') { ; }
    0          
626             elsif($mixed eq 'STRUCTURAL')
627             { # mixed_element eq STRUCTURAL is handled earlier
628 0         0 panic "mixed structural handled as normal element";
629             }
630 0         0 else { error __x"unknown mixed_elements value `{value}'", value => $mixed }
631              
632 1 0 33     3 if(!@attrs && !@anya)
633             { return
634 0     0   0 sub { my ($doc, $data) = @_;
635 0 0       0 my $node = ref $data eq 'HASH' ? $data->{_} : $data;
636 0 0       0 return $doc->importNode($node)
637             if UNIVERSAL::isa($node, 'XML::LibXML::Element');
638 0         0 error __x"mixed `{tag}' requires XML::LibXML::Node, not `{found}' at {path}"
639             , tag => $tag, found => $data, path => $path;
640 0         0 };
641             }
642              
643 1         1 my $iut = $self->{ignore_unused_tags};
644 2     2   5 sub { my ($doc, $data) = @_;
645 2 50       16 defined $data or return;
646              
647 2 100       14 return $doc->importNode($data)
648             if UNIVERSAL::isa($data, 'XML::LibXML::Element');
649              
650 1 50       6 my $copy = UNIVERSAL::isa($data, 'HASH') ? {%$data} : {_ => $data};
651 1         3 my $content = delete $copy->{_};
652 1 50       4 defined $content or return;
653              
654             #XXX there are no regression test for these options
655 1         1 my $node;
656 1 50 33     8 if(blessed $content && $content->isa('XML::LibXML::Node'))
    0 0        
    0          
657 1         7 { $node = $doc->importNode($content);
658             }
659             elsif($is_nillable && $content eq 'NIL')
660             { # nillable element
661 0         0 $node = $doc->createElement($tag);
662 0         0 $node->setAttribute($nilattr => 'true');
663             }
664             elsif($content =~ /\<.*?\>|\&\w+\;/)
665             { # XXX I do not know a way to fill text nodes without getting
666             # entity encoding on them. Apparently, libxml2 has a
667             # xmlStringTextNoenc, which is not provided in XML::LibXML.
668             # Now, we need the expensive roundtrip, via the parser.
669              
670 0         0 my $frag = XML::LibXML->new
671             ->parse_balanced_chunk(encode utf8 => $content);
672              
673 0         0 $node = $frag->firstChild;
674 0 0       0 if($node->localName ne $locname)
675             { # --> <$locname>$nodes
676 0         0 my $c = $node;
677 0         0 $node = $doc->createElement($tag);
678 0         0 $node->appendChild($frag);
679             }
680             }
681             else
682             { # Plain text
683 0         0 $node = $doc->createElement($tag);
684 0         0 $node->appendText($content);
685             }
686              
687 1         2 my @childs;
688 1         4 for(my $i=0; $i<@attrs; $i+=2)
689 1         1529 { push @childs, $attrs[$i+1]->($doc, delete $copy->{$attrs[$i]});
690             }
691              
692             push @childs, $_->($doc, $copy)
693 1         4 for @anya;
694              
695 1 50       3 if(%$copy)
696             { my @not_used
697 0 0       0 = defined $iut ? (grep $_ !~ $iut, keys %$copy) : keys %$copy;
698              
699 0 0       0 if(my @not_used = sort keys %$copy)
700 0         0 { error __xn "tag `{tags}' not processed at {path}"
701             , "unprocessed tags {tags} at {path}", scalar @not_used
702             , tags => [sort @not_used], path => $path;
703             }
704             }
705              
706 1 50       2 @childs or return $node;
707             $node->addChild
708             ( ref $_ && $_->isa('XML::LibXML::Node') ? $_
709             : $doc->createTextNode(defined $_ ? $_ : ''))
710 1 0 33     23 for @childs;
    50          
711 1         4 $node;
712 1         6 };
713             }
714              
715             #
716             # simpleType
717             #
718              
719             sub makeSimpleElement
720 575     575 0 1397 { my ($self, $path, $tag, $st, undef, undef, undef, $is_nillable) = @_;
721 575 100       2495 my $nilattr = $is_nillable ? $self->nil($path) : undef;
722              
723             sub {
724 498     498   741 my ($doc, $data) = @_;
725 498 50       1280 return $doc->importNode($data)
726             if UNIVERSAL::isa($data, 'XML::LibXML::Element');
727             $data = $data->{_}
728 498 100       988 if ref $data eq 'HASH';
729              
730 498 100 100     1442 my $value = ($is_nillable && $data eq 'NIL')
731             ? $doc->createAttribute($nilattr => 'true')
732             : $st->($doc, $data);
733              
734 467 50       1164 defined $value
735             or return ();
736              
737 467         2614 my $node = $doc->createElement($tag);
738 467 50 33     1739 error __x"expected single value for {tag}, but got {type}"
739             , tag => $tag, type => ref($value)
740             if ref $value eq 'ARRAY' || ref $value eq 'HASH';
741              
742 467 50       6098 $node->addChild
    100          
743             ( UNIVERSAL::isa($value, 'XML::LibXML::Node') ? $value
744             : $doc->createTextNode(defined $value ? $value : ''));
745 467         1733 $node;
746 575         2563 };
747             }
748              
749             sub makeBuiltin
750 705     705 0 1477 { my ($self, $path, $node, $type, $def, $check_values) = @_;
751 705 50       1401 my $check = $check_values ? $def->{check} : undef;
752 705 100       2244 my $err = $path eq $type
753             ? N__"illegal value `{value}' for type {type}"
754             : N__"illegal value `{value}' for type {type} at {path}";
755              
756 705         2101 my $format = $def->{format};
757 705         888 my $trans = $self->{prefixes};
758              
759             $check
760             ? ( defined $format
761 526 100   526   4006 ? sub { defined $_[1] or return undef;
762 504         1339 my $value = $format->($_[1], $trans, $path);
763 503 100 66     3274 return $value if defined $value && $check->($value);
764 11         32 error __x$err, value => $value, type => $type, path => $path;
765             }
766 28 50 33 28   123 : sub { return $_[1] if !defined $_[1] || $check->($_[1]);
767 0         0 error __x$err, value => $_[1], type => $type, path => $path;
768             }
769             )
770             : ( defined $format
771 6 50   6   21 ? sub { defined $_[1] ? $format->($_[1], $trans, $path) : undef }
772 45     45   76 : sub { $_[1] }
773 705 100       4049 );
    100          
    100          
774             }
775              
776             # simpleType
777              
778             sub makeList
779 28     28 0 56 { my ($self, $path, $st) = @_;
780 34     34   56 sub { my ($doc, $v) = @_;
781 34 50       69 defined $v or return undef;
782 34 100       78 join ' ', grep defined, map {$st->($doc, $_)}
  49         117  
783             ref $v eq 'ARRAY' ? @$v : $v;
784 28         100 };
785             }
786              
787             sub makeFacetsList
788 12     12 0 27 { my ($self, $path, $st, $info, $early, $late) = @_;
789 12         30 my @e = grep defined, @$early;
790 12         29 my @l = grep defined, @$late;
791 12     12   19 sub { my ($doc, $v) = @_;
792 12 50       25 defined $v or return undef;
793 12         31 $_->($v) for @l;
794 8 50       37 my $list = join ' ', map $st->($doc, $_), ref $v eq 'ARRAY' ? @$v : $v;
795 8 50 33     34 defined $list && length $list or return;
796 8         13 do { $list = $_->($list) } for @e;
  4         12  
797 7         14 $list;
798             }
799 12         56 }
800              
801             sub makeFacets
802 87     87 0 196 { my ($self, $path, $st, $info, $early, $late) = @_;
803 87 100 100     298 @$early || @$late or return $st;
804 74     74   128 sub { my ($doc, $v) = @_;
805 74 50       145 defined $v or return undef;
806 74         189 $v = $_->($v) for @$late;
807 54         227 $v = $st->($doc, $v);
808 53 50       156 defined $v or return undef;
809 53         108 $v = $_->($v) for @$early;
810 52         90 $v;
811 86         450 };
812             }
813              
814             sub makeUnion
815 18     18 0 47 { my ($self, $path, @types) = @_;
816 25     25   42 sub { my ($doc, $value) = @_;
817 25 50       48 defined $value or return undef;
818 25 100       43 for(@types) {my $v = try { $_->($doc, $value) }; $@ or return $v }
  37         210  
  37         9794  
  37         2981  
819              
820 2 50       16 substr $value, 20, -5, '...' if length($value) > 50;
821 2         5 error __x"no match for `{text}' in union at {path}"
822             , text => $value, path => $path;
823 18         63 };
824             }
825              
826             sub makeSubstgroup
827 8     8 0 39 { my ($self, $path, $type, %done) = @_;
828              
829 8 50   0   31 keys %done or return bless sub { () }, 'BLOCK';
  0         0  
830 8         22 my %do = map { @$_ } values %done;
  23         52  
831              
832             bless
833 10     10   18 sub { my ($doc, $values) = @_;
834             #warn "SUBST($type) AVAILABLE ARE ", join ', ', keys %do;
835 10         36 foreach my $take (keys %do)
836 19         33 { my $subst = delete $values->{$take};
837 19 100       37 defined $subst or next;
838              
839 10         25 return $do{$take}->($doc, $subst);
840             }
841 0         0 ();
842 8         64 }, 'BLOCK';
843             }
844              
845             # Attributes
846              
847             sub makeAttributeRequired
848 11     11 0 39 { my ($self, $path, $ns, $tag, $label, $do) = @_;
849              
850 10     10   25 sub { my $value = $do->(@_);
851 10 50       134 return $_[0]->createAttribute($tag, $value)
852             if defined $value;
853              
854 0         0 error __x"attribute `{tag}' is required at {path}"
855             , tag => $tag, path => $path;
856 11         44 };
857             }
858              
859             sub makeAttributeProhibited
860 3     3 0 11 { my ($self, $path, $ns, $tag, $label, $do) = @_;
861              
862 3     3   7 sub { my $value = $do->(@_);
863 3 100       9 defined $value or return ();
864              
865 1         5 error __x"attribute `{tag}' is prohibited at {path}"
866             , tag => $tag, path => $path;
867 3         44 };
868             }
869              
870             sub makeAttribute
871 82     82 0 209 { my ($self, $path, $ns, $tag, $label, $do) = @_;
872 80     80   169 sub { my $value = $do->(@_);
873 79 100       799 defined $value ? $_[0]->createAttribute($tag, $value) : ();
874 82         2090 };
875             }
876              
877             sub makeAttributeDefault
878 7     7 0 21 { my ($self, $path, $ns, $tag, $label, $do, $default_node) = @_;
879              
880 7         14 my $mode = $self->{default_values};
881             $mode eq 'IGNORE'
882             and return sub
883 3     3   6 { my $value = $do->(@_);
884 3 100       15 defined $value ? $_[0]->createAttribute($tag, $value) : ();
885 7 100       35 };
886              
887 4         20 my $default = $default_node->value;
888             $mode eq 'EXTEND'
889             and return sub
890 2     2   5 { my $value = $do->(@_);
891 2 100       5 defined $value or $value = $default;
892 2         15 $_[0]->createAttribute($tag, $value);
893 4 100       19 };
894              
895             $mode eq 'MINIMAL'
896             and return sub
897 2     2   6 { my $value = $do->(@_);
898 2 100 66     12 return () if defined $value && $value eq $default;
899 1         7 $_[0]->createAttribute($tag, $value);
900 2 50       13 };
901              
902 0         0 error __x"illegal default_values mode `{mode}'", mode => $mode;
903             }
904              
905             sub makeAttributeFixed
906 6     6 0 19 { my ($self, $path, $ns, $tag, $label, $do, $fixed) = @_;
907 6 50       50 $fixed = $fixed->value if ref $fixed;
908              
909 5     5   11 sub { my ($doc, $value) = @_;
910 5 100       11 defined $value or return ();
911              
912 4 100       13 $value eq $fixed
913             or error __x"value of attribute `{tag}' is fixed to `{fixed}', not `{got}' at {path}"
914             , tag => $tag, got => $value, fixed => $fixed, path => $path;
915              
916 3         24 $doc->createAttribute($tag, $fixed);
917 6         37 };
918             }
919              
920             # any
921              
922             sub _split_any_list($$$)
923 14     14   22 { my ($path, $type, $v) = @_;
924 14 50       37 my @nodes = ref $v eq 'ARRAY' ? @$v : defined $v ? $v : return ([], []);
    100          
925 14         16 my (@attrs, @elems);
926              
927 14         17 foreach my $node (@nodes)
928 14 50       31 { UNIVERSAL::isa($node, 'XML::LibXML::Node')
929             or error __x"elements for 'any' are XML::LibXML nodes, not {string} at {path}"
930             , string => $node, path => $path;
931              
932 14 100       34 if($node->isa('XML::LibXML::Attr'))
933 12         15 { push @attrs, $node;
934 12         17 next;
935             }
936              
937 2 50       6 if($node->isa('XML::LibXML::Element'))
938 2         3 { push @elems, $node;
939 2         3 next;
940             }
941              
942 0         0 error __x"an XML::LibXML::Element or ::Attr is expected as 'any' or 'anyAttribute value with {type}, but a {kind} was found at {path}"
943             , type => $type, kind => ref $node, path => $path;
944             }
945              
946 14         29 return (\@attrs, \@elems);
947             }
948              
949             sub makeAnyAttribute
950 5     5 0 11 { my ($self, $path, $handler, $yes, $no, $process) = @_;
951 5 100       6 my %yes = map +($_ => 1), @{$yes || []};
  5         20  
952 5 100       10 my %no = map +($_ => 1), @{$no || []};
  5         17  
953 5         6 my $prefs = $self->{prefixes};
954              
955 5         12 weaken $self;
956              
957             bless
958 5     5   10 sub { my ($doc, $values) = @_;
959              
960 5         7 my @res;
961 5         16 foreach my $label (sort keys %$values)
962 10         13 { my ($type, $ns, $local);
963 10 50       20 if(substr($label, 0, 1) eq '{')
    0          
964 10         17 { ($ns, $local) = unpack_type $label;
965 10         11 $type = $label;
966             }
967             elsif(index($label, ':') >= 0)
968 0         0 { (my $prefix, $local) = split ':', $label, 2;
969 0         0 my $match = first {$_->{prefix} eq $prefix} values %$prefs;
  0         0  
970 0 0       0 my $ns = $match ? $match->{uri} : undef;
971 0         0 $type = pack_type $ns, $local;
972             }
973 0         0 else {next} # not fully qualified, not an 'any'
974              
975 10 100 100     31 $yes{$ns} or next if keys %yes;
976 8 100 100     19 $no{$ns} and next if keys %no;
977              
978 6 50       18 my $value = delete $values->{$label} or next;
979 6         12 my ($attrs, $elems) = _split_any_list $path, $type, $value;
980              
981 6 50       14 $values->{$type} = $elems if @$elems;
982 6 50       12 @$attrs or next;
983              
984 6         10 foreach my $node (@$attrs)
985 6         12 { my $nodetype = type_of_node $node;
986 6 50       17 next if $nodetype eq $type;
987              
988 0         0 error __x"provided 'anyAttribute' node has type {type}, but labeled with {other} at {path}"
989             , type => $nodetype, other => $type, path => $path
990             }
991              
992 6         16 push @res, @$attrs;
993             }
994 5         12 @res;
995 5         47 }, 'ANY';
996             }
997              
998             sub makeAnyElement
999 5     5 0 13 { my ($self, $path, $handler, $yes, $no, $process, $min, $max) = @_;
1000 5 100       6 my %yes = map +($_ => 1), @{$yes || []};
  5         20  
1001 5 100       9 my %no = map +($_ => 1), @{$no || []};
  5         18  
1002 5         16 my $prefs = $self->{prefixes};
1003              
1004 5   100     14 $handler ||= 'SKIP_ALL';
1005 5         21 weaken $self;
1006              
1007             bless
1008 5     5   10 sub { my ($doc, $values) = @_;
1009 5         6 my @res;
1010              
1011 5         22 foreach my $label (sort keys %$values)
1012 17         33 { my ($type, $ns, $local);
1013 17 100       42 if(substr($label, 0, 1) eq '{')
    100          
1014 10         19 { ($ns, $local) = unpack_type $label;
1015 10         18 $type = $label;
1016             }
1017             elsif(index($label, ':') >= 0)
1018 2         6 { (my $prefix, $local) = split ':', $label, 2;
1019 2         12 my $match = first {$_->{prefix} eq $prefix} values %$prefs;
  3         6  
1020 2 50       8 $ns = $match ? $match->{uri} : undef;
1021 2         6 $type = pack_type $ns, $local;
1022             }
1023 5         7 else {next} # not fully qualified, not an 'any'
1024              
1025 12 100 100     29 $yes{$ns} or next if keys %yes;
1026 10 100 100     22 $no{$ns} and next if keys %no;
1027              
1028 8 50       25 my $value = delete $values->{$label} or next;
1029 8         41 my ($attrs, $elems) = _split_any_list $path, $type, $value;
1030              
1031 8 100       18 $values->{$type} = $attrs if @$attrs;
1032 8 100       24 @$elems or next;
1033              
1034 2         4 foreach my $node (@$elems)
1035 2         9 { my $nodens = $node->namespaceURI;
1036 2 50       4 defined $nodens or next; # see README.todo work-around
1037              
1038 2         5 my $nodetype = type_of_node $node;
1039 2 50       7 next if $nodetype eq $type;
1040              
1041 0         0 error __x"provided 'any' element node has type {type}, but labeled with {other} at {path}"
1042             , type => $nodetype, other => $type, path => $path
1043             }
1044              
1045 2         4 push @res, @$elems;
1046 2 50 33     7 $max eq 'unbounded' || @res <= $max
1047             or error __x"too many 'any' elements after consuming {count} nodes of {type}, max {max} at {path}"
1048             , count => scalar @$elems, type => $type
1049             , max => $max, path => $path;
1050             }
1051              
1052 5 50       19 @res >= $min
1053             or error __x"too few 'any' elements, got {count} for minimum {min} at {path}"
1054             , count => scalar @res, min => $min, path => $path;
1055              
1056 5 100       18 @res ? @res : undef; # empty, then "0 but true"
1057 5         61 }, 'ANY';
1058             }
1059              
1060             # xsi:type handling
1061              
1062             sub makeXsiTypeSwitch($$$$)
1063 3     3 0 8 { my ($self, $where, $elem, $default_type, $types) = @_;
1064 3         10 my $xsi = $self->_registerNSprefix(xsi => SCHEMA2001i, 1) . ':type';
1065 3         7 my %types;
1066 3         21 foreach my $type (sort keys %$types)
1067 6         12 { my ($ns, $local) = unpack_type $type;
1068 6         13 my $tag = $self->makeTagQualified($where, undef, $local, $ns);
1069              
1070             # register code under both prefixed and full type name
1071 6         21 $types{$self->prefixed($type)} = $types{$type} = [$tag,$types->{$type}];
1072             }
1073              
1074             sub {
1075 6     6   8 my ($doc, $values) = @_;
1076             ref $values eq 'HASH' && $values->{XSI_TYPE}
1077 6 50 33     27 or return $types{$default_type}[1]->(@_);
1078              
1079 6         22 my %v = %$values;
1080 6         13 my $type = delete $v{XSI_TYPE};
1081 6 50       13 my $def = $types{$type}
1082             or error __x"specified xsi:type list for `{default}' does not contain `{got}'"
1083             , default => $default_type, got => $type;
1084              
1085 6         12 my ($t, $code) = @$def;
1086 6 50       11 my $node = $code->($doc, \%v)
1087             or return;
1088 6         61 $node->setAttribute($xsi, $t);
1089 6         63 $node;
1090 3         46 };
1091             }
1092              
1093             sub makeHook($$$$$$$)
1094 9     9 0 26 { my ($self, $path, $r, $tag, $before, $replace, $after, $fulltype) = @_;
1095 9 50 100     30 return $r unless $before || $replace || $after;
      66        
1096              
1097 9         12 my $do_replace;
1098 9 100       17 if($replace)
1099 1 50   1   9 { return sub {()} if grep $_ eq 'SKIP', @$replace;
  1         2  
1100              
1101             # Input for replace is Perl, output is XML... so we cannot stack them
1102 0 0       0 error __x"writer only supports one replace hook (for {type})"
1103             , type => $fulltype
1104             if @$replace > 1;
1105              
1106 0         0 $do_replace = $self->_decodeReplace($path, $replace->[0]);
1107             }
1108              
1109 8 100       24 my @do_before = $before ? map $self->_decodeBefore($path,$_), @$before :();
1110 8 100       38 my @do_after = $after ? map $self->_decodeAfter($path,$_), @$after :();
1111              
1112             sub
1113 8     8   12 { my ($doc, $val) = @_;
1114 8 50       19 defined $val or return;
1115 8         12 foreach (@do_before)
1116 6         13 { $val = $_->($doc, $val, $path, $fulltype);
1117 6 50       5236 defined $val or return ();
1118             }
1119              
1120 8 50       24 my $xml = $do_replace
1121             ? $do_replace->($doc, $val, $path, $tag, $r, $fulltype)
1122             : $r->($doc, $val);
1123 8 50       59 defined $xml or return ();
1124              
1125 8         16 foreach (@do_after)
1126 5         11 { $xml = $_->($doc, $xml, $path, $val, $fulltype);
1127 5 50       22 defined $xml or return ();
1128             }
1129              
1130 8         15 $xml;
1131 8         43 };
1132             }
1133              
1134             sub _decodeBefore($$)
1135 6     6   11 { my ($self, $path, $call) = @_;
1136 6 50       22 return $call if ref $call eq 'CODE';
1137              
1138             # $code->($doc, $values, $path)
1139 0     0   0 $call eq 'PRINT_PATH' ? sub { print "$_[2]\n"; $_[1] }
  0         0  
1140 0 0       0 : error __x"labeled before hook `{name}' undefined for WRITER", name=>$call;
1141             }
1142              
1143             sub _decodeReplace($$)
1144 0     0   0 { my ($self, $path, $call) = @_;
1145 0 0       0 return $call if ref $call eq 'CODE';
1146              
1147             # SKIP already handled
1148             # $replace->($doc, $val, $path, $tag, $replaced)
1149 0         0 error __x"labeled replace hook `{name}' undefined for WRITER", name=>$call;
1150             }
1151              
1152             sub _decodeAfter($$)
1153 5     5   11 { my ($self, $path, $call) = @_;
1154 5 100       16 return $call if ref $call eq 'CODE';
1155              
1156 1     1   6 $call eq 'PRINT_PATH' ? sub { print "$_[2]\n"; $_[1] }
  1         2  
1157 1 50       7 : error __x"labeled after hook `{name}' undefined for WRITER", name=>$call;
1158             }
1159              
1160             sub makeBlocked($$$)
1161 19     19 0 40 { my ($self, $where, $class, $type) = @_;
1162 19         35 my $err_type = $self->prefixed($type);
1163              
1164             # errors are produced in class=misfit to allow other choices to succeed.
1165             $class eq 'anyType'
1166 4     4   15 ? { st => sub { error __x"use of `{type}' blocked at {where}"
1167             , type => $err_type, where => $where, _class => 'misfit';
1168             }}
1169             : $class eq 'simpleType'
1170 2     2   8 ? { st => sub { error __x"use of {class} `{type}' blocked at {where}"
1171             , class => $class, type => $err_type, where => $where
1172             , _class => 'misfit';
1173             }}
1174             : $class eq 'complexType'
1175             ? { elems => [] }
1176             : $class eq 'ref'
1177 0     0   0 ? { st => sub { error __x"use of referenced `{type}' blocked at {where}"
1178             , type => $err_type, where => $where, _class => 'misfit';
1179             }}
1180 19 50       174 : panic "blocking of $class for $type not implemented";
    100          
    100          
    100          
1181             }
1182              
1183             sub addTypeAttribute($$)
1184 1     1 0 3 { my ($self, $type, $do) = @_;
1185 1         9 my $xsi = $self->_registerNSprefix(xsi => SCHEMA2001i, 1) . ':type';
1186 1         2 my $xsd = $self->_registerNSprefix(xsd => SCHEMA2001, 1);
1187 1         4 my $typed = $self->prefixed($type);
1188              
1189             sub {
1190 1     1   2 my $r = $do->(@_);
1191 1 50 33     6 $type && $r && UNIVERSAL::isa($r, 'XML::LibXML::Element') or return $r;
      33        
1192 1 50       31 return $r if $r->getAttributeNS(SCHEMA2001i, 'type');
1193 1         18 $r->setAttribute($xsi, $typed);
1194 1         14 $r;
1195 1         11 };
1196             }
1197              
1198             #------------
1199              
1200             1;