1package SOAP::GenericInputStream;
2
3use strict;
4use vars qw($VERSION);
5use SOAP::Defs;
6use SOAP::TypeMapper;
7
8$VERSION = '0.28';
9
10########################################################################
11# constructor
12########################################################################
13sub new {
14    my ($class, $typeuri, $typename, $resolver, $type_mapper) = @_;
15
16    $type_mapper ||= SOAP::TypeMapper->defaultMapper();
17
18    my $self = {
19        resolver    => $resolver,
20        diags       => 'root',
21        type_mapper => $type_mapper,
22        hash        => {},
23        text        => '',
24        has_accessors => 0,
25    };
26
27    $self->{$soapperl_intrusive_hash_key_typeuri}  = $typeuri  if $typeuri;
28    $self->{$soapperl_intrusive_hash_key_typename} = $typename if $typename;
29
30    bless $self, $class;
31}
32
33########################################################################
34# interface ISoapStream
35########################################################################
36sub content {
37#   my ($self, $text) = @_;
38    &_content;
39}
40sub simple_accessor {
41#   my ($self, $accessor_uri, $accessor_name, $typeuri, $typename, $content) = @_;
42    &_simple_accessor;
43}
44
45sub compound_accessor {
46#    my ($self, $accessor_uri, $accessor_name, $typeuri, $typename, $is_package, $resolver) = @_;
47    &_compound_accessor;
48}
49
50sub reference_accessor {
51#    my ($self, $accessor_uri, $accessor_name, $object) = @_;
52    &_reference_accessor;
53}
54
55sub forward_reference_accessor {
56#    my ($self, $accessor_uri, $accessor_name) = @_;
57    &_forward_reference_accessor;
58}
59
60sub term {
61#   my ($self) = @_;
62    &_term;
63}
64
65########################################################################
66# implementation
67########################################################################
68sub _content {
69    my ($self, $text) = @_;
70
71    $self->{text} = $text;
72}
73sub _simple_accessor {
74    my ($self, $accessor_uri, $accessor_name, $typeuri, $typename, $content) = @_;
75
76    #
77    # TBD: perform appropriate transformation based on $typename
78    #
79
80    ++$self->{has_accessors};
81
82    $self->_add_accessor($accessor_name, $content);
83}
84
85sub _compound_accessor {
86    my ($self, $accessor_uri, $accessor_name, $typeuri, $typename, $is_package, $resolver) = @_;
87
88    my $my_resolver = sub {
89        my $child_object = shift;
90        $self->_add_accessor($accessor_name, $child_object);
91        $resolver->($child_object) if $resolver;
92    };
93
94    my $stream = $self->{type_mapper}->get_deserializer($typeuri,
95                                                        $typename,
96                                                        $my_resolver);
97
98    ++$self->{has_accessors};
99
100    #
101    # DIAGS
102    #
103    {
104        my $typename_or_undef = defined($typename) ? $typename : '<undef>';
105        $stream->{diags} = "parent accessor: <$accessor_name>, type: $typename_or_undef";
106    }
107    $stream;
108}
109
110sub _reference_accessor {
111    my ($self, $accessor_uri, $accessor_name, $object) = @_;
112
113    ++$self->{has_accessors};
114
115    $self->_add_accessor($accessor_name, $object);
116}
117
118sub _forward_reference_accessor {
119    my ($self, $accessor_uri, $accessor_name) = @_;
120
121    ++$self->{has_accessors};
122
123    # return a closure to complete the transaction at a later date
124    sub { $self->_add_accessor($accessor_name, shift) };
125}
126
127sub _term {
128    my ($self) = @_;
129
130    my $text = $self->{text};
131    my $hash = $self->{hash};
132
133    #
134    # to determine whether this is a hash or a scalar node,
135    # see if there were any accessors
136    #
137    my $object;
138    if ($self->{has_accessors}) {
139	#
140	# there were accessors, so verify that there was no
141	# non-whitespace text interspersed in between them
142	#
143	if ($text =~ /\S/) {
144	    die "Found non-whitespace content between accessors: [$text]";
145	}
146	$object = $self->{hash};
147    }
148    else {
149	$object = $self->{text};
150    }
151
152    $hash->{$soapperl_intrusive_hash_key_typeuri}  = $self->{$soapperl_intrusive_hash_key_typeuri}  if exists $self->{$soapperl_intrusive_hash_key_typeuri};
153    $hash->{$soapperl_intrusive_hash_key_typename} = $self->{$soapperl_intrusive_hash_key_typename} if exists $self->{$soapperl_intrusive_hash_key_typename};
154
155    $self->{resolver}->($object);
156}
157
158#############################################################
159# misc
160#############################################################
161sub _add_accessor {
162    my ($self, $accessor_name, $object) = @_;
163
164    my $hash = $self->{hash};
165
166    if (exists $hash->{$accessor_name}) {
167        die "Duplicate accessor: $accessor_name"
168    }
169    $hash->{$accessor_name} = $object;
170}
171
1721;
173
174__END__
175
176=head1 NAME
177
178SOAP::GenericInputStream - Default handler for SOAP::Parser output
179
180=head1 SYNOPSIS
181
182    use SOAP::Parser;
183
184    my $parser = SOAP::Parser->new();
185
186    $parser->parsefile('soap.xml');
187
188    my $headers = $parser->get_headers();
189    my $body    = $parser->get_body();
190
191
192=head1 DESCRIPTION
193
194As you can see from the synopsis, you won't use SOAP::GenericInputStream
195directly, but rather the SOAP::Parser will create instances of it when
196necessary to unmarshal SOAP documents.
197
198The main reason for this documentation is to describe the interface
199exposed from SOAP::GenericInputStream because you need to implement this
200interface if you'd like to have the parser create something more exotic
201than what SOAP::GenericInputStream produces.
202
203=head2 new(TypeUri, TypeName, Resolver)
204
205TypeUri and TypeName are strings that indicate the type of object being
206unmarshaled. Resolver is a function pointer takes a single argument,
207the resulting object, and you should call through this pointer in your
208implementation of term (which means you need to store it until term is
209called). Here's an example of a minimal implementation, assuming you've
210stored the object reference in $self->{object}:
211
212    sub new {
213        my ($class, $typeuri, $typename, $resolver) = @_;
214        return bless { resolver => $resolver }, $class;
215    }
216
217    sub term {
218        my ($self) = @_;
219        $self->{resolver}->($self->{object});
220    }
221
222=head2 simple_accessor(AccessorUri, AccessorName, TypeUri, TypeName, Content)
223
224SOAP::Parser calls this function when it encounters a simple (scalar) accessor.
225You are told the uri and name of both the accessor and any xsi:type attribute.
226If the packet being unmarshaled doesn't use namespaces (this is possible but isn't
227recommended by the SOAP spec), AccessorUri will be undefined. Unless there is an
228explicit xsi:type, TypeUri and TypeName will also be undefined. So the only two
229parameters that are guaranteed to be defined are AccessorName and Content.
230
231AccessorUri and AccessorName gives the namespace and name of the element,
232and Content contains the scalar content (always a string).
233
234=head2 compound_accessor(AccessorUri, AccessorName, TypeUri, TypeName, IsPackage, Resolver)
235
236
237SOAP::Parser calls this function when it encounters a compound accessor (e.g.,
238a structured type whose value is inlined under the accessor). The first four
239parameters here are as described in simple_accessor above. IsPackage is a hint
240that tells you that this node is a package (generally you can ignore this; SOAP::Parser
241does all the work to deal with packages). Resolver may or may not be defined,
242and I'll discuss how it works shortly.
243
244NOTE NOTE NOTE: The SOAP "package" attribute was dropped when the SOAP spec
245                went from version 1.0 to version 1.1. Use package-related
246                functionality at your own risk - you may not interoperate
247                with other servers if you rely on it. I'll eventually remove
248                this feature if it doesn't reappear in the spec soon.
249
250This function must return a blessed object reference that implements the
251same interface (nothing prohibits you from simply returning $self, but since SOAP::Parser
252keeps track of these object references on a per-node basis, it's usually easier just
253to create a new instance of your class and have each instance know how to unmarshal
254a single object).
255
256If Resolver is defined, you'll need to call it when the new stream is term'd to
257communicate the resulting object reference to the Parser, so be sure to propagate
258this reference to the new stream you create to do the unmarshaling. Since you probably
259also need to be notified when the new object is created, you'll not normally hand Resolver
260directly to the new stream, but rather you'll provide your own implementation of Resolver
261that does something with the object and then chains to the Resolver passed in from the
262parser:
263
264    sub compound_accessor {
265        my ($self, $accessor_uri, $accessor_name, $typeuri, $typename, $is_package, $resolver) = @_;
266
267        my $object = $self->{object};
268
269        # create a closure to pass to the new input stream
270        my $my_resolver = sub {
271            my ($newly_unmarshaled_object) = @_;
272
273            # do something with the object yourself
274            $object->{$accessor_name} = $newly_unmarshaled_object;
275
276            # chain to the Parser's resolver if it's defined
277            $resolver->($child_object) if $resolver;
278        };
279
280        return $self->{type_mapper}->get_deserializer($typeuri, $typename, $my_resolver);
281    }
282
283=head2 reference_accessor(AccessorUri, AccessorName, Object)
284
285SOAP::Parser calls this function when it encounters a reference to an object that
286it's already unmarshaled. AccessorUri and AccessorName are the same as in simple_accessor,
287and Object is a reference to a thingy; it's basically whatever was resolved when
288another stream (perhaps one that you implemented) unmarshaled the thingy. This could
289be a blessed object reference, or simply a reference to a scalar (in SOAP it is possible
290to communicate pointers to multiref scalars). In any case, you should add this new
291reference to the object graph. Here's a simple example:
292
293    sub reference_accessor {
294        my ($self, $accessor_uri, $accessor_name, $object) = @_;
295
296        $self->{object}{$accessor_name} = $object;
297    }
298
299=head2 forward_reference_accessor(AccessorUri, AccessorName)
300
301SOAP::Parser calls this function when it encounters a reference to an object that
302has not yet been unmarshaled (a forward reference). You should return a function
303pointer that expects a single argument (the unmarshaled object). This can be as simple
304as creating a closure that simply delays a call to reference_accessor on yourself:
305
306
307    sub forward_reference_accessor {
308        my ($self, $accessor_uri, $accessor_name) = @_;
309
310        # return a closure to complete the transaction at a later date
311        return sub {
312            my ($object) = @_;
313            $self->reference_accessor($accessor_uri, $accessor_name, $object);
314        };
315    }
316
317=head2 term()
318
319SOAP::Parser calls this function when there are no more accessors for the given node.
320You are expected to call the Resolver you were passed at construction time at this point
321to pass the unmarshaled object reference to your parent. Note that due to forward
322references, the object may not be complete yet (it may have oustanding forward references
323that haven't yet been resolved). This isn't a problem, because the parse isn't finished
324yet, and as long as you've provided a resolver that fixes up these object references
325from your implementation of forward_reference_accessor, by the time the parse is complete,
326your object have all its references resolved by the parser.
327
328See the description of new() for an example implementation of this function.
329
330=head1 DEPENDENCIES
331
332SOAP::TypeMapper
333
334=head1 AUTHOR
335
336Keith Brown
337
338=head1 SEE ALSO
339
340perl(1).
341
342=cut
343