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-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-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   1280 use vars '$VERSION';
  7         16  
  7         380  
11             $VERSION = '3.26';
12              
13              
14 7     7   39 use warnings;
  7         14  
  7         185  
15 7     7   30 use strict;
  7         33  
  7         161  
16              
17 7     7   36 use Log::Report 'xml-compile-soap';
  7         27  
  7         51  
18              
19 7     7   5160 use XML::Compile ();
  7         195352  
  7         255  
20 7         474 use XML::Compile::Util qw(SCHEMA2001 SCHEMA2001i pack_type
21 7     7   55 unpack_type type_of_node);
  7         14  
22 7     7   3558 use XML::Compile::Cache ();
  7         840802  
  7         243  
23 7     7   2642 use XML::Compile::SOAP::Util qw/:xop10 SOAP11ENC/;
  7         18  
  7         1023  
24              
25 7     7   4057 use Time::HiRes qw/time/;
  7         10892  
  7         36  
26 7     7   1187 use MIME::Base64 qw/decode_base64/;
  7         15  
  7         428  
27              
28             # XML::Compile::WSA::Util often not installed
29 7     7   40 use constant WSA10 => 'http://www.w3.org/2005/08/addressing';
  7         9  
  7         3786  
30              
31 11     11   58 sub _xop_enabled() { exists $INC{'XML/Compile/XOP.pm'} }
32              
33              
34             sub new($@)
35 6     6 1 14 { my $class = shift;
36              
37 6 50       24 error __x"you can only instantiate sub-classes of {class}", class => $class
38             if $class eq __PACKAGE__;
39              
40 6         52 (bless {}, $class)->init( {@_} );
41             }
42              
43             sub init($)
44 6     6 0 20 { my ($self, $args) = @_;
45 6   50     131 $self->{XCS_mime} = $args->{media_type} || 'application/soap+xml';
46              
47             my $schemas = $self->{XCS_schemas} = $args->{schemas}
48 6   33     107 || XML::Compile::Cache->new(allow_undeclared => 1
49             , any_element => 'ATTEMPT', any_attribute => 'ATTEMPT');
50              
51 6 50       2685 UNIVERSAL::isa($schemas, 'XML::Compile::Cache')
52             or panic "schemas must be a Cache object";
53              
54 6         15 $self;
55             }
56              
57             sub _initSOAP($)
58 6     6   17 { my ($thing, $schemas) = @_;
59             return $thing
60 6 50       24 if $schemas->{did_init_SOAP}++; # ugly
61              
62 6         29 $schemas->addPrefixes(xsd => SCHEMA2001, xsi => SCHEMA2001i);
63              
64 6         676 $thing;
65             }
66              
67              
68             { my (%registered, %envelope);
69             sub register($)
70 7     7 1 38 { my ($class, $uri, $env, $opclass) = @_;
71 7         22 $registered{$uri} = $class;
72 7 50       56 $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   54 use Carp 'cluck';
  7         13  
  7         32024  
87 193 50   193 1 6023 ref $_[0] or cluck;
88 193         720 shift->{XCS_schemas}}
89              
90             #--------------------
91              
92             sub compileMessage($@)
93 11     11 1 44 { my ($self, $direction, %args) = @_;
94 11   50     35 $args{style} ||= 'document';
95              
96 11 50       106 $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 1697 { my ($thing, $xml) = @_;
105 1 50       13 my $env = $xml->isa('XML::LibXML::Document') ? $xml->documentElement :$xml;
106              
107 1         3 my (@header, @body, $wsa_action);
108 1 50       5 if(my ($header) = $env->getChildrenByLocalName('Header'))
109 1 100       47 { @header = map { $_->isa('XML::LibXML::Element') ? type_of_node($_) : ()}
  3         48  
110             $header->childNodes;
111              
112 1 50       5 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       12 { @body = map { $_->isa('XML::LibXML::Element') ? type_of_node($_) : () }
  3         31  
120             $body->childNodes;
121             }
122              
123 1         4 +{ header => \@header
124             , body => \@body
125             , wsa_action => $wsa_action
126             };
127             }
128              
129             #------------------------------------------------
130             # Sender
131              
132             sub _sender(@)
133 5     5   30 { my ($self, %args) = @_;
134              
135 5 50       21 error __"option 'role' only for readers" if $args{role};
136 5 50       20 error __"option 'roles' only for readers" if $args{roles};
137              
138             my $hooks = $args{hooks} # make copy of calling hook-list
139 5 50       21 = $args{hooks} ? [ @{$args{hooks}} ] : [];
  0         0  
140              
141 5         11 my @mtom;
142 5 100       28 push @$hooks, $self->_writer_xop_hook(\@mtom)
143             if _xop_enabled;
144              
145             my ($body, $blabels) = $args{create_body}
146 5 50       73 ? $args{create_body}->($self, %args)
147             : $self->_writer_body(\%args);
148 5         47 my ($faults, $flabels) = $self->_writer_faults(\%args, $args{faults});
149              
150 5         54 my ($header, $hlabels) = $self->_writer_header(\%args);
151 5         37 push @$hooks, $self->_writer_hook($self->envType('Header'), @$header);
152              
153 5   50     25 my $style = $args{style} || 'none';
154 5 50       21 if($style eq 'document')
    0          
155 5         28 { 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         20 my $envelope = $self->_writer($self->envType('Envelope'), %args);
177              
178             sub
179 10 100   10   9104 { my ($values, $charset) = ref $_[0] eq 'HASH' ? @_ : ( {@_}, undef);
180 10         46 my %copy = %$values; # do not destroy the calling hash
181             my $doc = delete $copy{_doc}
182 10   33     413 || 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     114 );
188              
189 10         37 foreach my $label (@$hlabels)
190 4 100       11 { exists $copy{$label} or next;
191 3   33     18 $data{Header}{$label} ||= delete $copy{$label};
192             }
193              
194 10         32 foreach my $label (@$blabels, @$flabels)
195 20 100       56 { exists $copy{$label} or next;
196 8   33     54 $data{Body}{$label} ||= delete $copy{$label};
197             }
198              
199 10 50 66     43 if(@$blabels==2 && !keys %{$data{Body}} ) # ignore 'Fault'
  7 50       61  
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         26 @mtom = (); # filled via hook
210              
211             #use Data::Dumper;
212             #warn "REPROCESSED: ", Dumper \%data;
213 10 50       39 my $root = $envelope->($doc, \%data)
214             or return;
215              
216 10         1343 $doc->setDocumentElement($root);
217              
218 10 100       135 return ($doc, \@mtom)
219             if wantarray;
220              
221 8 50       35 @mtom == 0
222             or error __x"{nr} XOP objects lost in sender"
223             , nr => scalar @mtom;
224 8         37 $doc;
225 5         30094 };
226             }
227              
228             sub _writer_hook($$@)
229 10     10   95 { my ($self, $type, @do) = @_;
230              
231             my $code = sub
232 13     13   816 { my ($doc, $data, $path, $tag) = @_;
233 13 50       48 UNIVERSAL::isa($data, 'XML::LibXML::Element')
234             and return $data;
235              
236 13         48 my %data = %$data;
237 13         35 my @h = @do;
238 13         18 my @childs;
239 13         36 while(@h)
240 23         1002 { my ($k, $c) = (shift @h, shift @h);
241 23 100       74 if(my $v = delete $data{$k})
242 11         37 { push @childs, $c->($doc, $v);
243             }
244             }
245              
246 13 50       873 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         67 my $node = $doc->createElement($tag);
254 13         113 $node->appendChild($_) for @childs;
255 13         211 $node;
256 10         73 };
257              
258 10         80 +{ 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   17 { my ($self, $args) = @_;
314 5         52 my (@rules, @hlabels);
315              
316 5   100     34 my $header = $args->{header} || [];
317 5         21 my $soapenv = $self->envelopeNS;
318              
319 5 50       114 foreach my $h (ref $header eq 'ARRAY' ? @$header : $header)
320 2         8 { my $part = $h->{parts}[0];
321 2         6 my $label = $part->{name};
322 2         5 my $code = $part->{writer};
323 2 50       8 if($part->{element})
    0          
324 2   33     69 { $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         55 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   17 { my ($self, $args) = @_;
344 5         22 my (@rules, @blabels);
345              
346 5   33     22 my $body = $args->{body} || $args->{fault};
347 5   100     24 my $use = $body->{use} || 'literal';
348             # $use eq 'literal'
349             # or error __x"RPC encoded not supported by this version";
350              
351 5   50     18 my $parts = $body->{parts} || [];
352 5         9 my $style = $args->{style};
353 5   33     35 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       24 if($part->{element})
    0          
359 9         44 { $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         122 push @rules, $label => $code;
371 9         22 push @blabels, $label;
372             }
373              
374 5         27 (\@rules, \@blabels);
375             }
376              
377             sub _writer_part_element($$)
378 11     11   29 { my ($self, $args, $part) = @_;
379 11         24 my $element = $part->{element};
380 11         58 my $soapenv = $self->envelopeNS;
381              
382             $part->{writer} ||= $self->_writer
383             ( $element, %$args
384 30 100   30   11518 , include_namespaces => sub {$_[0] ne $soapenv && $_[2]}
385             , xsi_type_everywhere => $args->{is_rpc_enc}
386 11   66     114 );
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   3 { my ($self, $xop_objects) = @_;
412              
413             my $collect_objects = sub {
414 4     4   252 my ($doc, $val, $path, $tag, $r) = @_;
415 4 100       27 return $r->($doc, $val)
416             unless UNIVERSAL::isa($val, 'XML::Compile::XOP::Include');
417              
418 1         7 my $node = $val->xmlNode($doc, $path, $tag);
419 1         14 push @$xop_objects, $val;
420 1         51 $node;
421 1         5 };
422              
423 1         5 +{ extends => 'xsd:base64Binary', replace => $collect_objects };
424             }
425              
426             #------------------------------------------------
427             # Receiver
428              
429             sub _receiver(@)
430 6     6   23 { my ($self, %args) = @_;
431              
432             error __"option 'destination' only for writers"
433 6 50       27 if $args{destination};
434              
435             error __"option 'mustUnderstand' only for writers"
436 6 50       22 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         56 my $header = $self->_reader_header(\%args);
443              
444 6         13 my $xops; # forward backwards pass-on
445 6         54 my $body = $self->_reader_body(\%args, \$xops);
446              
447 6   50     28 my $style = $args{style} || 'document';
448 6   50     39 my $kind = $args{kind} || 'request-response';
449 6 50       34 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         83 push @$body, $self->_reader_fault_reader;
465              
466 6 50       31336 my @hooks = @{$self->{hooks} || []};
  6         60  
467 6         36 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         28 my $envelope = $self->_reader($self->envType('Envelope')
476             , %args, hooks => \@hooks);
477              
478             # add simplified fault information
479 6         32643 my $faultdec = $self->_reader_faults(\%args, $args{faults});
480              
481             sub
482 9     9   7529 { (my $xml, $xops) = @_;
483 9         37 my $data = $envelope->($xml);
484 9 100       61 my @pairs = ( %{delete $data->{Header} || {}}
485 9 50       1097 , %{delete $data->{Body} || {}});
  9         57  
486 9         39 while(@pairs)
487 10         30 { my $k = shift @pairs;
488 10         32 $data->{$k} = shift @pairs;
489             }
490              
491 9         38 $faultdec->($data);
492 9         55 $data;
493 6         104 };
494             }
495              
496             sub _reader_hook($$)
497 12     12   132 { my ($self, $type, $do) = @_;
498 12         69 my %trans = map +($_->[1] => [ $_->[0], $_->[2] ]), @$do; # we need copies
499 12         40 my $envns = $self->envelopeNS;
500              
501             my $code = sub
502 12     12   4962 { my ($xml, $trans, $path, $label) = @_;
503 12         25 my %h;
504 12         34 foreach my $child ($xml->childNodes)
505 23 100       173 { next unless $child->isa('XML::LibXML::Element');
506 10         38 my $type = type_of_node $child;
507 10 100       203 if(my $t = $trans{$type})
508 9         28 { my ($label, $code) = @$t;
509 9 50       30 my $v = $code->($child) or next;
510 9 50       1923 if(!defined $v) { }
    50          
    0          
511 9         29 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         28 next;
515             }
516             else
517 1         4 { $h{$type} = $child;
518 1         7 trace __x"node {type} not understood, expected are {has}",
519             type => $type, has => [sort keys %trans];
520             }
521              
522 1 50 50     100 return ($label => $self->replyMustUnderstandFault($type))
523             if $child->getAttributeNS($envns, 'mustUnderstand') || 0;
524             }
525 11         57 ($label => \%h);
526 12         53 };
527              
528 12         57 +{ 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   21 { my ($self, $args) = @_;
560 6   100     64 my $header = $args->{header} || [];
561 6         14 my @rules;
562              
563 6         19 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     27 my $code = $part->{reader} ||= $self->_reader($element, %$args);
568 2         28798 push @rules, [$label, $element, $code];
569             }
570              
571 6         18 \@rules;
572             }
573              
574             sub _reader_body($$)
575 6     6   16 { my ($self, $args, $refxops) = @_;
576 6         14 my $body = $args->{body};
577 6   100     45 my $parts = $body->{parts} || [];
578 6 50       14 my @hooks = @{$args->{hooks} || []};
  6         32  
579 6 100       21 push @hooks, $self->_reader_xop_hook($refxops)
580             if _xop_enabled;
581              
582 6         24 local $args->{hooks} = \@hooks;
583              
584 6         10 my @rules;
585 6         18 foreach my $part (@$parts)
586 4         9 { my $label = $part->{name};
587              
588 4         8 my ($t, $code);
589 4 50       16 if($part->{element})
    0          
590 4         28 { ($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         42 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   11 { my ($self, $args, $part) = @_;
607              
608 4         9 my $element = $part->{element};
609 4   33     51 my $code = $part->{reader} || $self->_reader($element, %$args);
610              
611 4         44308 ($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   2 { my ($self, $refxops) = @_;
644              
645             my $xop_merge = sub
646 4     4   900 { my ($xml, $args, $path, $type, $r) = @_;
647 4 100       15 if(my $incls = $xml->getElementsByTagNameNS(XOP10, 'Include'))
648 1 50 50     176 { my $href = $incls->shift->getAttribute('href') || ''
649             or return ($type => $xml);
650              
651 1         38 $href =~ s/^cid://;
652 1 50       32 my $xop = $$refxops->{$href}
653             or return ($type => $xml);
654              
655 1         8 return ($type => $xop);
656             }
657              
658 3         491 ($type => decode_base64 $xml->textContent);
659 1         6 };
660              
661 1         17 +{ type => 'xsd:base64Binary', replace => $xop_merge };
662             }
663              
664 12     12   78 sub _reader(@) { shift->schemas->reader(@_) }
665 17     17   90 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;