File Coverage

blib/lib/VMOMI/ComplexType.pm
Criterion Covered Total %
statement 6 6 100.0
branch n/a
condition n/a
subroutine 2 2 100.0
pod n/a
total 8 8 100.0


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