File Coverage

blib/lib/W3C/SOAP/XSD.pm
Criterion Covered Total %
statement 45 174 25.8
branch 0 88 0.0
condition 0 51 0.0
subroutine 15 37 40.5
pod 6 6 100.0
total 66 356 18.5


line stmt bran cond sub pod time code
1             package W3C::SOAP::XSD;
2              
3             # Created on: 2012-05-26 23:50:44
4             # Create by: Ivan Wills
5             # $Id$
6             # $Revision$, $HeadURL$, $Date$
7             # $Revision$, $Source$, $Date$
8              
9 1     1   1134 use Moose;
  1         2  
  1         5  
10 1     1   3261 use warnings;
  1         2  
  1         23  
11 1     1   4 use version;
  1         1  
  1         7  
12 1     1   59 use Carp qw/carp croak cluck confess longmess/;
  1         2  
  1         61  
13 1     1   4 use Scalar::Util;
  1         2  
  1         34  
14 1     1   4 use List::Util;
  1         2  
  1         39  
15             #use List::MoreUtils;
16 1     1   4 use Data::Dumper qw/Dumper/;
  1         1  
  1         40  
17 1     1   4 use English qw/ -no_match_vars /;
  1         1  
  1         5  
18 1     1   315 use Moose::Util::TypeConstraints;
  1         2  
  1         8  
19 1     1   2024 use MooseX::Types::XMLSchema;
  1         191858  
  1         11  
20 1     1   4042 use W3C::SOAP::XSD::Types qw/:all/;
  1         3  
  1         4  
21 1     1   2983 use W3C::SOAP::XSD::Traits;
  1         3  
  1         90  
22 1     1   7 use W3C::SOAP::Utils qw/split_ns/;
  1         1  
  1         8  
23 1     1   340 use Try::Tiny;
  1         2  
  1         56  
24 1     1   5 use DateTime::Format::Strptime qw/strptime/;
  1         1  
  1         2040  
