File Coverage

blib/lib/Pod/WSDL.pm
Criterion Covered Total %
statement 42 263 15.9
branch 0 86 0.0
condition 0 49 0.0
subroutine 14 29 48.2
pod 3 3 100.0
total 59 430 13.7


line stmt bran cond sub pod time code
1             package Pod::WSDL;
2              
3             # TODO: make array based objects work as own complex types
4             # TODO: non RPC style bindings
5             # TODO: read type information alternatively from own file
6             # TODO: write soapAction attribute in operations?
7              
8 6     6   214335 use strict;
  6         14  
  6         291  
9 6     6   35 use warnings;
  6         12  
  6         529  
10 6     6   34 use Carp;
  6         10  
  6         677  
11 6     6   7432 use IO::Scalar;
  6         180183  
  6         301  
12 6     6   9124 use Pod::Text;
  6         483430  
  6         5810  
13 6     6   4854 use Pod::WSDL::Method;
  6         23  
  6         208  
14 6     6   129 use Pod::WSDL::Return;
  6         15  
  6         145  
15 6     6   41 use Pod::WSDL::Param;
  6         10  
  6         144  
16 6     6   32 use Pod::WSDL::Fault;
  6         11  
  6         117  
17 6     6   32 use Pod::WSDL::Doc;
  6         11  
  6         313  
18 6     6   4552 use Pod::WSDL::Type;
  6         19  
  6         441  
19 6     6   46 use Pod::WSDL::Writer;
  6         12  
  6         160  
20 6     6   33 use Pod::WSDL::Utils qw(:writexml :namespaces :messages :types);
  6         9  
  6         1922  
21 6     6   35 use Pod::WSDL::AUTOLOAD;
  6         11  
  6         41073  
