File Coverage

lib/XML/Compile/SOAP.pm
Criterion Covered Total %
statement 245 357 68.6
branch 70 150 46.6
condition 30 75 40.0
subroutine 41 59 69.4
pod 10 14 71.4
total 396 655 60.4


line stmt bran cond sub pod time code
1             # Copyrights 2007-2021 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-SOAP. Meta-POD processed
6             # with 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::SOAP;
10 7     7   1319 use vars '$VERSION';
  7         12  
  7         343  
11             $VERSION = '3.27';
12              
13              
14 7     7   37 use warnings;
  7         12  
  7         169  
15 7     7   30 use strict;
  7         19  
  7         138  
16              
17 7     7   30 use Log::Report 'xml-compile-soap';
  7         11  
  7         58  
18              
19 7     7   4959 use XML::Compile ();
  7         179680  
  7         249  
20 7         415 use XML::Compile::Util qw(SCHEMA2001 SCHEMA2001i pack_type
21 7     7   55 unpack_type type_of_node);
  7         12  
22 7     7   3479 use XML::Compile::Cache ();
  7         778892  
  7         251  
23 7     7   2765 use XML::Compile::SOAP::Util qw/:xop10 SOAP11ENC/;
  7         19  
  7         1067  
24              
25 7     7   4088 use Time::HiRes qw/time/;
  7         10464  
  7         28  
26 7     7   1190 use MIME::Base64 qw/decode_base64/;
  7         13  
  7         476  
27              
28             # XML::Compile::WSA::Util often not installed
29 7     7   45 use constant WSA10 => 'http://www.w3.org/2005/08/addressing';
  7         20  
  7         3552  
