File Coverage

blib/lib/VMOMI/ComplexType.pm
Criterion Covered Total %
statement 12 12 100.0
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 16 16 100.0


line stmt bran cond sub pod time code
1             package VMOMI::ComplexType;
2              
3 1     1   1951 use strict;
  1         3  
  1         42  
4 1     1   9 use warnings;
  1         3  
  1         53  
5              
6 1     1   8 use constant P5NS => 'VMOMI';
  1         4  
  1         90  
7 1     1   6 use Scalar::Util qw(blessed);
  1         2  
  1         1349  
8              
9             sub new {
10             my ($class, %args) = @_;
11             my $self = { };
12            
13             if (%args) {
14             foreach my $name (keys %args) {
15             if ( grep { $_->[0] eq $name } $class->get_class_members ) {
16             $self->{$name} = $args{$name};
17             }
18             }
19             }
20             return bless $self, $class;
21             }
22              
23             sub AUTOLOAD {
24             my $self = shift;
25             my ($name, $class);
26             $name = our $AUTOLOAD;
27             $class = ref $self;
28              
29             return if $name =~ /::DESTROY$/;
30             $name =~ s/.*:://;
31            
32             if ( grep { $_->[0] eq $name } $class->get_class_members ) {
33             $self->{$name} = shift if @_;
34             } else {
35             Exception::Autoload->throw(
36             message => "unknown property '$name' in " . ref $self
37             );
38             }
39            
40             if (exists $self->{$name}) {
41             return $self->{$name};
42             } else {
43             return undef;
44             }
45             }
46              
47             sub deserialize {
48             my ($class, $reader, $stub) = @_;
49             my ($self, $p_depth, $p_name, $p_ntype, $p_class);
50            
51             return undef if not defined $reader;
52             $self = { };
53            
54             $p_name = $reader->name;
55             $p_depth = $reader->depth;
56             $p_ntype = $reader->nodeType;
57             $p_class = $reader->getAttributeNs(
58             'type', 'http://www.w3.org/2001/XMLSchema-instance' );
59             if (defined $p_class) {
60             $p_class = P5NS . "::$p_class";
61             } else {
62             $p_class = $class;
63             }
64              
65             while ($reader->read) {
66             my ($c_depth, $c_name, $c_ntype, $c_class, $member_info, $content, $value, $value_type,
67             $ns_class, @keyvalues);
68            
69             $c_name = $reader->name;
70             $c_depth = $reader->depth;
71             $c_ntype = $reader->nodeType;
72             $c_class = $reader->getAttributeNs(
73             'type', 'http://www.w3.org/2001/XMLSchema-instance' );
74            
75             last if ($c_name eq $p_name and $c_ntype != $p_ntype and $c_depth == $p_depth);
76             next if not $c_ntype == 1;
77            
78             ($member_info) = grep { $_->[0] eq $c_name } $p_class->get_class_members;
79             if (not defined $member_info) {
80             Exception::Deserialize->throw(
81             message => "deserialization error: undefined class member '$c_name'" .
82             " for class '$p_class'"
83             );
84             }
85            
86             if (defined $c_class) {
87             if ($c_class =~ m/boolean/) {
88             $c_class = 'boolean';
89             } elsif ($c_class =~ m/^xsd/) {
90             $c_class = undef;
91             }
92             }
93              
94             my ($m_name, $m_class, $is_array, $is_mandatory) = @$member_info;
95             if (not defined $c_class) {
96             if (defined $m_class and $m_class eq 'anyType') {
97             $c_class = undef;
98             } else {
99             $c_class = $m_class;
100             }
101             }
102              
103             if ($c_class) {
104             if ($c_class eq 'boolean') {
105             $content = $reader->readInnerXml;
106             if ($content =~ m/(true|1)/i) {
107             $value = 1;
108             } elsif ($content =~ m/(false|0)/i) {
109             $value = 0;
110             } else {
111             Exception::Deserialize->throw(
112             message => "deserialization error: server returned '$content'" .
113             " as a boolean for member '$m_name' in class '$p_class'"
114             );
115             }
116             } else {
117             # SimpleType, ComplexType
118             $ns_class = P5NS . "::$c_class";
119             $value = $ns_class->deserialize($reader, $stub);
120             }
121            
122             } else {
123             # xsd type; deserialize as string
124             $value = $reader->readInnerXml;
125             }
126            
127             # ManagedObjectReference; determine ManagedObject class and deserialize
128             if (ref $value eq P5NS . "::ManagedObjectReference") {
129             $ns_class = P5NS . "::" . $value->type;
130             # TODO: Add constructor method unique to ManagedObject for instantiation
131             $value = $ns_class->new($stub, $value);
132             }
133            
134             ## Array values are returned as references [ ]
135             if ($is_array) {
136             $self->{$m_name} = [ ] if not defined $self->{$m_name};
137             push @{ $self->{$m_name} }, $value;
138             } else {
139             $self->{$m_name} = $value;
140             }
141            
142             # Convert ArrayOf* objects to perl arrays
143             $value_type = ref $value;
144             if ($value_type =~ m/ArrayOf.*/) {
145             @keyvalues = %$value;
146             if (@keyvalues) {
147             $self->{$m_name} = pop @keyvalues;
148             }
149             }
150             }
151             return bless $self, $p_class;
152             }
153              
154             # TODO: Review the overall serialize logic, hitting a few bugs, particularly around anyType,
155             # emits and arrays?
156             sub serialize {
157             my ($self, $tag, $emit_type) = @_;
158             my ($node, @class_members, $p_class);
159            
160             $node = new XML::LibXML::Element($tag);
161             if ($emit_type) {
162             $node->setAttribute('xsi:type', $emit_type);
163             }
164            
165             $p_class = ref $self;
166              
167             ## Enumerate expected class members
168             foreach my $member_info ( $self->get_class_members ) {
169             my ($m_name, $m_class, $is_array, $is_mandatory) = @$member_info;
170             my ($m_value, @values);
171            
172             ## Coerce all member values into an array
173             if (exists $self->{$m_name}) {
174             $m_value = $self->{$m_name};
175             if (ref $m_value eq 'ARRAY') {
176             @values = @$m_value;
177             } else {
178             @values = ($m_value);
179             }
180             } else {
181             @values = ( );
182             }
183            
184             foreach my $val (@values) {
185             my ($c_node, $c_class, $c_value, $c_type);
186            
187             $c_node = new XML::LibXML::Element($m_name);
188            
189             # Add empty child node when child value is undefined
190             if (not defined $val) {
191             $node->addChild($c_node);
192             next;
193             }
194              
195             if (defined $m_class) {
196             # Boolean
197             if ($m_class eq 'boolean') {
198             if ($val =~ m/(true|1)/i) {
199             $c_value = 'true';
200             } elsif ($val =~ m/(false|0)/i) {
201             $c_value = 'false';
202             } else {
203             Exception::Serialize->throw(
204             message => "serialization error: cannot convert '$c_value' to" .
205             " boolean for member '$m_name' in class '$m_class'"
206             );
207             }
208             $c_node->appendText($c_value);
209             $node->addChild($c_node);
210             next;
211             }
212            
213             # ComplexType, SimpleType, PrimitiveType
214             $c_class = ref($val);
215             if ($m_class eq 'anyType') {
216             if ($c_class eq '') {
217             # If value is not an object, serialize as unspecified 'string'
218             $c_node->appendText($val);
219             $node->addChild($c_node);
220             next;
221             }
222             }
223            
224             if ($m_class eq 'ManagedObjectReference') {
225             if ($c_class->isa(P5NS . "::ManagedObject")) {
226             $val = $val->{'moref'};
227             }
228             }
229            
230             if (defined $c_class) {
231             $c_type = $c_class;
232             $c_type =~ s/.*:://;
233             }
234            
235             if ($c_type) {
236             $c_node = $val->serialize($m_name, $c_type);
237             } else {
238             $c_node = $val->serialize($m_name);
239             }
240             $node->addChild($c_node);
241             } else {
242             # Primitive
243             $c_node->appendText($val);
244             $node->addChild($c_node);
245             }
246             }
247             }
248             return $node;
249             }
250              
251             sub TO_JSON {
252             my $self = shift;
253             my $this = { };
254             my @ancestors = $self->get_class_ancestors();
255            
256             $this->{'_class'} = ref $self;
257             $this->{'_class'} =~ s/VMOMI:://;
258             $this->{'_ancestors'} = \@ancestors;
259              
260             # ArrayOf*
261             if ($this->{'_class'} =~ m/^ArrayOf/) {
262             # expect only one member for ArrayOf* objects
263             my ($name, $type, $is_array, $is_mandatory) = @{ $self->get_class_members() }[0];
264              
265             if (not defined $self->{$name}) {
266             return [ ];
267             } else {
268             return $self->{$name};
269             }
270             }
271             foreach ( $self->get_class_members() ) {
272             my ($name, $type, $is_array, $is_mandatory) = @$_;
273             my $val = $self->{$name};
274              
275             if (defined $val) {
276             # MOREFs are converted to ManagedObjects by p5vmomi and class members could be
277             # arrays, undefined, etc. Check for 'blessed' ManagedObjectReferences while letting
278             # other types fall through to the JSON::XS processor.
279             if ( $type eq 'ManagedObjectReference' ) {
280             if (blessed $val and $val->isa(P5NS . "::ManagedObject") ) {
281             $this->{$name} = $val->{'moref'};
282             }
283             } else {
284             $this->{$name} = $val;
285             }
286             }
287             }
288              
289             return $this;
290             }
291              
292             sub get_class_ancestors {
293             return ();
294             }
295              
296             sub get_class_members {
297             return ();
298             }
299              
300             1;