package SOAP::WSDL::Deserializer::XSD;
use strict;
use warnings;
use Class::Std::Fast::Storable;
use SOAP::WSDL::SOAP::Typelib::Fault11;
use SOAP::WSDL::Expat::MessageParser;

use version; our $VERSION = qv('2.00.99_3');

my %class_resolver_of        :ATTR(:name<class_resolver>        :default<()>);
my %response_header_parts_of :ATTR(:name<response_header_parts> :default<()>);
my %response_body_parts_of   :ATTR(:name<response_body_parts>   :default<()>);
my %strict_of                :ATTR(:get<strict> :init_arg<strict> :default<1>);
my %parser_of                :ATTR();

sub set_strict {
    undef $parser_of{${$_[0]}};
    $strict_of{${$_[0]}} = $_[1];
}

# TODO cleanup
#
# init_from_caller is currently in a pretty ugly state, due to a late
# introduction os some kind of inversion of control.
#
# init_from_caller transfers control over the initialization process from
# the SOAP::WSDL::Client to the deserializer, which can grab the information
# required from the (passed) calling object.
#
# The inversion of control is currently incomplete, because the deserializer
# cannot get the method info from the client (this is not implemented yet)
# and therefor gets passed the method info hash ref.
#
# Moreover, method names for retrieving class and name resolver are
# somewhat mixed up.
#
# Unfortunately, we cannot just change the implementataion, as it would
# require users to re-generate their interfaces.

sub init_from_caller {
    my ($self, $caller, $method) = @_;

    my $parser
        = $parser_of{ ${ $self } }
      ||= SOAP::WSDL::Expat::MessageParser->new();

    # old style
    $parser->class_resolver( $caller->get_class_resolver() )
        if $caller->can('get_class_resolver');

    # new style
    $parser->set_name_resolver( $caller->_get_name_resolver() )
        if $caller->can('get_name_resolver');

    if ( ref $method ) {
        # set class resolver if serializer supports it
        if ( $caller->isa('SOAP::WSDL::Client') ) {
            $parser->set_header_parts( $method->{header}->{response}->{parts} );
            $parser->set_body_parts( $method->{body}->{response}->{parts} );
        }
        else {
            $parser->set_header_parts( $method->{header}->{parts} );
            $parser->set_body_parts( $method->{body}->{parts} );
        }
    }
}

sub deserialize {
    my ($self, $content) = @_;

    $parser_of{ ${ $self } } = SOAP::WSDL::Expat::MessageParser->new({
        strict => $strict_of{ ${ $self } },
        # old style init variant
        (defined $class_resolver_of{${ $self }}
        ? (class_resolver => $class_resolver_of{${ $self }})
        : () )
    })
        if not $parser_of{ ${ $self } };

    eval { $parser_of{ ${ $self } }->parse_string( $content ) };
    if ($@) {
        return $self->generate_fault({
            code => 'SOAP-ENV:Server',
            role => 'urn:localhost',
            message => "Error deserializing message: $@. \n"
                       . "Message was: \n$content"
        });
    }
    return ( $parser_of{ ${ $self } }->get_data(), $parser_of{ ${ $self } }->get_header() );
}

sub generate_fault {
    my ($self, $args_from_ref) = @_;
    return SOAP::WSDL::SOAP::Typelib::Fault11->new({
            faultcode => $args_from_ref->{ code } || 'SOAP-ENV:Client',
            faultactor => $args_from_ref->{ role } || 'urn:localhost',
            faultstring => $args_from_ref->{ message } || "Unknown error"
    });
}

1;

__END__

=head1 NAME

SOAP::WSDL::Deserializer::XSD - Deserializer SOAP messages into SOAP::WSDL::XSD::Typelib:: objects

=head1 DESCRIPTION

Default deserializer for SOAP::WSDL::Client and interface classes generated by
SOAP::WSDL. Converts SOAP messages to SOAP::WSDL::XSD::Typlib:: based objects.

Needs a class_resolver typemap either passed by the generated interface
or user-provided.

SOAP::WSDL::Deserializer classes implement the API described in
L<SOAP::WSDL::Factory::Deserializer>.

=head1 USAGE

Usually you don't need to do anything to use this package - it's the default
deserializer for SOAP::WSDL::Client and interface classes generated by
SOAP::WSDL.

If you want to use the XSD serializer from SOAP::WSDL, set the outputtree()
property and provide a class_resolver.

=head1 OPTIONS

=over

=item * strict

Enables/disables strict XML processing. Strict processing is enabled by
default. To disable strict XML processing pass the following to the
constructor or use the C<set_strict> method:

 strict => 0

=back

=head1 METHODS

=head2 deserialize

Deserializes the message.

=head2 generate_fault

Generates a L<SOAP::WSDL::SOAP::Typelib::Fault11|SOAP::WSDL::SOAP::Typelib::Fault11>
object and returns it.

=head2 set_strict

Enable/disable strict XML parsing. Default is enabled.

=head1 LICENSE AND COPYRIGHT

Copyright 2004-2007 Martin Kutter.

This file is part of SOAP-WSDL. You may distribute/modify it under
the same terms as perl itself.

=head1 AUTHOR

Martin Kutter E<lt>martin.kutter fen-net.deE<gt>

=head1 REPOSITORY INFORMATION

 $Rev: 861 $
 $LastChangedBy: kutterma $
 $Id: XSD.pm 861 2010-03-28 10:41:26Z kutterma $
 $HeadURL: http://soap-wsdl.svn.sourceforge.net/svnroot/soap-wsdl/SOAP-WSDL/branches/Typemap/lib/SOAP/WSDL/Deserializer/XSD.pm $

=cut