30              
31 11     11   77 sub _xop_enabled() { exists $INC{'XML/Compile/XOP.pm'} }
32              
33              
34             sub new($@)
35 6     6 1 17 { my $class = shift;
36              
37 6 50       93 error __x"you can only instantiate sub-classes of {class}", class => $class
38             if $class eq __PACKAGE__;
39              
40 6         64 (bless {}, $class)->init( {@_} );
41             }
42              
43             sub init($)
44 6     6 0 19 { my ($self, $args) = @_;
45 6   50     62 $self->{XCS_mime} = $args->{media_type} || 'application/soap+xml';
46              
47             my $schemas = $self->{XCS_schemas} = $args->{schemas}
48 6   33     119 || XML::Compile::Cache->new(allow_undeclared => 1
49             , any_element => 'ATTEMPT', any_attribute => 'ATTEMPT');
50              
51 6 50       2661 UNIVERSAL::isa($schemas, 'XML::Compile::Cache')
52             or panic "schemas must be a Cache object";
53              
54 6         18 $self;
55             }
56              
57             sub _initSOAP($)
58 6     6   15 { my ($thing, $schemas) = @_;
59             return $thing
60 6 50       24 if $schemas->{did_init_SOAP}++; # ugly
61              
62 6         27 $schemas->addPrefixes(xsd => SCHEMA2001, xsi => SCHEMA2001i);
63              
64 6         690 $thing;
65             }
66              
67              
68             { my (%registered, %envelope);
69             sub register($)
70 7     7 1 32 { my ($class, $uri, $env, $opclass) = @_;
71 7         27 $registered{$uri} = $class;
72 7 50       67 $envelope{$env} = $opclass if $env;
73             }
74 0     0 0 0 sub plugin($) { $registered{$_[1]} }
75 0     0 0 0 sub fromEnvelope($) { $envelope{$_[1]} }
76 0     0 0 0 sub registered($) { values %registered }
77             }
78              
79             #--------------------
80              
81 0     0 1 0 sub version() {panic "not implemented"}
82 0     0 1 0 sub mediaType() {shift->{XCS_mime}}
83              
84              
85             sub schemas() {
86 7     7   52 use Carp 'cluck';
  7         13  
  7         30030  
87 193 50   193 1 5581 ref $_[0] or cluck;
88 193         782 shift->{XCS_schemas}}
89              
90             #--------------------
91              
92             sub compileMessage($@)
93 11     11 1 43 { my ($self, $direction, %args) = @_;
94 11   50     35 $args{style} ||= 'document';
95              
96 11 50       121 $direction eq 'SENDER' ? $self->_sender(%args)
    100          
97             : $direction eq 'RECEIVER' ? $self->_receiver(%args)
98             : error __x"message direction is 'SENDER' or 'RECEIVER', not `{dir}'"
99             , dir => $direction;
100             }
101              
102              
103             sub messageStructure($)
104 1     1 1 1487 { my ($thing, $xml) = @_;
105 1 50       11 my $env = $xml->isa('XML::LibXML::Document') ? $xml->documentElement :$xml;
106              
107 1         3 my (@header, @body, $wsa_action);
108 1 50       4 if(my ($header) = $env->getChildrenByLocalName('Header'))
109 1 100       20 { @header = map { $_->isa('XML::LibXML::Element') ? type_of_node($_) : ()}
  3         41  
110             $header->childNodes;
111              
112 1 50       4 if(my $wsa = ($header->getChildrenByTagNameNS(WSA10, 'Action'))[0])
113 0         0 { $wsa_action = $wsa->textContent;
114 0         0 for($wsa_action) { s/^\s+//; s/\s+$//; s/\s{2,}/ /g }
  0         0  
  0         0  
  0         0  
115             }
116             }
117              
118 1 50       30 if(my ($body) = $env->getChildrenByLocalName('Body'))
119 1 100       9 { @body = map { $_->isa('XML::LibXML::Element') ? type_of_node($_) : () }
  3         26  
120             $body->childNodes;
121             }
122              
123 1         3 +{ header => \@header
124             , body => \@body
125             , wsa_action => $wsa_action
126             };
127             }
128              
129             #------------------------------------------------
130             # Sender
131              
132             sub _sender(@)
133 5     5   26 { my ($self, %args) = @_;
134              
135 5 50       22 error __"option 'role' only for readers" if $args{role};
136 5 50       21 error __"option 'roles' only for readers" if $args{roles};
137              
138             my $hooks = $args{hooks} # make copy of calling hook-list
139 5 50       24 = $args{hooks} ? [ @{$args{hooks}} ] : [];
  0         0  
140              
141 5         22 my @mtom;
142 5 100       21 push @$hooks, $self->_writer_xop_hook(\@mtom)
143             if _xop_enabled;
144              
145             my ($body, $blabels) = $args{create_body}
146 5 50       66 ? $args{create_body}->($self, %args)
147             : $self->_writer_body(\%args);
148 5         56 my ($faults, $flabels) = $self->_writer_faults(\%args, $args{faults});
149              
150 5         64 my ($header, $hlabels) = $self->_writer_header(\%args);
151 5         40 push @$hooks, $self->_writer_hook($self->envType('Header'), @$header);
152              
153 5   50     23 my $style = $args{style} || 'none';
154 5 50       23 if($style eq 'document')
    0          
155 5         22 { push @$hooks, $self->_writer_hook($self->envType('Body')
156             , @$body, @$faults);
157             }
158             elsif($style eq 'rpc')
159             { my $procedure = $args{procedure} || $args{body}{procedure}
160 0 0 0     0 or error __x"sending operation requires procedure name with RPC";
161              
162 0   0     0 my $use = $args{use} || $args{body}{use} || 'literal';
163 0         0 my $bt = $self->envType('Body');
164 0 0       0 push @$hooks, $use eq 'literal'
165             ? $self->_writer_body_rpclit_hook($bt, $procedure, $body, $faults)
166             : $self->_writer_body_rpcenc_hook($bt, $procedure, $body, $faults);
167             }
168             else
169 0         0 { error __x"unknown style `{style}'", style => $style;
170             }
171              
172             #
173             # Pack everything together in one procedure
174             #
175              
176 5         22 my $envelope = $self->_writer($self->envType('Envelope'), %args);
177              
178             sub
179 10 100   10   8971 { my ($values, $charset) = ref $_[0] eq 'HASH' ? @_ : ( {@_}, undef);
180 10         49 my %copy = %$values; # do not destroy the calling hash
181             my $doc = delete $copy{_doc}
182 10   33     461 || XML::LibXML::Document->new('1.0', $charset || 'UTF-8');
183              
184             my %data = (
185             Body => delete $copy{Body} || {},
186             Header => delete $copy{Header},
187 10   50     97 );
188              
189 10         32 foreach my $label (@$hlabels)
190 4 100       14 { exists $copy{$label} or next;
191 3   33     20 $data{Header}{$label} ||= delete $copy{$label};
192             }
193              
194 10         30 foreach my $label (@$blabels, @$flabels)
195 20 100       49 { exists $copy{$label} or next;
196 8   33     62 $data{Body}{$label} ||= delete $copy{$label};
197             }
198              
199 10 50 66     53 if(@$blabels==2 && !keys %{$data{Body}} ) # ignore 'Fault'
  7 50       57  
200             { # even when no params, we fill at least one body element
201 0         0 $data{Body}{$blabels->[0]} = \%copy;
202             }
203             elsif(keys %copy)
204 0         0 { trace __x"available blocks: {blocks}",
205             blocks => [ sort @$hlabels, @$blabels, @$flabels ];
206 0         0 error __x"call data not used: {blocks}", blocks => [keys %copy];
207             }
208              
209 10         25 @mtom = (); # filled via hook
210              
211             #use Data::Dumper;
212             #warn "REPROCESSED: ", Dumper \%data;
213 10 50       45 my $root = $envelope->($doc, \%data)
214             or return;
215              
216 10         1242 $doc->setDocumentElement($root);
217              
218 10 100       150 return ($doc, \@mtom)
219             if wantarray;
220              
221 8 50       34 @mtom == 0
222             or error __x"{nr} XOP objects lost in sender"
223             , nr => scalar @mtom;
224 8         23 $doc;
225 5         28763 };
226             }
227              
228             sub _writer_hook($$@)
229 10     10   103 { my ($self, $type, @do) = @_;
230              
231             my $code = sub
232 13     13   805 { my ($doc, $data, $path, $tag) = @_;
233 13 50       44 UNIVERSAL::isa($data, 'XML::LibXML::Element')
234             and return $data;
235              
236 13         46 my %data = %$data;
237 13         46 my @h = @do;
238 13         29 my @childs;
239 13         34 while(@h)
240 23         787 { my ($k, $c) = (shift @h, shift @h);
241 23 100       85 if(my $v = delete $data{$k})
242 11         45 { push @childs, $c->($doc, $v);
243             }
244             }
245              
246 13 50       1477 if(keys %data)
247 0         0 { warning __x"unused values {names}", names => [keys %data];
248 0         0 my @h = @do; my @keys;
  0         0  
249 0         0 while(@h) { push @keys, shift @h; shift @h}
  0         0  
  0         0  
250 0         0 trace "expected: ". join ' ', @keys;
251             }
252              
253 13         63 my $node = $doc->createElement($tag);
254 13         111 $node->appendChild($_) for @childs;
255 13         148 $node;
256 10         88 };
257              
258 10         40 +{ type => $type, replace => $code };
259             }
260              
261             sub _writer_body_rpclit_hook($$$$$)
262 0     0   0 { my ($self, $type, $procedure, $params, $faults) = @_;
263 0         0 my @params = @$params;
264 0         0 my @faults = @$faults;
265 0         0 my $schemas = $self->schemas;
266              
267 0         0 my $proc = $schemas->prefixed($procedure);
268 0         0 my ($prefix) = split /\:/, $proc;
269 0         0 my $prefdef = $schemas->prefix($prefix);
270 0         0 my $proc_ns = $prefdef->{uri};
271 0         0 $prefdef->{used} = 0;
272              
273             my $code = sub
274 0     0   0 { my ($doc, $data, $path, $tag) = @_;
275 0 0       0 UNIVERSAL::isa($data, 'XML::LibXML::Element')
276             and return $data;
277              
278 0         0 my %data = %$data;
279 0         0 my @f = @faults;
280 0         0 my (@fchilds, @pchilds);
281 0         0 while(@f)
282 0         0 { my ($k, $c) = (shift @f, shift @f);
283 0         0 my $v = delete $data{$k};
284 0 0       0 push @fchilds, $c->($doc, $v) if defined $v;
285             }
286 0         0 my @p = @params;
287 0         0 while(@p)
288 0         0 { my ($k, $c) = (shift @p, shift @p);
289 0         0 my $v = delete $data{$k};
290 0 0       0 push @pchilds, $c->($doc, $v) if defined $v;
291             }
292 0 0       0 warning __x"unused values {names}", names => [keys %data]
293             if keys %data;
294              
295 0         0 my $proc = $doc->createElement($proc);
296 0         0 $proc->setNamespace($proc_ns, $prefix, 0);
297 0         0 $proc->setAttribute("SOAP-ENV:encodingStyle", SOAP11ENC);
298              
299 0         0 $proc->appendChild($_) for @pchilds;
300              
301 0         0 my $node = $doc->createElement($tag);
302 0         0 $node->appendChild($proc);
303 0         0 $node->appendChild($_) for @fchilds;
304 0         0 $node;
305 0         0 };
306              
307 0         0 +{ type => $type, replace => $code };
308             }
309              
310             *_writer_body_rpcenc_hook = \&_writer_body_rpclit_hook;
311              
312             sub _writer_header($)
313 5     5   14 { my ($self, $args) = @_;
314 5         46 my (@rules, @hlabels);
315              
316 5   100     34 my $header = $args->{header} || [];
317 5         28 my $soapenv = $self->envelopeNS;
318              
319 5 50       119 foreach my $h (ref $header eq 'ARRAY' ? @$header : $header)
320 2         9 { my $part = $h->{parts}[0];
321 2         6 my $label = $part->{name};
322 2         5 my $code = $part->{writer};
323 2 50       9 if($part->{element})
    0          
324 2   33     57 { $code ||= $self->_writer_part_element($args, $part);
325             }
326             elsif(my $type = $part->{type})
327 0   0     0 { $code ||= $self->_writer_part_type($args, $part);
328 0         0 $label = (unpack_type $part->{name})[1];
329             }
330             else
331 0         0 { error __x"header part {name} has neither `element' nor `type'"
332             , name => $label;
333             }
334              
335 2         53 push @rules, $label => $code;
336 2         7 push @hlabels, $label;
337             }
338              
339 5         23 (\@rules, \@hlabels);
340             }
341              
342             sub _writer_body($)
343 5     5   16 { my ($self, $args) = @_;
344 5         21 my (@rules, @blabels);
345              
346 5   33     20 my $body = $args->{body} || $args->{fault};
347 5   100     31 my $use = $body->{use} || 'literal';
348             # $use eq 'literal'
349             # or error __x"RPC encoded not supported by this version";
350              
351 5   50     21 my $parts = $body->{parts} || [];
352 5         12 my $style = $args->{style};
353 5   33     24 local $args->{is_rpc_enc} = $style eq 'rpc' && $use eq 'encoded';
354              
355 5         17 foreach my $part (@$parts)
356 9         21 { my $label = $part->{name};
357 9         12 my $code;
358 9 50       25 if($part->{element})
    0          
359 9         61 { $code = $self->_writer_part_element($args, $part);
360             }
361             elsif(my $type = $part->{type})
362 0         0 { $code = $self->_writer_part_type($args, $part);
363 0         0 $label = (unpack_type $part->{name})[1];
364             }
365             else
366 0         0 { error __x"body part {name} has neither `element' nor `type'"
367             , name => $label;
368             }
369              
370 9         140 push @rules, $label => $code;
371 9         25 push @blabels, $label;
372             }
373              
374 5         28 (\@rules, \@blabels);
375             }
376              
377             sub _writer_part_element($$)
378 11     11   30 { my ($self, $args, $part) = @_;
379 11         24 my $element = $part->{element};
380 11         64 my $soapenv = $self->envelopeNS;
381              
382             $part->{writer} ||= $self->_writer
383             ( $element, %$args
384 30 100   30   10964 , include_namespaces => sub {$_[0] ne $soapenv && $_[2]}
385             , xsi_type_everywhere => $args->{is_rpc_enc}
386 11   66     136 );
387             }
388              
389             sub _writer_part_type($$)
390 0     0   0 { my ($self, $args, $part) = @_;
391              
392             $args->{style} eq 'rpc'
393             or error __x"part {name} uses `type', only for rpc not {style}"
394 0 0       0 , name => $part->{name}, style => $args->{style};
395              
396             return $part->{writer}
397 0 0       0 if $part->{writer};
398              
399 0         0 my $soapenv = $self->envelopeNS;
400              
401             $part->{writer} = $self->schemas->compileType
402             ( WRITER => $part->{type}, %$args, element => $part->{name}
403 0 0   0   0 , include_namespaces => sub {$_[0] ne $soapenv && $_[2]}
404             , xsi_type_everywhere => $args->{is_rpc_enc}
405 0         0 );
406             }
407              
408 0     0   0 sub _writer_faults($) { ([], []) }
409              
410             sub _writer_xop_hook($)
411 1     1   5 { my ($self, $xop_objects) = @_;
412              
413             my $collect_objects = sub {
414 4     4   233 my ($doc, $val, $path, $tag, $r) = @_;
415 4 100       25 return $r->($doc, $val)
416             unless UNIVERSAL::isa($val, 'XML::Compile::XOP::Include');
417              
418 1         5 my $node = $val->xmlNode($doc, $path, $tag);
419 1         18 push @$xop_objects, $val;
420 1         2 $node;
421 1         7 };
422              
423 1         5 +{ extends => 'xsd:base64Binary', replace => $collect_objects };
424             }
425              
426             #------------------------------------------------
427             # Receiver
428              
429             sub _receiver(@)
430 6     6   26 { my ($self, %args) = @_;
431              
432             error __"option 'destination' only for writers"
433 6 50       26 if $args{destination};
434              
435             error __"option 'mustUnderstand' only for writers"
436 6 50       21 if $args{understand};
437              
438             # roles are not checked (yet)
439             # my $roles = $args{roles} || $args{role} || 'ULTIMATE';
440             # my @roles = ref $roles eq 'ARRAY' ? @$roles : $roles;
441              
442 6         48 my $header = $self->_reader_header(\%args);
443              
444 6         13 my $xops; # forward backwards pass-on
445 6         61 my $body = $self->_reader_body(\%args, \$xops);
446              
447 6   50     28 my $style = $args{style} || 'document';
448 6   50     40 my $kind = $args{kind} || 'request-response';
449 6 50       37 if($style eq 'rpc')
    50          
450 0   0     0 { my $procedure = $args{procedure} || $args{body}{procedure};
451 0 0 0     0 keys %{$args{body}}==0 || $procedure
  0         0  
452             or error __x"receiving operation requires procedure name with RPC";
453              
454 0   0     0 my $use = $args{use} || $args{body}{use} || 'literal';
455 0 0       0 $body = $use eq 'literal'
456             ? $self->_reader_body_rpclit_wrapper($procedure, $body)
457             : $self->_reader_body_rpcenc_wrapper($procedure, $body);
458             }
459             elsif($style ne 'document')
460 0         0 { error __x"unknown style `{style}'", style => $style;
461             }
462              
463             # faults are always possible
464 6         64 push @$body, $self->_reader_fault_reader;
465              
466 6 50       29853 my @hooks = @{$self->{hooks} || []};
  6         115  
467 6         48 push @hooks
468             , $self->_reader_hook($self->envType('Header'), $header)
469             , $self->_reader_hook($self->envType('Body'), $body );
470              
471             #
472             # Pack everything together in one procedure
473             #
474              
475 6         29 my $envelope = $self->_reader($self->envType('Envelope')
476             , %args, hooks => \@hooks);
477              
478             # add simplified fault information
479 6         32462 my $faultdec = $self->_reader_faults(\%args, $args{faults});
480              
481             sub
482 9     9   8288 { (my $xml, $xops) = @_;
483 9         43 my $data = $envelope->($xml);
484 9 100       62 my @pairs = ( %{delete $data->{Header} || {}}
485 9 50       1443 , %{delete $data->{Body} || {}});
  9         51  
486 9         36 while(@pairs)
487 10         21 { my $k = shift @pairs;
488 10         34 $data->{$k} = shift @pairs;
489             }
490              
491 9         40 $faultdec->($data);
492 9         84 $data;
493 6         95 };
494             }
495              
496             sub _reader_hook($$)
497 12     12   164 { my ($self, $type, $do) = @_;
498 12         58 my %trans = map +($_->[1] => [ $_->[0], $_->[2] ]), @$do; # we need copies
499 12         44 my $envns = $self->envelopeNS;
500              
501             my $code = sub
502 12     12   5165 { my ($xml, $trans, $path, $label) = @_;
503 12         22 my %h;
504 12         40 foreach my $child ($xml->childNodes)
505 23 100       163 { next unless $child->isa('XML::LibXML::Element');
506 10         47 my $type = type_of_node $child;
507 10 100       176 if(my $t = $trans{$type})
508 9         26 { my ($label, $code) = @$t;
509 9 50       34 my $v = $code->($child) or next;
510 9 50       1881 if(!defined $v) { }
    50          
    0          
511 9         31 elsif(!exists $h{$label}) { $h{$label} = $v }
512 0         0 elsif(ref $h{$label} eq 'ARRAY') { push @{$h{$label}}, $v }
  0         0  
513 0         0 else { $h{$label} = [ $h{$label}, $v ] }
514 9         30 next;
515             }
516             else
517 1         6 { $h{$type} = $child;
518 1         9 trace __x"node {type} not understood, expected are {has}",
519             type => $type, has => [sort keys %trans];
520             }
521              
522 1 50 50     108 return ($label => $self->replyMustUnderstandFault($type))
523             if $child->getAttributeNS($envns, 'mustUnderstand') || 0;
524             }
525 11         54 ($label => \%h);
526 12         59 };
527              
528 12         51 +{ type => $type
529             , replace => $code
530             };
531            
532             }
533              
534             sub _reader_body_rpclit_wrapper($$)
535 0     0   0 { my ($self, $procedure, $body) = @_;
536 0         0 my %trans = map +($_->[1] => [ $_->[0], $_->[2] ]), @$body;
537              
538             # this should use key_rewrite, but there is no $wsdl here
539             # my $label = $wsdl->prefixed($procedure);
540 0         0 my $label = (unpack_type $procedure)[1];
541              
542             my $code = sub
543 0 0   0   0 { my $xml = shift or return {};
544 0         0 my %h;
545 0         0 foreach my $child ($xml->childNodes)
546 0 0       0 { $child->isa('XML::LibXML::Element') or next;
547 0         0 my $type = type_of_node $child;
548 0 0       0 if(my $t = $trans{$type})
549 0         0 { $h{$t->[0]} = $t->[1]->($child) }
550 0         0 else { $h{$type} = $child }
551             }
552 0         0 \%h;
553 0         0 };
554              
555 0         0 [ [ $label => $procedure => $code ] ];
556             }
557              
558             sub _reader_header($)
559 6     6   16 { my ($self, $args) = @_;
560 6   100     34 my $header = $args->{header} || [];
561 6         13 my @rules;
562              
563 6         17 foreach my $h (@$header)
564 2         5 { my $part = $h->{parts}[0];
565 2         4 my $label = $part->{name};
566 2         5 my $element = $part->{element};
567 2   33     21 my $code = $part->{reader} ||= $self->_reader($element, %$args);
568 2         25956 push @rules, [$label, $element, $code];
569             }
570              
571 6         21 \@rules;
572             }
573              
574             sub _reader_body($$)
575 6     6   17 { my ($self, $args, $refxops) = @_;
576 6         15 my $body = $args->{body};
577 6   100     42 my $parts = $body->{parts} || [];
578 6 50       13 my @hooks = @{$args->{hooks} || []};
  6         35  
579 6 100       28 push @hooks, $self->_reader_xop_hook($refxops)
580             if _xop_enabled;
581              
582 6         20 local $args->{hooks} = \@hooks;
583              
584 6         11 my @rules;
585 6         16 foreach my $part (@$parts)
586 4         9 { my $label = $part->{name};
587              
588 4         7 my ($t, $code);
589 4 50       11 if($part->{element})
    0          
590 4         29 { ($t, $code) = $self->_reader_body_element($args, $part) }
591             elsif($part->{type})
592 0         0 { ($t, $code) = $self->_reader_body_type($args, $part) }
593             else
594 0         0 { error __x"part {name} has neither element nor type specified"
595             , name => $label;
596             }
597 4         22 push @rules, [ $label, $t, $code ];
598             }
599              
600             #use Data::Dumper;
601             #warn "RULES=", Dumper \@rules, $parts;
602 6         23 \@rules;
603             }
604              
605             sub _reader_body_element($$)
606 4     4   10 { my ($self, $args, $part) = @_;
607              
608 4         9 my $element = $part->{element};
609 4   33     52 my $code = $part->{reader} || $self->_reader($element, %$args);
610              
611 4         41319 ($element, $code);
612             }
613              
614             sub _reader_body_type($$)
615 0     0   0 { my ($self, $args, $part) = @_;
616 0         0 my $name = $part->{name};
617              
618 0 0       0 $args->{style} eq 'rpc'
619             or error __x"only rpc style messages can use 'type' as used by {part}"
620             , part => $name;
621              
622             return $part->{reader}
623 0 0       0 if $part->{reader};
624              
625 0         0 my $type = $part->{type};
626 0         0 my ($ns, $local) = unpack_type $type;
627              
628             my $r = $part->{reader} =
629 0         0 $self->schemas->compileType
630             ( READER => $type, %$args
631             , element => $name # $args->{body}{procedure}
632             );
633              
634 0         0 ($name, $r);
635             }
636              
637             sub _reader_faults($)
638 0     0   0 { my ($self, $args) = @_;
639 0     0   0 sub { shift };
  0         0  
640             }
641              
642             sub _reader_xop_hook($)
643 1     1   3 { my ($self, $refxops) = @_;
644              
645             my $xop_merge = sub
646 4     4   783 { my ($xml, $args, $path, $type, $r) = @_;
647 4 100       18 if(my $incls = $xml->getElementsByTagNameNS(XOP10, 'Include'))
648 1 50 50     156 { my $href = $incls->shift->getAttribute('href') || ''
649             or return ($type => $xml);
650              
651 1         40 $href =~ s/^cid://;
652 1 50       35 my $xop = $$refxops->{$href}
653             or return ($type => $xml);
654              
655 1         9 return ($type => $xop);
656             }
657              
658 3         412 ($type => decode_base64 $xml->textContent);
659 1         6 };
660              
661 1         4 +{ type => 'xsd:base64Binary', replace => $xop_merge };
662             }
663              
664 12     12   96 sub _reader(@) { shift->schemas->reader(@_) }
665 17     17   93 sub _writer(@) { shift->schemas->writer(@_) }
666              
667             #------------------------------------------------
668              
669              
670 0     0 1   sub roleURI($) { panic "not implemented" }
671              
672              
673 0     0 1   sub roleAbbreviation($) { panic "not implemented" }
674              
675              
676 0     0 1   sub replyMustUnderstandFault($) { panic "not implemented" }
677              
678             #----------------------
679              
680              
681             1;