25              
26             extends 'W3C::SOAP::Base';
27              
28             our $VERSION = version->new('0.11');
29              
30             has xsd_ns => (
31             is => 'rw',
32             isa => 'Str',
33             );
34             has xsd_ns_name => (
35             is => 'rw',
36             isa => 'Str',
37             predicate => 'has_xsd_ns_name',
38             clearer => 'clear_xsd_ns_name',
39             builder => '_xsd_ns_name',
40             lazy => 1,
41             );
42              
43             {
44             my %required;
45             my $require = sub {
46             my ($module) = @_;
47             return if $required{$module}++;
48             return if eval{ $module->can('new') };
49              
50             my $file = "$module.pm";
51             $file =~ s{::}{/}gxms;
52             require $file;
53             };
54             around BUILDARGS => sub {
55             my ($orig, $class, @args) = @_;
56             my $args
57             = !@args ? {}
58             : @args == 1 ? $args[0]
59             : {@args};
60              
61             if ( blessed $args && $args->isa('XML::LibXML::Node') ) {
62             my $xml = $args;
63             my $child = $xml->firstChild;
64             my $map = $class->xml2perl_map;
65             my ($element) = $class =~ /::([^:]+)$/xms;
66             $args = {};
67              
68             while ($child) {
69             if ( $child->nodeName !~ /^[#]/xms ) {
70             my ($node_ns, $node) = split_ns($child->nodeName);
71             confess "Could not get node from (".$child->nodeName." via '$node_ns', '$node')\n"
72             if !$map->{$node};
73             my $attrib = $map->{$node};
74             $node = $attrib->name;
75             my $module = $attrib->has_xs_perl_module ? $attrib->xs_perl_module : undef;
76             $require->($module) if $module;
77             my $value = $module ? $module->new($child) : $child->textContent;
78              
79             $args->{$node}
80             = !exists $args->{$node} ? $value
81             : ref $args->{$node} ne 'ARRAY' ? [ $args->{$node} , $value ]
82             : [ @{$args->{$node}}, $value ];
83             }
84             $child = $child->nextSibling;
85             }
86             }
87              
88             return $class->$orig($args);
89             };
90             }
91              
92             my %ns_map;
93             my $count = 0;
94             sub _xsd_ns_name {
95 0     0     my ($self) = @_;
96 0           return $self->get_xsd_ns_name($self->xsd_ns);
97             }
98              
99             sub get_xsd_ns_name {
100 0     0 1   my ($self, $ns) = @_;
101              
102 0 0         return $ns_map{$ns} if $ns_map{$ns};
103              
104 0           return $ns_map{$ns} = 'WSX' . $count++;
105             }
106              
107             sub _from_xml {
108 0     0     my ($class, $type) = @_;
109 0           my $xml = $_;
110 0 0 0       confess "Unknown conversion " . ( (ref $xml) || $xml )
      0        
      0        
111             if !$xml || !blessed $xml || !$xml->isa('XML::LibXML::Node');
112              
113 0           my $ret;
114              
115             try {
116 0     0     $ret = $type->new($xml);
117             }
118             catch {
119 0     0     $_ =~ s/\s at \s .*//xms;
120 0           warn "$class Failed in building from $type\->new($xml) : $_\n",
121             "Will use :\n\t'",
122             $xml->toString,
123             "'\n\tor\n\t'",
124             $xml->textContent,"'\n",
125             '*' x 222,
126             "\n";
127 0           $ret = $xml->textContent;
128 0           };
129              
130 0           return $ret;
131             }
132              
133             sub xml2perl_map {
134 0     0 1   my ($class) = @_;
135 0           my %map;
136              
137 0           for my $attr ($class->get_xml_nodes) {
138 0           $map{$attr->xs_name} = $attr;
139             }
140              
141             # get super class nodes (if any)
142 0           my $meta = $class->meta;
143              
144 0           for my $super ( $meta->superclasses ) {
145 0 0 0       next if !$super->can('xml2perl_map') && $super ne __PACKAGE__;
146 0           %map = ( %{ $super->xml2perl_map }, %map );
  0            
147             }
148              
149 0           return \%map;
150             }
151              
152             sub to_xml {
153 0     0 1   my ($self, $xml) = @_;
154 0 0         confess "No XML document passed to attach nodes to!" if !$xml;
155 0           my $child;
156 0           my $meta = $self->meta;
157 0           my @attributes = $self->get_xml_nodes;
158              
159 0           my @nodes;
160 0           $self->clear_xsd_ns_name;
161 0 0         my $xsd_ns_name = $self->xsd_ns ? $self->xsd_ns_name : undef;
162              
163 0           for my $att (@attributes) {
164 0           my $name = $att->name;
165              
166             # skip attributes that are not XSD attributes
167 0 0         next if !$att->does('W3C::SOAP::XSD');
168 0           my $has = "has_$name";
169              
170             # skip sttributes that are not set
171 0 0         next if !$self->$has;
172              
173 0 0         my $xml_name = $att->has_xs_name ? $att->xs_name : $name;
174 0 0         my $xml_ns = $att->has_xs_ns ? $att->xs_ns : $self->xsd_ns;
175 0 0         my $xml_ns_name
    0          
176             = !defined $xml_ns ? $xsd_ns_name
177             : $xml_ns ? $self->get_xsd_ns_name($xml_ns)
178             : '';
179              
180 0 0         my $value = ref $self->$name eq 'ARRAY' ? $self->$name : [$self->$name];
181              
182 0           for my $item (@$value) {
183 0 0         my $tag = $xml->createElement($xml_ns_name ? $xml_ns_name . ':' . $xml_name : $xml_name);
184 0 0         $tag->setAttribute("xmlns:$xml_ns_name" => $xml_ns) if $xml_ns;
185              
186 0 0 0       if ( blessed($item) && $item->can('to_xml') ) {
    0 0        
187             #$item->xsd_ns_name( $xsd_ns_name ) if !$item->has_xsd_ns_name;
188 0           my @children = $item->to_xml($xml);
189 0           $tag->appendChild($_) for @children;
190             }
191             elsif ( ! defined $item && ! $att->has_xs_serialize ) {
192 0           $tag->setAttribute('nil', 'true');
193 0           $tag->setAttribute('null', 'true');
194             }
195             else {
196 0           local $_ = $item;
197 0 0         my $text
198             = $att->has_xs_serialize
199             ? $att->xs_serialize->($item)
200             : "$item";
201 0           $tag->appendChild( $xml->createTextNode($text) );
202             }
203              
204 0           push @nodes, $tag;
205             }
206             }
207              
208 0           return @nodes;
209             }
210              
211             sub to_data {
212 0     0 1   my ($self, %option) = @_;
213 0           my $child;
214 0           my $meta = $self->meta;
215 0           my @attributes = $self->get_xml_nodes;
216              
217 0           my %nodes;
218              
219 0           for my $att (@attributes) {
220 0           my $name = $att->name;
221              
222             # skip attributes that are not XSD attributes
223 0 0         next if !$att->does('W3C::SOAP::XSD');
224 0           my $has = "has_$name";
225              
226             # skip sttributes that are not set
227 0 0         next if !$self->$has;
228              
229 0 0 0       my $key_name = $att->has_xs_name && $option{like_xml} ? $att->xs_name : $name;
230 0           my $value = $self->$name;
231              
232 0 0         if ( ref $value eq 'ARRAY' ) {
233 0           my @elements;
234 0           for my $element (@$value) {
235 0 0 0       if ( blessed($element) && $element->can('to_data') ) {
236 0           push @elements, $element->to_data(%option);
237             }
238             }
239 0           $nodes{$key_name} = \@elements;
240             }
241             else {
242 0 0 0       if ( blessed($value) && $value->can('to_data') ) {
    0 0        
    0          
243 0           $value = $value->to_data(%option);
244             }
245             elsif ( ! defined $value && ! $att->has_xs_serialize ) {
246             }
247             elsif ($option{stringify}) {
248 0           local $_ = $value;
249 0 0         my $text
250             = $att->has_xs_serialize
251             ? $att->xs_serialize->($value)
252             : "$value";
253 0 0         $value = defined $value ? $text : $value;
254             }
255              
256 0           $nodes{$key_name} = $value;
257             }
258             }
259              
260 0           return \%nodes;
261             }
262              
263             sub get_xml_nodes {
264 0     0 1   my ($self) = @_;
265 0           my $meta = $self->meta;
266              
267 0           my @parent_nodes;
268 0           my @supers = $meta->superclasses;
269 0           for my $super (@supers) {
270 0 0 0       push @parent_nodes, $super->get_xml_nodes if $super ne __PACKAGE__ && eval { $super->can('get_xml_nodes') };
  0            
271             }
272              
273 0           return @parent_nodes, map {
274 0           $meta->get_attribute($_)
275             }
276             sort {
277 0           $meta->get_attribute($a)->insertion_order <=> $meta->get_attribute($b)->insertion_order
278             }
279             grep {
280 0           $meta->get_attribute($_)->does('W3C::SOAP::XSD::Traits')
281             }
282             $meta->get_attribute_list;
283             }
284              
285             my %types;
286             sub xsd_subtype {
287 0     0 1   my ($self, %args) = @_;
288 0   0       my $parent_type = $args{module} || $args{parent};
289              
290             # upgrade types
291 0 0         $parent_type
    0          
    0          
    0          
    0          
    0          
292             = $parent_type eq 'xs:date' ? 'xsd:date'
293             : $parent_type eq 'xs:dateTime' ? 'xsd:dateTime'
294             : $parent_type eq 'xs:boolean' ? 'xsd:boolean'
295             : $parent_type eq 'xs:double' ? 'xsd:double'
296             : $parent_type eq 'xs:decimal' ? 'xsd:decimal'
297             : $parent_type eq 'xs:long' ? 'xsd:long'
298             : $parent_type;
299              
300 0 0         my $parent_type_name
    0          
301             = $args{list} ? "ArrayRef[$parent_type]"
302             : $args{nillable} ? "Maybe[$parent_type]"
303             : $parent_type;
304              
305 0   0       my $subtype = $parent_type =~ /^xsd:\w/xms && Moose::Util::TypeConstraints::find_type_constraint($parent_type);
306 0 0 0       return $subtype if $subtype && !($args{list} || $args{simple_list});
      0        
307              
308 0     0     $subtype = subtype
309             as $parent_type_name,
310 0           message {"'$_' failed to validate as a $parent_type"};
311              
312 0 0         if ( $args{list} ) {
    0          
313 0 0         if ( $args{module} ) {
314             coerce $subtype =>
315             from 'xml_node' =>
316 0     0     via { [$parent_type->new($_)] };
  0            
317             coerce $subtype =>
318             from 'HashRef' =>
319 0     0     via { [$parent_type->new($_)] };
  0            
320             coerce $subtype =>
321             from 'ArrayRef[HashRef]' =>
322 0     0     via { [ map { $parent_type->new($_) } @$_ ] };
  0            
  0            
323             coerce $subtype =>
324             from $parent_type =>
325 0     0     via { [$_] };
  0            
326             }
327             else {
328             coerce $subtype =>
329             from 'xml_node' =>
330 0     0     via { [$_->textContent] };
  0            
331             coerce $subtype =>
332             from 'ArrayRef[xml_node]' =>
333 0     0     via { [ map { $_->textContent } @$_ ] };
  0            
  0            
334             }
335             }
336             elsif ( $args{module} ) {
337             coerce $subtype =>
338             from 'xml_node' =>
339 0     0     via { $parent_type->new($_) };
  0            
340             coerce $subtype =>
341             from 'HashRef' =>
342 0     0     via { $parent_type->new($_) };
  0            
343             }
344             else {
345             coerce $subtype =>
346             from 'xml_node' =>
347 0     0     via { $_->textContent };
  0            
348             }
349              
350 0 0         if ($args{simple_list}) {
351             coerce $subtype =>
352             from "ArrayRef" =>
353 0     0     via { join ' ', @$_ };
  0            
354             }
355             # Propogate coercion from Any via parent's type coercion.
356 0           my $this_type = $subtype->parent;
357 0 0 0       if ($this_type->has_parent && ref $this_type->parent) {
358             coerce $subtype
359             => from 'Any'
360             => via {
361 0 0 0 0     !defined $_ && $args{nillable} ? undef
    0          
362             : $args{nillable} ? Moose::Util::TypeConstraints::find_type_constraint($parent_type)->coerce($_)
363             : $this_type->parent->coerce($_)
364 0           };
365             }
366              
367 0           return $subtype;
368             }
369              
370             1;
371              
372             __END__
373              
374             =head1 NAME
375              
376             W3C::SOAP::XSD - The parent module for generated XSD modules.
377              
378             =head1 VERSION
379              
380             This documentation refers to W3C::SOAP::XSD version 0.11.
381              
382             =head1 SYNOPSIS
383              
384             use W3C::SOAP::XSD;
385              
386             # Brief but working code example(s) here showing the most common usage(s)
387             # This section will be as far as many users bother reading, so make it as
388             # educational and exemplary as possible.
389              
390              
391             =head1 DESCRIPTION
392              
393              
394             =head1 SUBROUTINES/METHODS
395              
396             =over 4
397              
398             =item C<get_xsd_ns_name ($ns)>
399              
400             Returns the namespace name for a particular namespace.
401              
402             =item C<xml2perl_map ()>
403              
404             Returns a mapping of XML tag elements to perl attributes
405              
406             =item C<to_xml ($xml)>
407              
408             Converts the object to an L<XML::LibXML> node.
409              
410             =item C<to_data (%options)>
411              
412             Converts this object to a perl data structure. If C<$option{like_xml}> is
413             specified and true, the keys will be the same as the XML tags otherwise the
414             keys will be perl names. If C<$option{stringify}> is true and specified
415             any non XSD objects will be stringified (eg DateTime objects).
416              
417             =item C<get_xml_nodes ()>
418              
419             Returns a list of attributes of the current object that have the
420             C<W3C::SOAP::XSD> trait (which is defined in L<W3C::SOAP::XSD::Traits>)
421              
422             =item C<xsd_subtype ()>
423              
424             Helper method to create XSD subtypes that do coercions form L<XML::LibXML>
425             objects and strings.
426              
427             =back
428              
429             =head1 DIAGNOSTICS
430              
431             =head1 CONFIGURATION AND ENVIRONMENT
432              
433             =head1 DEPENDENCIES
434              
435             =head1 INCOMPATIBILITIES
436              
437             =head1 BUGS AND LIMITATIONS
438              
439             There are no known bugs in this module.
440              
441             Please report problems to Ivan Wills (ivan.wills@gmail.com).
442              
443             Patches are welcome.
444              
445             =head1 AUTHOR
446              
447             Ivan Wills - (ivan.wills@gmail.com)
448              
449             =head1 LICENSE AND COPYRIGHT
450              
451             Copyright (c) 2012 Ivan Wills (14 Mullion Close, Hornsby Heights, NSW Australia 2077).
452             All rights reserved.
453              
454             This module is free software; you can redistribute it and/or modify it under
455             the same terms as Perl itself. See L<perlartistic>. This program is
456             distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
457             without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
458             PARTICULAR PURPOSE.
459              
460             =cut