File Coverage

blib/lib/SOAP/DirectI/Parse.pm
Criterion Covered Total %
statement 137 181 75.6
branch 33 68 48.5
condition 12 24 50.0
subroutine 16 18 88.8
pod 0 4 0.0
total 198 295 67.1


line stmt bran cond sub pod time code
1             #
2             #===============================================================================
3             #
4             # FILE: Parse.pm
5             #
6             # DESCRIPTION: SOAP::DirectI::Parse -- parsing SOAP DirectI's responses
7             #
8             # FILES: ---
9             # BUGS: ---
10             # NOTES: ---
11             # AUTHOR: Pavel Boldin (),
12             # COMPANY:
13             # VERSION: 1.0
14             # CREATED: 17.03.2009 20:52:05 MSK
15             # REVISION: ---
16             #===============================================================================
17              
18             package SOAP::DirectI::Parse;
19              
20 2     2   14295 use strict;
  2         5  
  2         81  
21 2     2   12 use warnings;
  2         3  
  2         63  
22              
23 2     2   998 use Data::Dumper;
  2         7384  
  2         174  
24 2     2   2298 use Smart::Comments -ENV;
  2         71462  
  2         21  
25              
26 2     2   5642 use Carp;
  2         6  
  2         4631  
27              
28             local $Data::Dumper::Purity = 1;
29             local $Data::Dumper::Indent = 1;
30              
31             #my $slurp = do {
32             # local $/;
33             # <>;
34             #};
35              
36             #$slurp =~ s/.*(
37             #$slurp =~ s/(<\/soapenv:Body>).*/$1/;
38              
39             sub new {
40 4     4 0 12048 my $class = shift;
41 4   33     28 $class = ref( $class ) || $class;
42              
43 4         11 my $self = {};
44 4         13 bless $self, $class;
45              
46 4         9 return $self;
47             }
48              
49             #bless my $obj = {}, __PACKAGE__;
50             #$obj->parse_xml_string( $slurp );
51              
52             #warn Dumper $obj->{tree};
53              
54             #warn Dumper [ $obj->parse_to_data_and_signature ];
55              
56             sub parse_xml_string {
57 17     17 0 33 my $self = shift;
58 17         22 my $str = shift;
59              
60 17         20 my $parent_tag = shift;
61              
62             #my @tag = ($str =~ m/^<(\w*:)?(\w+)([^>]*)(?:\/>|>(.*?)<\/\1?\2>)$/mxogs);
63              
64             ### parse_xml_string: $str
65              
66             # my @tag = ( $str =~ m{\G<(\w*:)?(\w+)([^>]*)(?:/>|>(.*?))}gms );
67              
68 17 50 66     68 if ( ! $parent_tag && not $str =~ s{\A\s*<\?xml[^>]*\?>}{} ) {
69 0         0 croak "Not an XML data\n";
70             }
71              
72             #use re 'debug';
73              
74 17         170 while ( $str =~ s{\A\s* # start of string
75             <([\w\-]+:)? # start of tag and namespace (if any)
76             (\w+) # name of tag
77             ([^>]*) # attributes in string form
78             (?:
79             />
80             |
81             (? # end of tag either by /> or >
82             (.*?) # content of tag
83             # namespace and tagname in closing tag
84             )
85             }{}gxs ) {
86 34         170 my @tag_arr = my ($namespace, $name, $attr, $content) = ($1, $2, $3, $4);
87              
88 34 50       65 if ( ! defined $name ) {
89 0         0 croak "Unable to parse: $str";
90             }
91              
92              
93 34         239 my $tag = {};
94              
95 34 100       63 if ( $namespace ) {
96 12         18 $namespace =~ tr/://d;
97 12         24 $tag->{namespace} = $namespace;
98             }
99              
100 34 50       90 $tag->{name} = $name if $name ;
101              
102             ### @tag_arr
103              
104 34 100       56 if ( $content ) {
105 31         48 $tag->{content} = $content;
106              
107 31 100       77 if ( $content =~ m/[<>]/ ) {
108 13         35 $self->parse_xml_string( $content, $tag );
109             }
110              
111             }
112              
113 34         58 $tag->{attrs} = $attr;
114              
115 34         159 while( $attr =~ s{^\s*((?:[a-zA-Z-_]+:)?[a-zA-Z-_]+)=[\'\"]([^'"]*)[\'\"]}{}mgosx ) {
116 54         338 $tag->{attr}{ $1 } = $2;
117             }
118              
119              
120 34 100       60 if ( ! $parent_tag ) {
121 4         14 $self->{tree} = $tag;
122 4         11 last;
123             }
124              
125 30         41 push @{ $parent_tag->{siblings} }, $tag;
  30         217  
126             }
127              
128 17 50 66     70 if ( ! $parent_tag && ! $self->{tree} ) {
129 0         0 croak "Could not parse $str: $.";
130             }
131             }
132              
133             sub fetch_data_and_signature {
134 4     4 0 19 my $self = shift;
135              
136 4         8 my $signature = {};
137              
138             ### $self->{tree}
139              
140 4         22 my $tree_root = $self->{tree};
141              
142 4   66     7 while(
143 8 50       71 @{ $tree_root->{siblings} || [] } == 1
144             && $tree_root->{siblings}[0]{namespace} =~ /soap/i
145             ) {
146              
147 4         8 $tree_root = $tree_root->{siblings}[0];
148             }
149              
150 4         5 my ($data, $args_sig);
151              
152 4         19 my @tags =
153 4         8 grep { not $_->{name} =~ m/multiRef/ } @{ $tree_root->{siblings} };
  4         10  
154              
155             ### @tags
156             ### $tree_root
157              
158 4 50 33     58 if ( @tags == 1 && $tags[0]->{name} =~ m/Response/i ) {
    50          
159 0         0 my %multirefs =
160 0         0 map { $_->{attr}{id} => $_ }
161 0         0 grep { $_->{name} =~ m/multiRef/ }
162 0         0 @{ $tree_root->{siblings} };
163              
164             ### %multirefs
165              
166 0         0 $self->{multirefs} = \%multirefs;
167              
168             #warn "multirefs: ", scalar keys %multirefs;
169              
170 0         0 my ($main_tag, $other) = @{ $tags[0]->{siblings} };
  0         0  
171 0 0       0 if ( $other ) {
172 0         0 croak "Something bad happened";
173             }
174            
175             #local $main_tag->{ name } = $tags[0]->{name};
176              
177             #warn $main_tag->{name};
178 0         0 $signature->{name} = $tags[0]->{name};
179              
180 0         0 @tags = ({ %$main_tag, name => $tags[0]->{name} });
181             }
182             elsif( @tags == 1 ) {
183 4         8 $tree_root = $tags[0];
184 4         5 @tags = @{ $tags[0]->{siblings} };
  4         14  
185             }
186              
187 4         12 foreach my $sibling ( @tags ) {
188 21         49 my @ret = $self->_parse_tag( $sibling );
189              
190 21         48 my $tname = $ret[1]->{key};
191 21         70 $tname = join '_', map { lc } split /(?=[A-Z])/, $tname;
  33         84  
192              
193 21         55 $data->{ $tname } = $ret[0];
194 21         51 push @$args_sig, $ret[1];
195             }
196              
197 4 100 66     23 if ( @tags != 1 && $tree_root->{namespace} ) {
198 2         6 my $ns = $self->{tree}{attr}{ 'xmlns:'.$tree_root->{namespace} };
199 2 50       9 $signature->{namespace} = $ns if $ns;
200             }
201              
202 4   33     21 $signature->{name} ||= $tree_root->{name};
203 4         9 $signature->{args} = $args_sig;
204              
205 4 100       10 if ( @tags == 1 ) {
206 2         13 return (values %$data, $signature);
207             }
208              
209 2         10 return ($data, $signature);
210             }
211              
212             sub _get_type {
213 22     22   23 my $self = shift;
214 22         24 my $tag = shift;
215              
216 22 50 33     110 if ( $tag->{name} =~ m/^fault/ || $tag->{name} eq 'detail' ) {
217 0         0 $tag->{attr}{'xsi:type'} = 'xsd:string';
218 0         0 return 'string';
219             }
220              
221 22         73 return $tag->{attr}{'xsi:type'};
222             }
223              
224             sub _parse_tag {
225 22     22   27 my $self = shift;
226 22         20 my $tag = shift;
227              
228 22 50       50 $tag or croak "No tag given";
229              
230 22 50       65 if ( my $href = $tag->{attr}{href} ) {
231 0         0 $href =~ s/^#//;
232 0 0       0 if ( ! wantarray ) {
233 0         0 return $self->_parse_tag( $self->{multirefs}{ $href } );
234             }
235             else {
236 0         0 my @ret = $self->_parse_tag( $self->{multirefs}{ $href } );
237              
238 0         0 $ret[1]->{key} = $tag->{name};
239              
240 0         0 return @ret;
241             }
242             }
243              
244 22 50       44 my $type = $self->_get_type( $tag )
245             or croak "Unknown type for tag $tag->{name}";
246              
247 22         83 $type =~ s/.*?://;
248              
249 22         37 $type = lc $type;
250 22 50       101 if ( my $s = $self->can('_parse_'.$type) ) {
251 22         45 return $s->( $self, $tag );
252             }
253              
254 0         0 croak "Cannot parse $type";
255             }
256              
257             sub unescape_xml {
258 20     20 0 25 $_ = shift;
259              
260 20 100       41 return $_ unless $_;
261              
262 18         25 s/&/&/xgs;
263 18         23 s/</
264 18         21 s/>/>/xgs;
265 18         19 s/"/\"/xgs;
266              
267 18         31 return $_;
268             }
269              
270             sub _parse_string {
271 20     20   26 my ($self, $tag) = @_;
272              
273 20         43 my $c = unescape_xml( $tag->{content} );
274              
275 20 50       45 return $c if not wantarray;
276              
277 20         27 my @ret = ($c);
278              
279 20         35 my $t = $tag->{attr}{'xsi:type'};
280 20         66 $t =~ s/^.*://;
281              
282 20         69 push @ret, {
283             key => $tag->{name},
284             type => $t,
285             };
286              
287 20         85 return @ret;
288             }
289              
290             sub _parse_boolean {
291 0     0   0 my ($self, $tag) = @_;
292              
293 0         0 my ($val, $sig);
294              
295 0 0       0 if ( wantarray ) {
296 0         0 ($val, $sig) = $self->_parse_string( $tag );
297             }
298             else {
299 0         0 $val = $self->_parse_string( $tag );
300             }
301              
302 0 0       0 if ( lc $val eq 'true' ) {
    0          
303 0         0 $val = 1;
304             }
305             elsif( $val eq 'false' ) {
306 0         0 $val = 0;
307             }
308              
309 0 0       0 return wantarray ? ($val, $sig) : $val;
310             }
311              
312              
313             sub _parse_int {
314 2     2   11 my ($self, $tag) = @_;
315              
316 2 50       6 return int( $self->_parse_string( $tag ) ) if not wantarray;
317              
318 2         6 my @ret = $self->_parse_string( $tag );
319 2         14 $ret[0] = int( $ret[0] );
320              
321 2         8 return @ret;
322             }
323              
324             sub _parse_vector_or_array {
325 1     1   3 my $self = shift;
326 1         1 my $tag = shift;
327              
328 1         2 my $array = [];
329 1         2 my $items = $tag->{siblings};
330              
331 1         1 my $elem_sig;
332              
333 1         2 foreach my $item (@$items) {
334 1 50       5 if ( $item->{name} ne 'item' ) {
335 0         0 croak "Vector or Array item has no name 'item'";
336             }
337              
338 1         2 my $value;
339            
340 1 50       2 if ( $elem_sig ) {
341 0         0 $value = $self->_parse_tag($item);
342             }
343             else {
344 1         5 ($value, $elem_sig) = $self->_parse_tag($item);
345             }
346              
347 1         5 push @$array, $value;
348             }
349              
350 1 50       4 return $array if not wantarray;
351              
352 1         3 my $type = $tag->{attr}{'xsi:type'};
353              
354 1         22 $type =~ m/(array|vector)/i;
355              
356 1         7 my $signature = {
357             key => $tag->{name},
358             type => lc $1,
359             elem_sig => $elem_sig,
360             };
361              
362 1         5 return ($array, $signature);
363             }
364              
365             sub _parse_vector {
366 1     1   3 shift->_parse_vector_or_array( @_ );
367             }
368              
369             sub _parse_array {
370 0     0   0 shift->_parse_vector_or_array( @_ );
371             }
372              
373             sub _parse_map {
374 1     1   2 my $self = shift;
375 1         2 my $tag = shift;
376              
377 1         16 my $hash = {};
378              
379 1         11 my $items = $tag->{siblings};
380              
381 1         2 my ($key_sig, $value_sig);
382              
383 1         3 foreach my $item (@$items) {
384 0 0       0 if ( $item->{name} ne 'item' ) {
385 0         0 croak "Map item has no name 'item'";
386             }
387              
388 0         0 my ($key, $value);
389              
390 0 0       0 if ( $key_sig ) {
391 0         0 $key = $self->_parse_tag($item->{siblings}[0]);
392 0         0 $value = $self->_parse_tag($item->{siblings}[1]);
393             }
394             else {
395 0         0 ($key, $key_sig) = $self->_parse_tag($item->{siblings}[0]);
396 0         0 ($value, $value_sig) = $self->_parse_tag($item->{siblings}[1]);
397              
398             #$key_type = ref $key ? $key_sig : $key_sig->{type}
399             #or croak "No key type given for $tag->{name}";
400             #$value_type = ref $value ? $value_sig : $value_sig->{type}
401             #or croak "No value type given for $tag->{name}";
402             }
403              
404              
405 0         0 $hash->{ $key } = $value;
406             }
407              
408             # warn Dumper $items;
409              
410 1 50       4 return $hash if not wantarray;
411              
412 1         2 my $signature = {};
413              
414 1         4 $signature->{key} = $tag->{name};
415 1         2 $signature->{type} = 'map';
416              
417 1         3 $signature->{key_sig} = $key_sig ;
418 1         2 $signature->{value_sig} = $value_sig ;
419              
420              
421 1         5 return ($hash, $signature);
422             }
423              
424             1;