22              
23             # -------------------------------------------------------------------------- #
24             # ------------------ > "CONSTANTS" ----------------------------------------- #
25             # -------------------------------------------------------------------------- #
26              
27             our $VERSION = "0.062";
28             our @ISA = qw/Pod::WSDL::AUTOLOAD/;
29              
30             our $WSDL_METHOD_REGEXP_BEG = qr/^=(?:begin)\s+wsdl\s*\n(.*?)^=(?:cut|end\s+wsdl).*?^\s*sub\s+(\w+)/ims;
31             our $WSDL_METHOD_REGEXP_FOR = qr/^=(?:for)\s+wsdl\s*\n(.*?)\n\n^\s*sub\s+(\w+)/ims;
32             our $WSDL_TYPE_REGEXP_BEG = qr/^=(?:begin)\s+wsdl\s*\n(.*?_ATTR.*?)^=(?:cut|end\s+wsdl)/ims;
33             our $WSDL_TYPE_REGEXP_FOR = qr/^=(?:for)\s+wsdl\s*\n(.*?_ATTR.*?)\n\n/ims;
34              
35             our $DEFAULT_BASE_NAME = 'myService';
36             our $PORT_TYPE_SUFFIX_NAME = 'Handler';
37             our $BINDING_SUFFIX_NAME = 'SoapBinding';
38             our $SERVICE_SUFFIX_NAME = 'Service';
39              
40             # Pod::WSDL::AUTOLOAD uses this
41             our %FORBIDDEN_METHODS = (
42             source => {get => 0, set => 0},
43             source => {get => 0, set => 0},
44             baseName => {get => 0, set => 0},
45             methods => {get => 0, set => 0},
46             location => {get => 1, set => 1},
47             namespaces => {get => 0, set => 0},
48             generateNS => {get => 0, set => 0},
49             types => {get => 0, set => 0},
50             writer => {get => 0, set => 0},
51             standardTypeArrays => {get => 0, set => 0},
52             emptymessagewritten => {get => 0, set => 0},
53             targetNS => {get => 1, set => 1},
54             );
55              
56             # -------------------------------------------------------------------------- #
57             # --------------- > PUBLIC METHODS ---------------------------------------- #
58             # -------------------------------------------------------------------------- #
59              
60             sub new {
61 0     0 1   my ($pkg, %data) = @_;
62 0           my $nsnum = 0;
63            
64 0 0         croak "I need a location, died" unless defined $data{location};
65 0 0         croak "I need a file or module name or a filehandle, died" unless defined $data{source};
66            
67 0 0 0       $data{use} = $LITERAL_USE if $data{style} and $data{style} eq $DOCUMENT_STYLE and !defined $data{use};
      0        
68 0 0 0       $data{use} = $LITERAL_USE and $data{style} = $DOCUMENT_STYLE if $data{wrapped} and !defined $data{use} and !defined $data{style};
      0        
      0        
69              
70 0     0     my $me = bless {
71             _source => $data{source},
72             _baseName => undef,
73             _methods => [],
74             _location => $data{location},
75             _namespaces => {},
76             _targetNS => undef,
77             _generateNS => sub {return $DEFAULT_NS_DECL . $nsnum++},
78 0   0       _types => {},
      0        
      0        
79             _writer => new Pod::WSDL::Writer(withDocumentation => $data{withDocumentation}, pretty => $data{pretty}),
80             _standardTypeArrays => {},
81             _emptymessagewritten => 0,
82             _use => $data{use} || $ENCODED_USE,
83             _style => $data{style} || $RPC_STYLE,
84             _wrapped => $data{wrapped} || 0,
85             }, $pkg;
86              
87 0 0 0       croak "'use' argument may only be one of $ENCODED_USE or $LITERAL_USE, died" if $me->use ne $ENCODED_USE and $me->use ne $LITERAL_USE;
88 0 0 0       croak "'style' argument may only be one of $RPC_STYLE or $DOCUMENT_STYLE, died" if $me->style ne $RPC_STYLE and $me->style ne $DOCUMENT_STYLE;
89 0 0 0       croak "The combination of use=$ENCODED_USE and style=$DOCUMENT_STYLE is not valid, died" if ($me->style eq $DOCUMENT_STYLE and $me->use eq $ENCODED_USE);
90              
91             ## AHICOX 10/12/2006
92             ## this is a quick and dirty hack to set the baseName
93             ## the baseName should probably be set from the POD
94             ## source (which is why it's set in _getModuleCode)
95             ## this quick hack takes the 'name' parameter when
96             ## we create the object, and
97            
98 0           $me->_initSource($data{'source'});
99 0           $me->_initNS;
100 0           $me->_initTypes;
101            
102 0           return $me;
103             }
104              
105             sub WSDL {
106 0     0 1   my $me = shift;
107 0           my %args = @_;
108            
109 0           my $wr = $me->writer;
110 0           $wr->prepare;
111              
112 0 0         if (%args) {
113 0 0         $wr->pretty($args{pretty}) if defined $args{pretty};
114 0 0         $wr->withDocumentation($args{withDocumentation}) if defined $args{withDocumentation};
115             }
116            
117 0           $me->writer->comment("WSDL for " . $me->{_location} . " created by " . ref ($me) . " version: $VERSION on " . scalar localtime);
118 0           $me->writer->startTag('wsdl:definitions', targetNamespace => $me->targetNS, %{$me->{_namespaces}});
  0            
119 0           $me->writer->wrNewLine(2);
120              
121 0           $me->_writeTypes;
122              
123 0           $_->writeMessages($me->types, $me->style, $me->wrapped) for @{$me->methods};
  0            
124              
125 0           $me->_writePortType;
126 0           $me->_writeBinding;
127 0           $me->_writeService;
128              
129 0           $me->writer->endTag('wsdl:definitions');
130 0           $me->writer->end;
131 0           return $me->writer->output;
132             }
133              
134             sub addNamespace {
135 0     0 1   my $me = shift;
136 0           my $uri = shift;
137 0           my $decl = shift;
138            
139 0 0         croak "I need a namespace, died" unless defined $uri;
140            
141 0 0         defined $decl or $decl = $me->{_generateNS};
142            
143 0 0         $decl = 'xmlns:' . $decl unless $decl =~ /xmlns:/;
144              
145 0           $me->{_namespaces}->{$decl} = $uri;
146             }
147              
148             # -------------------------------------------------------------------------- #
149             # ---------------- > INIT METHODS < ---------------------------------------- #
150             # -------------------------------------------------------------------------- #
151              
152             sub _initNS {
153 0     0     my $me = shift;
154 0           my $namespaces = shift;
155            
156 0   0       $namespaces ||= {};
157            
158 0           $me->addNamespace($namespaces->{$_}, $_) for keys %$namespaces;
159 0           $me->addNamespace($BASIC_NAMESPACES{$_}, $_) for keys %BASIC_NAMESPACES;
160 0           $me->addNamespace($me->targetNS, $IMPL_NS_DECL);
161 0           $me->addNamespace($me->targetNS, $TARGET_NS_DECL);
162             }
163              
164             sub _initSource {
165 0     0     my $me = shift;
166 0           my $src = shift;
167            
168 0           my ($baseName, $contents) = $me->_getModuleCode($src, 1);
169            
170             #set the baseName in the object
171 0           $me->baseName($baseName);
172              
173             # find =begin wsdl ... =end
174 0           while ($contents =~ /$WSDL_METHOD_REGEXP_BEG/g) {
175 0           $me->_parseMethodPod($2, $1);
176             }
177              
178             # find =for wsdl
179 0           while ($contents =~ /$WSDL_METHOD_REGEXP_FOR/g) {
180 0           $me->_parseMethodPod($2, $1);
181             }
182             }
183              
184             sub _initTypes {
185 0     0     my $me = shift;
186              
187            
188 0           for my $method (@{$me->{_methods}}) {
  0            
189 0           for my $param (@{$method->params},$method->return) {
  0            
190 0 0         next unless $param;
191 0 0         unless (exists $XSD_STANDARD_TYPE_MAP{$param->type}) {
    0          
192 0           $me->_addType($param->type, $param->array);
193             } elsif ($param->array) {
194            
195             #AHICOX: 10/10/2006
196             #changed to _standardTypeArrays (was singular)
197 0           $me->{_standardTypeArrays}->{$param->type} = 1;
198             }
199             }
200              
201 0           for my $fault (@{$method->faults}) {
  0            
202 0 0         unless (exists $XSD_STANDARD_TYPE_MAP{$fault->type}) {
203 0           $me->_addType($fault->type, 0);
204             }
205             }
206             }
207              
208             }
209              
210             sub _addType {
211 0     0     my $me = shift;
212 0           my $name = shift;
213 0           my $array = shift;
214            
215 0 0         if (exists $me->types->{$name}) {
216 0 0         $me->types->{$name}->array($array) if $array;
217 0           return;
218             }
219            
220 0           my $code = $me->_getModuleCode($name);
221 0           my $pod = '';
222 0           my $in = $code;
223 0           my $out = '';
224            
225             # collect =begin wsdl ... =end
226 0           while ($code =~ /$WSDL_TYPE_REGEXP_BEG/g) {
227 0           $pod .= "$1\n";
228             }
229            
230             # collect =for wsdl
231 0           while ($code =~ /$WSDL_TYPE_REGEXP_FOR/g) {
232 0           $pod .= "$1\n";
233             }
234              
235 0 0         warn "No pod wsdl found for type '$name'.\n" unless $pod;
236              
237 0           my $IN = new IO::Scalar \$in;
238 0           my $OUT = new IO::Scalar \$out;
239            
240 0           new Pod::Text()->parse_from_filehandle($IN, $OUT);
241            
242 0           $me->types->{$name} = new Pod::WSDL::Type(name => $name, array => $array, pod => $pod, descr => $out, writer => $me->writer);
243            
244 0           for my $attr (@{$me->types->{$name}->attrs}) {
  0            
245 0 0         unless (exists $XSD_STANDARD_TYPE_MAP{$attr->type}) {
    0          
246 0           $me->_addType($attr->type, $attr->array);
247             } elsif ($attr->array) {
248            
249             #AHICOX: 10/10/2006
250             #changed to _standardTypeArrays (was singular)
251 0           $me->{_standardTypeArrays}->{$attr->type} = 1;
252             }
253             }
254             }
255              
256             sub _parseMethodPod {
257 0     0     my $me = shift;
258 0           my $methodName = shift;
259 0           my $podData = shift;
260            
261 0           my $method = new Pod::WSDL::Method(name => $methodName, writer => $me->writer);
262            
263 0           my @data = split "\n", $podData;
264            
265             # Preprocess wsdl pod: trim all lines and concatenate lines not
266             # beginning with wsdl type tokens to previous line.
267             # Ignore first element, if it does not begin with wsdl type token.
268 0           for (my $i = $#data; $i >= 0; $i--) {
269            
270 0 0         if ($data[$i] !~ /^\s*(_INOUT|_IN|_OUT|_RETURN|_DOC|_FAULT|_ONEWAY)/i) {
271 0 0         if ($i > 0) {
272 0           $data[$i - 1] .= " $data[$i]";
273 0           $data[$i] = '';
274             }
275             }
276             }
277              
278 0           for (@data) {
279 0           s/\s+/ /g;
280 0           s/^ //;
281 0           s/ $//;
282              
283 0 0         if (/^_(INOUT|IN|OUT)\s+/i) {
    0          
    0          
    0          
    0          
284 0           my $param = new Pod::WSDL::Param($_);
285 0           $method->addParam($param);
286 0 0 0       $me->standardTypeArrays->{$param->type} = 1 if $param->array and $XSD_STANDARD_TYPE_MAP{$param->type};
287             } elsif (/^_RETURN\s+/i) {
288 0           my $return = new Pod::WSDL::Return($_);
289 0           $method->return($return);
290 0 0 0       $me->standardTypeArrays->{$return->type} = 1 if $return->array and $XSD_STANDARD_TYPE_MAP{$return->type};
291             } elsif (/^_DOC\s+/i) {
292 0           $method->doc(new Pod::WSDL::Doc($_));
293             } elsif (/^_FAULT\s+/i) {
294 0           $method->addFault(new Pod::WSDL::Fault($_));
295             } elsif (/^_ONEWAY\s*$/i) {
296 0           $method->oneway(1);
297             }
298             }
299              
300 0           push @{$me->{_methods}}, $method;
  0            
301             }
302              
303             sub _getModuleCode {
304 0     0     my $me = shift;
305 0           my $src = shift;
306 0           my $findNS = shift;
307            
308 0 0 0       if (ref $src and ($src->isa('IO::Handle') or $src->isa('GLOB'))) {
      0        
309 0           local $/ = undef;
310 0           my $contents = <$src>;
311 0 0         $me->_setTargetNS($contents) if $findNS;
312            
313             ##AHICOX: 10/12/2006
314             ##attempt to construct a base name based on the package
315 0           my $baseName = $DEFAULT_BASE_NAME;
316 0           $src =~ /package\s+(.*?)\s*;/s;
317 0 0         if ($1){
318 0           $baseName = $1;
319 0           $baseName =~ s/::(.)/uc $1/eg;
  0            
320             }
321            
322 0           return ($baseName, $contents);
323             } else {
324            
325 0           my $moduleFile;
326            
327 0 0         if (-e $src) {
328 0           $moduleFile = $src;
329             } else {
330 0           my $subDir = $src;
331 0           $subDir =~ s!::!/!g;
332            
333 0           my @files = map {"$_/$subDir.pm"} @INC;
  0            
334            
335 0           my $foundPkg = 0;
336            
337 0           for my $file (@files) {
338 0 0         if (-e $file) {
339 0           $moduleFile = $file;
340 0           last;
341             }
342             }
343             }
344            
345 0 0         if ($moduleFile) {
346 0 0         open IN, $moduleFile or die "Could not open $moduleFile, died";
347 0           local $/ = undef;
348 0           my $contents = ;
349 0           close IN;
350 0 0         $me->_setTargetNS($contents) if $findNS;
351            
352             ##AHICOX: 10/12/2006
353             ##attempt to construct a base name based on the package
354 0           my $baseName = $DEFAULT_BASE_NAME;
355 0           $contents =~ /package\s+(.*?)\s*;/s;
356 0 0         if ($1){
357 0           $baseName = $1;
358 0           $baseName =~ s/::(.)/uc $1/eg;
  0            
359             }
360            
361 0           return ($baseName, $contents);
362             } else {
363 0           die "Can't find any file '$src' and can't locate it as a module in \@INC either (\@INC contains " . join (" ", @INC) . "), died";
364             }
365             }
366             }
367              
368             sub _setTargetNS {
369 0     0     my $me = shift;
370 0           my $contents = shift;
371              
372 0           $contents =~ /package\s+(.*?)\s*;/s;
373              
374 0 0         if ($1) {
375 0           my $tmp = $1;
376 0           $tmp =~ s!::!/!g;
377 0           my $serverURL = $me->location;
378 0           $serverURL =~ s!(http(s)??://[^/]*).*!$1!;
379 0           $me->targetNS("$serverURL/$tmp");
380             } else {
381 0           $me->targetNS($me->location);
382             }
383             }
384              
385             # -------------------------------------------------------------------------- #
386             # -------------- > OUTPUT UTILITIES < -------------------------------------- #
387             # -------------------------------------------------------------------------- #
388              
389             sub _writeTypes {
390 0     0     my $me = shift;
391              
392 0 0 0       return if keys %{$me->standardTypeArrays} == 0 and keys %{$me->types} == 0;
  0            
  0            
393              
394 0           $me->writer->wrElem($START_PREFIX_NAME, 'wsdl:types');
395 0           $me->writer->wrElem($START_PREFIX_NAME, 'schema', targetNamespace => $me->namespaces->{'xmlns:' . $TARGET_NS_DECL}, xmlns => "http://www.w3.org/2001/XMLSchema");
396 0           $me->writer->wrElem($EMPTY_PREFIX_NAME, "import", namespace => "http://schemas.xmlsoap.org/soap/encoding/");
397            
398 0           for my $type (sort keys %{$me->standardTypeArrays}) {
  0            
399 0           $me->writer->wrElem($START_PREFIX_NAME, "complexType", name => $ARRAY_PREFIX_NAME . ucfirst $type);
400 0           $me->writer->wrElem($START_PREFIX_NAME, "complexContent");
401 0           $me->writer->wrElem($START_PREFIX_NAME, "restriction", base => "soapenc:Array");
402 0           $me->writer->wrElem($EMPTY_PREFIX_NAME, "attribute", ref => "soapenc:arrayType", "wsdl:arrayType" => 'soapenc:' . $type . '[]');
403 0           $me->writer->wrElem($END_PREFIX_NAME, "restriction");
404 0           $me->writer->wrElem($END_PREFIX_NAME, "complexContent");
405 0           $me->writer->wrElem($END_PREFIX_NAME, "complexType");
406             }
407              
408 0           for my $type (values %{$me->types}) {
  0            
409 0           $type->writeComplexType($me->types);
410             }
411              
412 0 0         if ($me->style eq $DOCUMENT_STYLE) {
413 0           for my $method (@{$me->methods}) {
  0            
414 0           $method->writeDocumentStyleSchemaElements($me->types);
415             }
416             }
417              
418 0           $me->writer->wrElem($END_PREFIX_NAME, 'schema');
419 0           $me->writer->wrElem($END_PREFIX_NAME, 'wsdl:types');
420 0           $me->writer->wrNewLine;
421             }
422              
423             sub _writePortType {
424 0     0     my $me = shift;
425            
426 0           $me->writer->wrElem($START_PREFIX_NAME, 'wsdl:portType', name => $me->baseName . $PORT_TYPE_SUFFIX_NAME);
427              
428 0           for my $method (@{$me->{_methods}}) {
  0            
429 0           $method->writePortTypeOperation;
430 0           $me->writer->wrNewLine;
431             }
432              
433 0           $me->writer->wrElem($END_PREFIX_NAME, 'wsdl:portType');
434 0           $me->writer->wrNewLine(1);
435             }
436              
437             sub _writeBinding {
438 0     0     my $me = shift;
439            
440 0           $me->writer->wrElem($START_PREFIX_NAME, 'wsdl:binding', name => $me->baseName . $BINDING_SUFFIX_NAME, type => $IMPL_NS_DECL . ':' . $me->baseName . $PORT_TYPE_SUFFIX_NAME);
441 0           $me->writer->wrElem($EMPTY_PREFIX_NAME, "wsdlsoap:binding", style => $me->style, transport => "http://schemas.xmlsoap.org/soap/http");
442 0           $me->writer->wrNewLine;
443            
444 0           for my $method (@{$me->methods}) {
  0            
445 0           $method->writeBindingOperation($me->targetNS, $me->use);
446 0           $me->writer->wrNewLine;
447             }
448              
449 0           $me->writer->wrElem($END_PREFIX_NAME, 'wsdl:binding');
450 0           $me->writer->wrNewLine;
451             }
452              
453             sub _writeService {
454 0     0     my $me = shift;
455            
456 0           $me->writer->wrElem($START_PREFIX_NAME, 'wsdl:service', name => $me->baseName . $PORT_TYPE_SUFFIX_NAME . $SERVICE_SUFFIX_NAME);
457 0           $me->writer->wrElem($START_PREFIX_NAME, 'wsdl:port', binding => $IMPL_NS_DECL . ':' . $me->baseName . $BINDING_SUFFIX_NAME, name => $me->baseName);
458 0           $me->writer->wrElem($EMPTY_PREFIX_NAME, "wsdlsoap:address", location => $me->location);
459 0           $me->writer->wrElem($END_PREFIX_NAME, 'wsdl:port');
460 0           $me->writer->wrElem($END_PREFIX_NAME, 'wsdl:service');
461              
462 0           $me->writer->wrNewLine;
463             }
464              
465             1;
466             __END__