import SOAP-WSDL 2.00_01 from CPAN

git-cpan-module:   SOAP-WSDL
git-cpan-version:  2.00_01
git-cpan-authorid: MKUTTER
git-cpan-file:     authors/id/M/MK/MKUTTER/SOAP-WSDL-2.00_01.tar.gz
This commit is contained in:
Martin Kutter
2009-12-12 19:47:41 -08:00
committed by Michael G. Schwern
parent bfbf5e27e0
commit 6fe52c4370
108 changed files with 11670 additions and 2369 deletions
+1560 -1573
View File
File diff suppressed because it is too large Load Diff
+105
View File
@@ -0,0 +1,105 @@
package SOAP::WSDL::Base;
use strict;
use warnings;
use Carp;
use Class::Std::Storable;
my %id_of :ATTR(:name<id> :default<()>);
my %name_of :ATTR(:name<name> :default<()>);
my %targetNamespace_of :ATTR(:name<targetNamespace> :default<()>);
my %xmlns_of :ATTR(:name<xmlns> :default<{}>);
sub STORABLE_freeze_pre :CUMULATIVE {};
sub STORABLE_freeze_post :CUMULATIVE {};
sub STORABLE_thaw_pre :CUMULATIVE {};
sub STORABLE_thaw_post :CUMULATIVE { return $_[0] };
# unfortunately, AUTOMETHOD is SLOW.
# Re-implement in derived package wherever speed is an issue...
#
sub AUTOMETHOD {
my ($self, $ident, @values) = @_;
my $subname = $_; # Requested subroutine name is passed via $_
# we're called as $self->push_something(@values);
if ($subname =~s{^push_}{}xms) {
# we're not paranoid - we could be checking get_subname, too
my $getter = "get_$subname";
my $setter = "set_$subname";
croak "no set accessor found for push_$subname"
if not ($self->can( $setter ));
return sub {
no strict qw(refs);
my $old_value = $self->$getter();
# Listify if not a list ref
$old_value = $old_value ? [ $old_value ] : [] if not ref $old_value;
push @$old_value , @values;
$self->$setter( $old_value );
};
}
# we're called as $obj->find_something($ns, $key)
elsif ($subname =~s {^find_}{get_}xms) {
return sub {
my @found_at = grep {
$_->get_targetNamespace() eq $values[0] &&
$_->get_name() eq $values[1]
}
@{ $self->$subname() };
return $found_at[0];
}
}
elsif ($subname =~s {^first_}{get_}xms) {
return sub {
my $result_ref = $self->$subname();
return if not $result_ref;
return $result_ref if (not ref $result_ref eq 'ARRAY');
return $result_ref->[0];
};
}
croak "$subname not found in class " . (ref $self || $self);
}
#sub to_string :STRINGIFY {
# $_[0]->_DUMP();
#}
sub init {
my $self = shift;
my @args = @_;
foreach my $value (@args)
{
die $value if (not defined ($value->{ Name }));
if ($value->{ Name } =~m{^xmlns\:}xms) {
die $xmlns_of{ ident $self }
if ref $xmlns_of{ ident $self } ne 'HASH';
$xmlns_of{ ident $self }->{ $value->{ Value } } =
$value->{ LocalName };
next;
}
elsif ($value->{ Name } =~m{^xmlns$}xms) {
# just ignore xmlns = for now
# TODO handle xmlns correctly - maybe via setting a prefix ?
next;
}
my $name = $value->{ LocalName };
my $method = "set_$name";
$self->$method( $value->{ Value } ) if ( $method );
}
return $self;
}
sub add_namespace {
my ($self, $uri, $prefix ) = @_;
return unless $uri;
$self->{ namespace } ||= {};
$self->{ namespace }->{ $uri } = $prefix;
}
sub to_typemap {
warn "to_typemap";
return q{};
}
1;
+130
View File
@@ -0,0 +1,130 @@
package SOAP::WSDL::Binding;
use strict;
use warnings;
use Class::Std::Storable;
use base qw(SOAP::WSDL::Base);
my %operation_of :ATTR(:name<operation> :default<()>);
my %type_of :ATTR(:name<type> :default<()>);
my %transport_of :ATTR(:name<transport> :default<()>);
my %style_of :ATTR(:name<style> :default<()>);
sub explain {
my $self = shift;
my $opt = shift;
my $name = $self->get_name();
my %ns_map = reverse %{ $opt->{ wsdl }->get_xmlns() };
my ($prefix, $localname) = split /:/ , $self->get_type();
my $portType = $opt->{ wsdl }->find_portType(
$ns_map{ $prefix }, $localname
) or die "portType $prefix:$localname not found !";
my $txt = <<"EOT";
=head2 Binding name: $name
=over
=item * Style $style_of{ ident $self }
=item * Transport $transport_of{ ident $self }
=back
=head3 Operations
EOT
foreach my $operation (@{ $self->get_operation() })
{
my $operation_name = $operation->get_name();
my $operation_style = $operation->get_style() || q{};
my $port_operation = $portType->find_operation( $ns_map{ $prefix },
$operation->get_name() )
or die "operation not found:" . $operation->get_name();
# TODO rename lexical $input to "message"
my $input_message = do {
my $input = $port_operation->first_input();
$input ? $input->explain($opt) : q{};
};
my $output_message = do {
my $input = $port_operation->first_output();
$input ? $input->explain($opt) : q{};
};
my $fault_message = do {
my $input = $port_operation->first_fault();
$input ? $input->explain($opt) : q{};
};
$txt .= <<"EOT";
=over
=item * $operation_name
=over 8
=item * Style: $operation_style
=item * Input Message:
$input_message
=item * Output Message:
$output_message
=item * Fault:
$fault_message
=back
=back
EOT
}
return $txt;
}
sub to_typemap {
my ($self, $opt) = @_;
my $name = $self->get_name();
my %ns_map = reverse %{ $opt->{ wsdl }->get_xmlns() };
my ($prefix, $localname) = split /:/ , $self->get_type();
my $portType = $opt->{ wsdl }->find_portType(
$ns_map{ $prefix }, $localname
) or die "portType $prefix:$localname not found !";
my $txt = q{};
foreach my $operation (@{ $self->get_operation() })
{
my $operation_name = $operation->get_name();
my $operation_style = $operation->get_style() || q{};
my $port_operation = $portType->find_operation( $ns_map{ $prefix },
$operation->get_name() )
or die "operation not found:" . $operation->get_name();
# TODO rename lexical $input to "message"
$txt .= do {
my $input = $port_operation->first_input();
$input ? $input->to_typemap($opt) : q{};
};
$txt .= do {
my $input = $port_operation->first_output();
$input ? $input->to_typemap($opt) : q{};
};
$txt .= do {
my $input = $port_operation->first_fault();
$input ? $input->to_typemap($opt) : q{};
};
}
return $txt;
}
1;
+446
View File
@@ -0,0 +1,446 @@
package SOAP::WSDL::Client;
use strict;
use warnings;
use vars qw/$AUTOLOAD/;
use Scalar::Util qw(blessed);
use SOAP::WSDL::Envelope;
use SOAP::WSDL::SAX::WSDLHandler;
BEGIN {
eval {
use XML::LibXML;
};
if ($@) {
use XML::SAX::ParserFactory;
}
}
use base qw/SOAP::Lite/;
sub outputtree {
my $self = shift;
return $self->{ _WSDL }->{ outputtree } if not @_;
return $self->{ _WSDL }->{ outputtree } = shift;
}
sub class_resolver {
my $self = shift;
return $self->{ _WSDL }->{ class_resolver } if not @_;
return $self->{ _WSDL }->{ class_resolver } = shift;
}
sub wsdlinit
{
my $self = shift;
my $wsdl_xml = SOAP::Schema->new( schema_url => $self->wsdl() )->access(
$self->wsdl()
);
my $filter;
my $parser = eval { XML::LibXML->new() };
if ($parser) {
$filter = SOAP::WSDL::SAX::WSDLHandler->new();
$parser->set_handler( $filter );
}
else {
$filter = SOAP::WSDL::SAX::WSDLHandler->new( base => 'XML::SAX::Base' );
$parser = XML::SAX::ParserFactory->parser( Handler => $filter );
}
$parser->parse_string( $wsdl_xml );
my $wsdl_definitions = $filter->get_data()
or die "unable to parse WSDL";
my $types = $wsdl_definitions->first_types()
or die "unable to extract schema from WSDL";
my $ns = $wsdl_definitions->get_xmlns()
or die "unable to extract XML Namespaces" . $wsdl_definitions->to_string;
( %{ $ns } ) or die "unable to extract XML Namespaces";
# setup lookup variables
$self->{ _WSDL }->{ wsdl_definitions } = $wsdl_definitions;
$self->{ _WSDL }->{ serialize_options } = {
autotype => 0,
readable => 1,
typelib => $types,
namespace => $ns,
};
$self->{ _WSDL }->{ explain_options } = {
readable => 1,
wsdl => $wsdl_definitions,
namespace => $ns,
typelib => $types,
};
return $self;
} ## end sub wsdlinit
sub _wsdl_get_service
{
my $self = shift;
my $service;
my $wsdl = $self->{ _WSDL }->{ wsdl_definitions };
my $ns = $wsdl->get_targetNamespace();
if ( $self->{ _WSDL }->{ servicename } )
{
$service =
$wsdl->find_service( $ns, $self->{ _WSDL }->{ servicename } );
}
else
{
$service = $wsdl->get_service()->[ 0 ];
warn "no servicename specified - using " . $service->get_name();
}
return $self->{ _WSDL }->{ service } = $service;
} ## end sub _wsdl_get_service
sub _wsdl_get_port
{
my $self = shift;
my $service = $self->{ _WSDL }->{ service }
|| $self->_wsdl_get_service();
my $wsdl = $self->{ _WSDL }->{ wsdl_definitions };
my $ns = $wsdl->get_targetNamespace();
my $port;
if ( $self->{ _WSDL }->{ portname } )
{
$port = $service->get_port( $ns, $self->{ _WSDL }->{ portname } );
}
else
{
$port = $service->get_port()->[ 0 ];
}
$self->{ _WSDL }->{ port } = $port;
# preload portType
$self->_wsdl_get_portType();
# Auto-set proxy - required before issuing call()
$self->proxy( $port->get_location() );
return $port;
} ## end sub _wsdl_get_port
sub _wsdl_get_binding
{
my $self = shift;
my $wsdl = $self->{ _WSDL }->{ wsdl_definitions };
my $ns = $wsdl->get_targetNamespace();
my $port = $self->{ _WSDL }->{ port }
|| $self->_wsdl_get_port();
my ( $prefix, $localname ) = split /:/, $port->get_binding();
# TODO lookup $ns instead of just using
# the top element's targetns...
my $binding = $wsdl->find_binding( $ns, $localname )
or die "no binding found for ", $port->get_binding();
return $self->{ _WSDL }->{ binding } = $binding;
} ## end sub _wsdl_get_binding
sub _wsdl_get_portType
{
my $self = shift;
my $wsdl = $self->{ _WSDL }->{ wsdl_definitions };
my $binding = $self->{ _WSDL }->{ binding }
|| $self->_wsdl_get_binding();
my $ns = $wsdl->get_targetNamespace();
my ( $prefix, $localname ) = split /:/, $binding->get_type();
my $portType = $wsdl->find_portType( $ns, $localname );
$self->{ _WSDL }->{ portType } = $portType;
return $portType;
} ## end sub _wsdl_get_portType
=pod
=head2 _wsdl_init_methods
=over
=item DESCRIPTION
Creates a lookup table containing the information required for all methods
specified for the service/port selected.
The lookup table is used by L<call|call>.
=back
=cut
sub _wsdl_init_methods {
my $self = shift;
my $wsdl = $self->{ _WSDL }->{ wsdl_definitions };
my $ns = $wsdl->get_targetNamespace();
# get bindings, portType, message, part(s)
# - use cached values where possible for speed,
# private methods if not for clear separation...
my $binding = $self->{ _WSDL }->{ binding }
|| $self->_wsdl_get_binding();
my $portType = $self->{ _WSDL }->{ portType }
|| $self->_wsdl_get_portType();
my $methodHashRef = {};
foreach my $binding_operation (@{ $binding->get_operation() })
{
my $method = {};
# get SOAP Action
# SOAP-Action is a required HTTP Header, so we need to look it up...
my $soap_binding_operation = $binding_operation->get_operation()->[0];
$method->{ soap_action } = $soap_binding_operation ?
$soap_binding_operation->get_soapAction() : $method;
# get parts
# 1. get operation from port
my $operation = $portType->find_operation( $ns,
$binding_operation->get_name() );
# 2. get input message name
my ( $prefix, $localname ) = split /:/,
$operation->get_input()->[0]->get_message();
# 3. get input message
my $message = $wsdl->find_message( $ns, $localname );
$method->{ parts } = $message->get_part();
# rpc / encoded methods may have a namespace specified.
# look it up and set it...
$method->{ namespace } = $binding_operation ?
$binding_operation->get_input()->[0]->get_namespace() : undef;
$methodHashRef->{ $binding_operation->get_name() } = $method;
}
$self->{ _WSDL }->{ methodInfo } = $methodHashRef;
return $methodHashRef;
}
sub call {
my $self = shift;
my $method = shift;
my $data = ref $_[0] ? $_[0] : { @_ };
my $content = q{};
my $envelope;
my $methodInfo;
if (blessed $data
&& $data->isa('SOAP::WSDL::XSD::Typelib::Builtin::anyType'))
{
$envelope = SOAP::WSDL::Envelope->serialize( $method, $data );
# TODO replace by something derived from binding - this is just a
# workaround...
$methodInfo->{ soap_action }
= join '/', $data->get_xmlns(), $method;
}
else {
my $methodLookup = $self->{ _WSDL }->{ methodInfo }
|| $self->_wsdl_init_methods();
$methodInfo = $methodLookup->{ $method };
my $partListRef = $methodInfo->{ parts };
# set serializer options
# TODO allow custom options here
my $opt = $self->{ _WSDL }->{ serialize_options };
# set response target namespace
# TODO make rpc-encoded encoding recognise this namespace
# $opt->{ targetNamespace } = $soap_binding_operation ?
# $operation->input()->namespace() : undef;
# serialize content
# TODO create surrounding element for rpc-encoded messages
foreach my $part ( @{ $partListRef } )
{
$content .= $part->serialize( $method, $data, $opt );
}
$envelope = SOAP::WSDL::Envelope->serialize(
$method, $content , $opt );
};
if ( $self->no_dispatch() )
{
return $envelope;
} ## end if ( $self->no_dispatch...
# get response via transport layer
# TODO remove dependency from SOAP::Lite and use a
# SAX-based filter using XML::LibXML to get the
# result.
# Filter should have the following methods:
# - result: returns the result of the call (like SOAP::Lite, but as
# perl data structure)
# - header: returns the content of the SOAP header
# - fault: returns the result of the call if a SOAP fault is sent back
# by the server. Retuns undef (nothing) if the call has been
# processed without errors.
my $response = $self->transport->send_receive(
context => $self, # this is provided for context
endpoint => $self->endpoint(),
action => $methodInfo->{ soap_action }, # SOAPAction from binding
envelope => $envelope, # use custom content
);
return $response if ($self->outputxml() );
if ($self->outputtree()) {
my ($parser, $handler); # replace by globals - singleton is faster
if (not $parser) {
require SOAP::WSDL::SOAP::Typelib::Fault11;
require SOAP::WSDL::SAX::MessageHandler;
require XML::LibXML;
$handler = SOAP::WSDL::SAX::MessageHandler->new(
{ class_resolver => $self->class_resolver() },
);
$parser = XML::LibXML->new();
$parser->set_handler( $handler);
}
# if we had no success (Transport layer error status code)
# or if transport layer failed
if (! $self->transport->is_success() ) {
# Try deserializing response - there may be some
if ($response) {
eval { $parser->parse_string( $response ) };
return $handler->get_data if not $@;
};
# generate & return fault if we cannot serialize response
# or have none...
return SOAP::WSDL::SOAP::Typelib::Fault11->new({
faultcode => 'soap:Server',
faultactor => 'urn:localhost',
faultstring => 'Error sending / receiving message: '
. $self->transport->message()
});
}
eval { $parser->parse_string( $response ) };
# return fault if we cannot deserialize response
if ($@) {
return SOAP::WSDL::SOAP::Typelib::Fault11->new({
faultcode => 'soap:Server',
faultactor => 'urn:localhost',
faultstring => "Error deserializing message: $@. \n"
. "Message was: \n$response"
});
}
return $handler->get_data();
}
# deserialize and store result
my $result = $self->{ '_call' } =
eval { $self->deserializer->deserialize( $response ) }
if $response;
if (
!$self->transport->is_success || # transport fault
$@ || # not deserializible
# fault message even if transport OK
# or no transport error (for example, fo TCP, POP3, IO implementations)
UNIVERSAL::isa( $result => 'SOAP::SOM' ) && $result->fault
)
{
return $self->{ '_call' } = (
$self->on_fault->(
$self, $@ ? $@ . ( $response || '' ) : $result
)
|| $result
);
# ? # trick editors
} ## end if ( !$self->transport...
return unless $response; # nothing to do for one-ways
return $result;
} ## end sub call
sub explain
{
my $self = shift;
my $opt = $self->{ _WSDL }->{ explain_options };
return $self->{ _WSDL }->{ wsdl_definitions }->explain( $opt );
} ## end sub explain
sub _load_method
{
my $method = shift;
no strict "refs";
*$method = sub {
my $self = shift;
return ( @_ ) ? $self->{ _WSDL }->{ $method } = shift
: $self->{ _WSDL }->{ $method }
};
} ## end sub _load_method
&_load_method( 'no_dispatch' );
&_load_method( 'wsdl' );
sub servicename
{
my $self = shift;
return $self->{ _WSDL }->{ servicename } if ( not @_ );
$self->{ _WSDL }->{ servicename } = shift;
my $ns = $self->{ _WSDL }->{ wsdl_definitions }->get_targetNamespace();
$self->{ _WSDL }->{ service } =
$self->{ _WSDL }->{ wsdl_definitions }
->find_service( $ns, $self->{ _WSDL }->{ servicename } )
or die "No such service: " . $self->{ _WSDL }->{ servicename };
} ## end sub servicename
sub portname
{
my $self = shift;
return $self->{ _WSDL }->{ portname } if ( not @_ );
$self->{ _WSDL }->{ portname } = shift;
my $ns = $self->{ _WSDL }->{ wsdl_definitions }->targetNamespace();
$self->{ _WSDL }->{ port } =
$self->{ _WSDL }->{ service }
->get_port( $ns, $self->{ _WSDL }->{ portname } )
or die "No such port: " . $self->{ _WSDL }->{ portname };
} ## end sub portname
=pod
=head1 Auto-Dispatching
SOAP::WSDL::Client does B<does not> support auto-dispatching.
This is on purpose: You may easily create interface classes by using
SOAP::WSDL::Client and implementing something like
sub mySoapMethod {
my $self = shift;
$soap_wsdl_client->call( mySoapMethod, @_);
}
You may even do this in a class factory - SOAP::WSDL provides the methods
for generating such interfaces.
SOAP::Lite's autodispatching mechanism is - though convenient - a constant
source of errors: Every typo in a method name gets caught by AUTOLOAD and
may lead to unpredictable results.
=cut
sub AUTOLOAD
{
my $method = substr($AUTOLOAD, rindex($AUTOLOAD, '::') + 2);
die "$method not found";
}
+127
View File
@@ -0,0 +1,127 @@
#!/usr/bin/perl -w
package SOAP::WSDL::Client::Base;
##################################################################################
## <OWNER>Internetteam
## <AUTHOR>Martin Kutter <martin.kutter@siemens.com>
## <CREATIONDATE>25.10.2006
##
## <FUNCTION>Base client for WSDL-based SOAP access
## Automatisch gefüllt:
## <CVSPROJECT>$HeadURL:$
## <REVISION>$Revision:$
################################################################################
use strict;
use Log::Log4perl;
use Class::Accessor;
use base qw/Class::Accessor/;
$SOAP::WSDL::Client::Base::VERSION = sprintf("0.%d", q$LastChangedRevision: 1$ =~/(\d+)/ );
__PACKAGE__->mk_accessors(
qw//
);
my $log = undef; # Global logger to speed up performance
=pod
=head1 NAME
SOAP::WSDL::Client::Base - Base client for WSDL-based SOAP access
=head1 SYNOPSIS
use SOAP::WSDL::Client::Base;
# TODO Add more Synopsis information
=head1 DESCRIPTION
# TODO Add Description
=cut
=pod
=head2 new
=over
=item SYNOPSIS
my $obj = ->new();
=item DESCRIPTION
Constructor.
=back
=cut
sub new
{
my $proto = shift;
my $class = ref $proto || $proto;
my $self = {
soapBindingStyle => 'rpc',
};
bless $self, $class;
$self->init(@_);
return $self;
}
sub soapBindingStyle
{
my $self = shift;
my $style = shift;
if ($style)
{
die "Binding style must be one of rpc|document"
if (not( $style=~m/^(rpc|document)$/));
$self->{ soapBindingStyle } = $style;
}
return $self->{ soapBindingStyle };
}
sub init
{
}
sub call
{
my $self = shift;
my $method = shift;
my $data = shift;
my $content;
}
1;
__END__
=pod
=head1 AUTHOR
Martin Kutter <martin.kutter@siemens.com>
=head1 COPYING
Copyright (c) 2005 SIEMENS AG. All rights reserved.
=head1 Repository information
$ID: $
$LastChangedDate: $
$LastChangedRevision: $
$LastChangedBy: $
$HeadURL: $
=cut
+62
View File
@@ -0,0 +1,62 @@
package SOAP::WSDL::Definitions;
use strict;
use warnings;
use Class::Std::Storable;
use base qw(SOAP::WSDL::Base);
my %types_of :ATTR(:name<types> :default<[]>);
my %message_of :ATTR(:name<message> :default<()>);
my %portType_of :ATTR(:name<portType> :default<()>);
my %binding_of :ATTR(:name<binding> :default<()>);
my %service_of :ATTR(:name<service> :default<()>);
my %namespace_of :ATTR(:name<namespace> :default<()>);
my %attributes_of :ATTR();
%attributes_of = (
binding => \%binding_of,
message => \%message_of,
portType => \%portType_of,
service => \%service_of,
);
# Function factory - we could be writing this method for all %attribute
# keys, too, but that's just C&P (eehm, Copy & Paste...)
foreach my $method(keys %attributes_of ) {
no strict qw/refs/;
# ... btw, we mean this method here...
*{ "find_$method" } = sub {
my ($self, @args) = @_;
my @found_at = grep {
$_->get_targetNamespace() eq $args[0] &&
$_->get_name() eq $args[1]
}
@{ $attributes_of{ $method }->{ ident $self } };
return $found_at[0];
};
}
sub explain {
my $self = shift;
my $opt = shift;
my $txt = '';
foreach my $service (@{ $self->get_service() })
{
$txt .= $service->explain( $opt );
$txt .= "\n";
}
return $txt;
}
sub to_typemap {
my $self = shift;
my $opt = shift;
$opt->{ wsdl } ||= $self;
$opt->{ prefix } ||= q{};
return join "\n",
map { $_->to_typemap( $opt ) } @{ $service_of{ ident $self } };
}
1;
+66
View File
@@ -0,0 +1,66 @@
#!/usr/bin/perl -w
package SOAP::WSDL::Envelope;
use strict;
use base qw/SOAP::WSDL::Base/;
my $SOAP_NS = 'http://schemas.xmlsoap.org/soap/envelope/';
my $XML_INSTANCE_NS = 'http://www.w3.org/2001/XMLSchema-instance';
sub serialize {
my ($self, $name, $data, $opt) = @_;
if (not $opt->{ namespace }->{ $SOAP_NS })
{
$opt->{ namespace }->{ $SOAP_NS } = 'SOAP-ENV';
}
if (not $opt->{ namespace }->{ $XML_INSTANCE_NS })
{
$opt->{ namespace }->{ $XML_INSTANCE_NS } = 'xsi';
}
my $soap_prefix = $opt->{ namespace }->{ $SOAP_NS };
# envelope start with namespaces
my $xml = "<$soap_prefix\:Envelope ";
while (my ($uri, $prefix) = each %{ $opt->{ namespace } })
{
$xml .= "\n\t" if ($opt->{'readable'});
$xml .= "xmlns:$prefix=\"$uri\" ";
}
# TODO insert encoding
$xml.='>';
$xml .= $self->serialize_header($name, $data, $opt);
$xml .= $self->serialize_body($name, $data, $opt);
$xml .= "\n" if ($opt->{ readable });
$xml .= '</' . $soap_prefix .':Envelope>';
$xml .= "\n" if ($opt->{ readable });
return $xml;
}
sub serialize_header {
my $xml = '';
return $xml;
}
sub serialize_body {
my $self = shift;
my $name = shift;
my $data = shift;
my $opt = shift;
my $soap_prefix = $opt->{ namespace }->{ $SOAP_NS };
my $xml = '';
$xml .= "\n" if ($opt->{ readable });
$xml .= "<$soap_prefix\:Body>";
$xml .= "\n" if ($opt->{ readable });
# include parts
$xml .= $data if ( defined($data) );
$xml .= "</$soap_prefix\:Body>";
return $xml;
}
+9
View File
@@ -0,0 +1,9 @@
package SOAP::WSDL::Message;
use strict;
use warnings;
use Class::Std::Storable;
use base qw(SOAP::WSDL::Base);
my %part_of :ATTR(:name<part> :default<[]>);
1;
+67
View File
@@ -0,0 +1,67 @@
package SOAP::WSDL::OpMessage;
use strict;
use warnings;
use Class::Std::Storable;
use base qw(SOAP::WSDL::Base);
my %body_of :ATTR(:name<body> :default<()>);
my %message_of :ATTR(:name<message> :default<()>);
my %use_of :ATTR(:name<use> :default<()>);
my %namespace :ATTR(:name<namespace> :default<()>);
my %encodingStyle_of :ATTR(:name<encodingStyle> :default<()>);
sub explain
{
my $self = shift;
my $opt = shift;
my $name = shift;
my $txt = '';
my %ns_map = reverse %{ $opt->{ wsdl }->get_xmlns() };
if ( $self->get_message() ) {
my ($prefix, $localname) = split /:/ , $self->get_message();
# TODO allow more messages && overloading by specifying name
my $message = $opt->{ wsdl }->get_message(
$ns_map{ $prefix }, $localname
);
for my $part(@{ $message->[0]->get_part() }) {
$opt->{ indent } .= "\t";
$txt .= $part->explain($opt);
$opt->{ indent } =~s/\t//;
$txt .= $opt->{ indent } . "\n";
}
}
else
{
if ($self->use())
{
$txt .= $opt->{ indent } . "$name use: " . $self->use(). "\n";
}
}
return $txt;
}
sub to_typemap {
my ($self, $opt) = @_;
my $txt = q{};
return q{} if not ( $self->get_message() ); # we're in binding
my %ns_map = reverse %{ $opt->{ wsdl }->get_xmlns() };
my ($prefix, $localname) = split /:/ , $self->get_message();
# TODO allow more messages && overloading by specifying name
my $message = $opt->{ wsdl }->find_message(
$ns_map{ $prefix }, $localname
);
for my $part(@{ $message->get_part() }) {
$txt .= $part->to_typemap($opt);
}
return $txt;
}
1;
+18
View File
@@ -0,0 +1,18 @@
package SOAP::WSDL::Operation;
use strict;
use warnings;
use Class::Std::Storable;
use base qw/SOAP::WSDL::Base/;
my %operation_of :ATTR(:name<operation> :default<()>);
my %input_of :ATTR(:name<input> :default<()>);
my %output_of :ATTR(:name<output> :default<()>);
my %fault_of :ATTR(:name<fault> :default<()>);
my %type_of :ATTR(:name<type> :default<()>);
my %style_of :ATTR(:name<style> :default<()>);
my %transport_of :ATTR(:name<transport> :default<()>);
my %parameterOrder_of :ATTR(:name<parameterOrder> :default<()>);
1;
+98
View File
@@ -0,0 +1,98 @@
package SOAP::WSDL::Part;
use strict;
use warnings;
use Class::Std::Storable;
use base qw(SOAP::WSDL::Base);
my %element_of :ATTR(:name<element> :default<()>);
my %type_of :ATTR(:name<type> :default<()>);
sub serialize
{
my $self = shift;
my $name = shift;
my $data = shift;
my $opt = shift;
my $typelib = $opt->{ typelib } || die "No typelib";
my %ns_map = reverse %{ $opt->{ namespace } };
my $item_name;
if ($item_name = $self->get_type() )
{
# resolve type
my ($prefix, $localname) = split /:/ , $item_name, 2;
my $type = $typelib->find_type(
$ns_map{ $prefix },
$localname
);
return $type->serialize( $self->get_name(), $data, $opt );
}
elsif ( $item_name = $self->get_element() )
{
my ($prefix, $localname) = split /:/ , $item_name, 2;
my $element = $typelib->find_element(
$ns_map{ $prefix },
$localname
);
return $element->serialize( undef, $data, $opt );
}
die "Neither type nor element - don't know what to do";
}
sub explain {
my ($self, $opt, $name ) = @_;
my $typelib = $opt->{ wsdl }->first_types()
|| die "No typelib";
my %ns_map = reverse %{ $opt->{ namespace } };
my $element = $self->get_type() || $self->get_element();
# resolve type
my ($prefix, $localname) = split /:/ , $element;
my $type = $typelib->find_type(
$ns_map{ $prefix },
$localname
)
|| $typelib->find_element(
$ns_map{ $prefix },
$localname
);
if (not $type)
{
warn "no type/element $element ({ $ns_map{ $prefix } }$localname) found for part " . $self->get_name();
return q{};
}
return $type->explain( $opt, $self->get_name() );
}
sub to_typemap {
my ($self, $opt, $name ) = @_;
my $txt = q{};
my $typelib = $opt->{ wsdl }->first_types()
|| die "No typelib";
my %ns_map = reverse %{ $opt->{ wsdl }->get_xmlns() };
my $element = $self->get_type() || $self->get_element();
# resolve type
my ($prefix, $localname) = split /:/ , $element;
my $type;
if ($type = $typelib->find_type( $ns_map{ $prefix }, $localname ) ) {
$txt .= "'/' => " . $type->get_name() . "\n";
}
else {
$type = $typelib->find_element( $ns_map{ $prefix }, $localname );
}
if (not $type) {
warn "no type/element $element ({ $ns_map{ $prefix } }$localname) found for part " . $self->get_name();
return q{};
}
$opt->{ path } = [];
$txt .= $type->to_typemap( $opt, $self->get_name() );
return $txt;
}
1;
+46
View File
@@ -0,0 +1,46 @@
package SOAP::WSDL::Port;
use strict;
use warnings;
use Class::Std::Storable;
use base qw(SOAP::WSDL::Base);
my %binding_of :ATTR(:name<binding> :default<()>);
my %location_of :ATTR(:name<location> :default<()>);
sub explain {
my $self = shift;
my $opt = shift;
my $txt =
"=head2 Port name: " . $self->get_name() . "\n\n"
. "=over\n\n"
. "=item * Binding: " . $self->get_binding() ."\n\n"
. "=item * Location: " . $self->get_location() ."\n\n"
. "=back\n\n";
# if ( $self->location() );
my %ns_map = reverse %{ $opt->{ namespace } };
my ($prefix, $localname) = split /:/ , $self->get_binding();
my $binding = $opt->{ wsdl }->find_binding(
$ns_map{ $prefix }, $localname
) or die "binding $prefix:$localname not found !";
$txt .= $binding->explain($opt);
return $txt;
}
sub to_typemap {
my $self = shift;
my $opt = shift;
my %ns_map = reverse %{ $opt->{ wsdl }->get_xmlns() };
my ($prefix, $localname) = split /:/ , $self->get_binding();
my $binding = $opt->{ wsdl }->find_binding(
$ns_map{ $prefix }, $localname
) or die "binding $prefix:$localname not found !";
return $binding->to_typemap($opt);
}
1;
+32
View File
@@ -0,0 +1,32 @@
package SOAP::WSDL::PortType;
use strict;
use warnings;
use Class::Std::Storable;
use base qw(SOAP::WSDL::Base);
my %operation_of :ATTR(:name<operation> :default<()>);
my %attributes_of :ATTR();
%attributes_of = (
operation => \%operation_of,
);
# Function factory - we could be writing this method for all %attribute
# keys, too, but that's just C&P (eehm, Copy & Paste...)
foreach my $method(keys %attributes_of ) {
no strict qw/refs/;
# ... btw, we mean this method here...
*{ "find_$method" } = sub {
my ($self, @args) = @_;
my @found_at = grep {
$_->get_targetNamespace() eq $args[0] &&
$_->get_name() eq $args[1]
}
@{ $attributes_of{ $method }->{ ident $self } };
return $found_at[0];
};
}
1;
+373
View File
@@ -0,0 +1,373 @@
#!/usr/bin/perl
package SOAP::WSDL::SAX::MessageHandler;
use strict;
use warnings;
use Scalar::Util qw(blessed);
use Class::Std::Storable;
use SOAP::WSDL::XSD::Typelib::Builtin;
my %characters_of :ATTR(:default<()>);
my %class_resolver_of :ATTR(:default<()> :init_attr<class_resolver>);
my %current_of :ATTR(:default<()>);
my %ignore_of :ATTR(:default<()>);
my %list_of :ATTR(:default<()>);
my %namespace_of :ATTR(:default<()>);
my %path_of :ATTR(:default<()>);
my %data_of :ATTR(:default<()>);
{
# we have to implement our own new - we need a blessed Hash ref as $self
# for being able to inherit from XML::SAX::Base...
no warnings qw(redefine);
sub new {
my $class = shift;
my $self = {}; # $class->SUPER::new(@_);
my $args = shift || {};
die "arguments to new must be single hash ref"
if @_ or ! ref $args eq 'HASH';
# nasty, but for those who want to use XML::SAX::Base or similar
# as parser factory
if ($args->{base}) {
# yup, naughty string eval
eval "use base qw($args->{base})"; ## no critic qw(ProhibitStringyEval)
}
else {
# create all those SAX methods...
# ...we ignore em all...
no strict qw(refs);
foreach my $method ( qw(
processing_instruction
ignorable_whitespace
set_document_locator
start_prefix_mapping
end_prefix_mapping
skipped_entity
start_cdata
end_cdata
comment
entity_reference
notation_decl
unparsed_entity_decl
element_decl
attlist_decl
doctype_decl
xml_decl
entity_decl
attribute_decl
internal_entity_decl
external_entity_decl
resolve_entity
start_dtd
end_dtd
start_entity
end_entity
warning
) ) {
*{ "$method" } = sub {};
}
}
$class_resolver_of{ ident $self } = $args->{ class_resolver }
or die "cannot parse message without type resolver";
return bless $self, $class;
}
}
sub start_document {
my $ident = ident $_[0];
$list_of{ $ident } = [];
$current_of{ $ident } = '__STOP__'; # use as marker
$namespace_of{ $ident } = {};
$ignore_of{ $ident } = [ qw(Envelope Body) ]; # SOAP elements
$path_of{ $ident } = [];
$data_of{ $ident } = undef;
}
sub start_element {
# use $_[n] for performance
my ($ident, $element) = (ident $_[0], $_[1]);
my $local_name = $element->{ LocalName };
# ignore top level elements
if (@{ $ignore_of{ $ident } }
&& $local_name eq $ignore_of{ $ident }->[0]) {
shift @{ $ignore_of{ $ident } };
return;
}
# empty characters
$characters_of{ $ident } = q{};
push @{ $path_of{ $ident } }, $local_name; # step down...
push @{ $list_of{ $ident } }, $current_of{ $ident }; # remember current
# resolve class of this element
my $class = $class_resolver_of{ $ident }->get_class( $path_of{ $ident } )
or die "Cannot resolve class for "
. join('/', @{ $path_of{ $ident } })
. " via "
. $class_resolver_of{ $ident };
# Check whether we have a primitive - we implement them as classes
# TODO replace with UNIVERSAL->isa() or maybe index - could be faster
# than m//
# TODO
if (not $class=~m{^SOAP::WSDL::XSD::Typelib::Builtin}xms) {
eval "require $class"; ## no critic qw(ProhibitStringyEval)
die $@ if $@;
}
# create object
my $obj = $class->new({
map { $_->{ Name } => $_->{ Value } }
values %{ $element->{ Attributes } }
});
# set current object
$current_of{ $ident } = $obj;
# remember top level element
$data_of{ $ident } = $obj if not defined $data_of{ $ident };
}
sub characters {
$characters_of{ ident $_[0] } .= $_[1]->{ Data };
}
sub end_element {
# $_[n] used for performance
my ($ident, $element) = (ident $_[0], $_[1]);
# This one easily handles ignores for us, too...
return if $list_of{ $ident }->[-1] eq '__STOP__';
if ( $current_of{ $ident }
->isa('SOAP::WSDL::XSD::Typelib::Builtin::anySimpleType') ) {
$current_of{ $ident }->set_value( $characters_of{ $ident } );
}
# set appropriate attribute in last element
# multiple values must be implemented in base class
my $method = "add_$element->{ LocalName }";
$list_of{ $ident }->[-1]->$method( $current_of{ $ident } );
# step up in path
pop @{ $path_of{ $ident } };
# step up in object hierarchy...
$current_of{ $ident } = pop @{ $list_of{ $ident } };
}
sub end_document {
my $self = shift;
my $ident = ident $self;
# destroy all remains except data_of
$list_of{ $ident } = ();
$namespace_of{ $ident } = ();
$ignore_of{ $ident } = ();
$path_of{ $ident } = ();
$characters_of{ $ident } = ();
}
sub get_data {
my $self = shift;
return $data_of{ ident $self };
}
sub fatal_error {
my $self = shift;
die "Fatal error parsing document: " , @_;
}
sub error {
my $self = shift;
die "Error parsing document: " , @_;
}
1;
=pod
=head1 NAME
SOAP::WSDL::SAX::MessageHandler - Convert SOAP messages to custom object trees
=head1 SYNOPSIS
# this is the direct variant, recommended for performance
use SOAP::WSDL::SAX::MessageHandler;
use XML::LibXML;
my $filter = SOAP::WSDL::SAX::MessageHandler->new( {
class_resolver => FakeResolver->new()
), "Object creation");
my $parser = XML::LibXML->new();
$parser->set_handler( $filter );
$parser->parse_string( $soap_message );
my $object_tree = $filter->get_data();
# This is the XML::ParserFactory variant - for those who want other
# parsers than XML::Simple....
use SOAP::WSDL::SAX::MessageHandler;
use XML::SAX::ParserFactory;
my $filter = SOAP::WSDL::SAX::MessageHandler->new( {
class_resolver => FakeResolver->new(),
base => 'XML::SAX::Base',
), "Object creation");
my $parser = XML::LibXML->new();
$parser->set_handler( $filter );
$parser->parse_string( $soap_message );
my $object_tree = $filter->get_data();
=head1 DESCRIPTION
Parses a SOAP message into an object tree.
For every element in the SOAP message, an object is created. The class
of the object is determined via a Resolver object which has to be passed
to new via the class_resolver parameter.
=head1 Writing a class resolver
The class resolver must returned a method "get_class", which is passed a list
ref of the current element's XPath (relative to Body), split by /.
This method must return a class name appropriate for a XML element.
A class resolver package might look like this:
package FakeResolver;
my %class_list = (
'EnqueueMessage' => 'Typelib::TEnqueueMessage',
'EnqueueMessage/MMessage' => 'Typelib::TMessage',
'EnqueueMessage/MMessage/MRecipientURI' => 'SOAP::WSDL::XSD::Builtin::anyURI',
'EnqueueMessage/MMessage/MMessageContent' => 'SOAP::WSDL::XSD::Builtin::string',
);
sub new { return bless {}, 'FakeResolver' };
sub get_class {
my $name = join('/', @{ $_[1] });
return ($class_list{ $name }) ? $class_list{ $name }
: warn "no class found for $name";
};
1;
=head1 Writing type library classes
Every element must have a correspondent one in the type library.
Type library classes must provide the following methods:
Builtin types should be resolved as SOAP::WSDL::XSD::Builtin::* classes
=over
=item * new
Constructor
=item * add_FOO
The add_FOO method is called for every child element of the XML node.
Characters are regarded as child element of the last XML node.
=back
A tyelib class implemented as Inside-Out object using Class::Std::Storable
as base class would look like this:
package Typelib::TEnqueueMessage;
use strict;
use Class::Std::Storable;
my %MMessage_of :ATTR(:name<MMessage> :default<()>);
sub add_MMessage {
my ($self, $value) = @_;
my $ident = ident $self;
# we're the first value
return $MMessage_of{ $ident } = $value
if not defined $MMessage_of{ $ident };
# we're the second value
return $MMessage_of{ $ident } = [
$MMessage_of{ $ident }, $value ]
if not ref $MMessage_of{ $ident } eq 'ARRAY';
# we're third or later
push @{ $MMessage_of{ $ident } }, $value;
return $MMessage_of{ $ident };
}
}
1;
Of course one could use a method factory for these add_FOO methods - see
t/lib/Typelib/Base.pm for an example.
=head1 Performance
SOAP::WSDL::SAX::MessageHandler with a raw XML::LibXML parser almost reaches
the performance of XML::Simple with XML::Parser (and expat) as low-level
parser.
And SOAP::WSDL::SAX::MessageHandler builds up a object tree, while
XML::Simple just emits hash data structures:
SOAP::WSDL::SAX::MessageHandler:
1 wallclock secs ( 1.39 usr + 0.00 sys = 1.39 CPU) @ 719.42/s (n=1000)
XML::Simple:
2 wallclock secs ( 1.25 usr + 0.01 sys = 1.26 CPU) @ 790.51/s (n=1000)
If you know a faster way for parsing XML with a reasonable simple API than
XML::LibXML, please let me know...
=head1 Bugs and Limitations
=over
=item * Ignores all namespaces
=item * Does not handle mixed content
=item * The SOAP header is ignored
=back
=head1 AUTHOR
Replace the whitespace by @ for E-Mail Address.
Martin Kutter E<lt>martin.kutter fen-net.deE<gt>
=head1 COPYING
This module may be used under the same terms as perl itself.
=head1 Repository information
$ID: $
$LastChangedDate: $
$LastChangedRevision: $
$LastChangedBy: $
$HeadURL: $
+167
View File
@@ -0,0 +1,167 @@
package SOAP::WSDL::SAX::WSDLHandler;
use strict;
use warnings;
use Carp;
use Class::Std::Storable;
# use base qw(XML::SAX::Base);
use SOAP::WSDL::TypeLookup;
my %tree_of :ATTR(:name<tree> :default<{}>);
my %order_of :ATTR(:name<order> :default<[]>);
my %targetNamespace_of :ATTR(:name<targetNamespace> :default<()>);
my %current_of :ATTR(:name<current> :default<()>);
{
# we have to implement our own new - we need a blessed Hash ref as $self
# for being able to inherit from XML::SAX::Base...
no warnings qw(redefine);
sub new {
my $class = shift;
my $self = {}; # $class->SUPER::new(@_);
my $args = shift || {};
die "arguments to new must be single hash ref"
if @_ or ! ref $args eq 'HASH';
# nasty, but for those who want to use XML::SAX::Base or similar
# as parser factory
if ($args->{base}) {
# yup, naughty string eval
eval "use base qw($args->{base})"; ## no critic qw(ProhibitStringyEval)
}
else {
# create all those SAX methods...
# ...we ignore em all...
no strict qw(refs);
foreach my $method ( qw(
characters
processing_instruction
ignorable_whitespace
set_document_locator
start_prefix_mapping
end_prefix_mapping
skipped_entity
start_cdata
end_cdata
comment
entity_reference
notation_decl
unparsed_entity_decl
element_decl
attlist_decl
doctype_decl
xml_decl
entity_decl
attribute_decl
internal_entity_decl
external_entity_decl
resolve_entity
start_dtd
end_dtd
start_entity
end_entity
warning
error
) ) {
*{ "$method" } = sub {};
}
}
return bless $self, $class;
}
};
sub start_document {
my ($self, $ident) = ($_[0], ident $_[0]);
$tree_of{ $ident } = {};
$order_of{ $ident } = [];
$targetNamespace_of{ $ident } = undef;
$current_of{ $ident } = undef;
}
sub start_element {
my ($self, $element) = @_;
my $ident = ident $self;
my $action = SOAP::WSDL::TypeLookup->lookup(
$element->{ NamespaceURI },
$element->{ LocalName }
);
if ($action)
{
if ($action->{ type } eq 'CLASS')
{
eval "require $action->{ class }";
croak $@, $tree_of{ $ident } if ($@);
my $class = $action->{ class };
my $obj = $class->new()->init(
values %{ $element->{ Attributes } }
);
# set element in parent
if ($current_of{ $ident })
{
# inherit namespace, but don't override
$obj->set_targetNamespace(
$current_of{ $ident }->get_targetNamespace() )
if not $obj->get_targetNamespace();
# push on name list
my $method = "push_$element->{ LocalName }";
no strict qw(refs);
$current_of{ $ident }->$method( $obj );
# remember element for stepping back
push @{ $order_of{ $ident } }, $current_of{ $ident };
}
else
{
$tree_of{ $ident } = $obj;
}
# set new element (step down)
$current_of{ $ident } = $obj;
}
elsif ($action->{ type } eq 'PARENT')
{
$current_of{ $ident }->init( values %{ $element->{ Attributes } } );
}
elsif ($action->{ type } eq 'METHOD')
{
my $method = $action->{ method } || $element->{ LocalName };
no strict qw(refs);
# call method with
# - default value ($action->{ value } if defined,
# dereferencing lists
# - the values of the elements Attributes hash
$current_of{ $ident }->$method( defined $action->{ value }
? ref $action->{ value }
? @{ $action->{ value } }
: ($action->{ value })
: values %{ $element->{ Attributes } } );
}
}
}
sub end_element {
my ($self, $element) = @_;
my $ident = ident $self;
my $action = SOAP::WSDL::TypeLookup->lookup(
$element->{ NamespaceURI },
$element->{ LocalName }
) || {};
if ($action->{ type } && $action->{ type } eq 'CLASS' )
{
$current_of{ $ident } = pop @{ $order_of{ $ident } };
}
}
sub get_data {
my $self = shift;
return $tree_of{ ident $self };
}
1;
+47
View File
@@ -0,0 +1,47 @@
package SOAP::WSDL::SOAP::Typelib::Fault11;
use strict;
use warnings;
use Class::Std::Storable;
use Data::Dumper;
use SOAP::WSDL::XSD::Typelib::ComplexType;
use SOAP::WSDL::XSD::Typelib::Element;
use base qw(
SOAP::WSDL::XSD::Typelib::Element
SOAP::WSDL::XSD::Typelib::ComplexType
);
my %faultcode_of :ATTR(:get<faultcode>);
my %faultstring_of :ATTR(:get<faultstring>);
my %faultactor_of :ATTR(:get<faultactor>);
my %detail_of :ATTR(:get<faultdetail>);
# always return false in boolean context - a fault is never true...
sub as_bool :BOOLIFY { return; }
__PACKAGE__->_factory(
[ qw(faultcode faultstring faultactor faultdetail) ],
{
faultcode => \%faultcode_of,
faultstring => \%faultstring_of,
faultactor => \%faultactor_of,
detail => \%detail_of,
},
{
faultcode => 'SOAP::WSDL::XSD::Typelib::Builtin::qName',
faultstring => 'SOAP::WSDL::XSD::Typelib::Builtin::string',
faultactor => 'SOAP::WSDL::XSD::Typelib::Builtin::anyURI',
detail => 'SOAP::WSDL::XSD::Typelib::Builtin::anyType',
}
);
sub get_xmlns { return 'http://schemas.xmlsoap.org/soap/envelope/' };
__PACKAGE__->__set_name('Fault');
__PACKAGE__->__set_nillable();
__PACKAGE__->__set_minOccurs();
__PACKAGE__->__set_maxOccurs();
__PACKAGE__->__set_ref('');
1;
+27
View File
@@ -0,0 +1,27 @@
package SOAP::WSDL::Service;
use strict;
use warnings;
use Class::Std::Storable;
use base qw(SOAP::WSDL::Base);
my %port_of :ATTR(:name<port> :default<()>);
sub explain {
my $self = shift;
my $opt = shift;
my $txt ="=head1 Service name\n\n" . $self->get_name() . "\n\n";
foreach my $port (@{ $self->get_port() } )
{
$txt .= $port->explain( $opt );
}
return $txt;
}
sub to_typemap {
my $self = shift;
my $opt = shift;
return join "\n",
map { $_->to_typemap( $opt ) } @{ $port_of{ ident $self } };
}
1;
+23
View File
@@ -0,0 +1,23 @@
package SOAP::WSDL::SoapOperation;
use Class::Std::Storable;
use base qw(SOAP::WSDL::Base);
my %input_of :ATTR(:name<input> :default<()>);
my %output_of :ATTR(:name<output> :default<()>);
my %fault_of :ATTR(:name<fault> :default<()>);
my %style_of :ATTR(:name<style> :default<()>);
my %soapAction_of :ATTR(:name<soapAction> :default<()>);
sub explain {
my $self = shift;
my $opt = shift;
my $txt = '';
$opt->{ indent } ||= '';
$txt .= $opt->{ indent } . "soapAction: " . $self->soapAction() . "\n"
if ( $self->soapAction() );
return $txt;
}
1;
+152
View File
@@ -0,0 +1,152 @@
package SOAP::WSDL::TypeLookup;
my %TYPES = (
# wsdl:
'http://schemas.xmlsoap.org/wsdl/' => {
binding => {
type => 'CLASS',
class => 'SOAP::WSDL::Binding',
},
definitions => {
type => 'CLASS',
class => 'SOAP::WSDL::Definitions',
},
portType => {
type => 'CLASS',
class => 'SOAP::WSDL::PortType',
},
types => {
type => 'SKIP',
},
message => {
type => 'CLASS',
class => 'SOAP::WSDL::Message',
},
part => {
type => 'CLASS',
class => 'SOAP::WSDL::Part',
},
service => {
type => 'CLASS',
class => 'SOAP::WSDL::Service',
},
port => {
type => 'CLASS',
class => 'SOAP::WSDL::Port',
},
operation => {
type => 'CLASS',
class => 'SOAP::WSDL::Operation',
},
input => {
type => 'CLASS',
class => 'SOAP::WSDL::OpMessage',
},
output => {
type => 'CLASS',
class => 'SOAP::WSDL::OpMessage',
},
fault => {
type => 'CLASS',
class => 'SOAP::WSDL::OpMessage',
},
types => {
type => 'CLASS',
class => 'SOAP::WSDL::Types',
}
},
# soap:
'http://schemas.xmlsoap.org/wsdl/soap/' => {
operation => {
type => 'CLASS',
class => 'SOAP::WSDL::SoapOperation',
},
binding => {
type => 'PARENT',
},
body => {
type => 'PARENT',
},
address => {
type => 'PARENT',
}
},
'http://www.w3c.org/2001/XMLSchema' => {
schema => {
type => 'CLASS',
class => 'SOAP::WSDL::XSD::Schema',
},
element => {
type => 'CLASS',
class => 'SOAP::WSDL::XSD::Element',
},
simpleType => {
type => 'CLASS',
class => 'SOAP::WSDL::XSD::SimpleType',
},
complexType => {
type => 'CLASS',
class => 'SOAP::WSDL::XSD::ComplexType',
},
simpleContent => {
type => 'METHOD',
method => 'set_contentModel',
value => 'simpleContent'
},
complexContent => {
type => 'METHOD',
method => 'set_contentModel',
value => 'complexContent'
},
restriction => {
type => 'METHOD',
method => 'set_restriction',
},
list => {
type => 'METHOD',
method => 'set_list',
},
union => {
type => 'METHOD',
method => 'set_union',
},
enumeration => {
type => 'METHOD',
method => 'push_enumeration',
},
group => {
type => 'METHOD',
method => 'set_flavor',
value => 'all',
},
all => {
type => 'METHOD',
method => 'set_flavor',
value => 'all',
},
choice => {
type => 'METHOD',
method => 'set_flavor',
value => 'choice',
},
sequence => {
type => 'METHOD',
method => 'set_flavor',
value => 'sequence',
},
},
);
# thei're equal (typo ?)
$TYPES{ 'http://www.w3.org/2001/XMLSchema' } = $TYPES{
'http://www.w3c.org/2001/XMLSchema'
};
sub lookup {
my $self = shift;
my $namespace = shift || 'http://schemas.xmlsoap.org/wsdl/';
my $name = shift;
return $TYPES{ $namespace }->{ $name };
}
+37
View File
@@ -0,0 +1,37 @@
package SOAP::WSDL::Types;
use strict;
use warnings;
use SOAP::WSDL::XSD::Schema::Builtin;
use Class::Std::Storable;
use base qw(SOAP::WSDL::Base);
my %schema_of :ATTR(:name<schema> :default<[]>);
sub START {
my ($self, $ident, $args_of) = @_;
$self->push_schema( SOAP::WSDL::XSD::Schema::Builtin->new() );
return $self;
}
sub find_type {
my ($self, $ns, $name) = @_;
foreach my $schema (@{ $schema_of{ ident $self } }) {
my $type = $schema->find_type($ns, $name);
return $type if $type;
}
return;
}
sub find_element {
my ($self, $ns, $name) = @_;
foreach my $schema (@{ $schema_of{ ident $self } }) {
my $type = $schema->find_element($ns, $name);
return $type if $type;
}
return;
}
1;
+254
View File
@@ -0,0 +1,254 @@
package SOAP::WSDL::XSD::ComplexType;
use strict;
use warnings;
use Carp;
use Class::Std::Storable;
use Scalar::Util qw(blessed);
use base qw/SOAP::WSDL::Base/;
my %annotation_of :ATTR(:name<annotation> :default<()>);
my %element_of :ATTR(:name<element> :default<()>);
my %flavor_of :ATTR(:name<flavor> :default<()>);
my %base_of :ATTR(:name<base> :default<()>);
my %itemType_of :ATTR(:name<itemType> :default<()>);
my %enumeration_of :ATTR(:name<enumeration> :default<()>);
my %abstract_of :ATTR(:name<abstract> :default<()>);
# is set to simpleContent/complexContent
my %content_Model_of :ATTR(:name<contentModel> :default<()>);
sub find_element {
my ($self, @args) = @_;
my @found_at = grep {
$_->get_targetNamespace() eq $args[0] &&
$_->get_name() eq $args[1]
}
@{ $element_of{ ident $self } };
return $found_at[0];
}
sub push_element {
my $self = shift;
my $element = shift;
if ($flavor_of{ ident $self } eq 'all')
{
$element->set_minOccurs(0) if not defined ($element->get_minOccurs);
$element->set_maxOccurs(1) if not defined ($element->get_maxOccurs);
}
elsif ($flavor_of{ ident $self } eq 'sequence')
{
$element->set_minOccurs(1) if not defined ($element->get_minOccurs);
$element->set_maxOccurs(1) if not defined ($element->get_maxOccurs);
}
push @{ $element_of{ ident $self } }, $element;
}
sub set_restriction {
my $self = shift;
my $element = shift;
$flavor_of{ ident $self } = 'restriction';
$base_of{ ident $self } = $element->{ Value };
}
sub init {
my $self = shift;
my @args = @_;
$self->SUPER::init( @args );
}
sub serialize
{
my ($self, $name, $value, $opt) = @_;
$opt->{ indent } ||= q{};
my $flavor = $self->get_flavor();
my $xml = '';
$xml .= $opt->{ indent } if ($opt->{ readable }); # add indentation
$xml .= "<$name";
if ( $opt->{ autotype })
{
my $ns = $self->get_targetNamespace();
my $prefix = $opt->{ namespace }->{ $ns }
|| die 'No prefix found for namespace '. $ns;
$xml .= join q{}, " type=\"$prefix:", $self->get_name(), '"'
if ($self->get_name() );
}
$xml .= '>';
$xml .= "\n" if ( $opt->{ readable } ); # add linebreak
if ( ($flavor eq "sequence") or ($flavor eq "all") )
{
$opt->{ indent } .= "\t";
for my $element (@{ $self->get_element() })
{
# might be list - listify
$value = [ $value ] if not ref $value eq 'ARRAY';
for my $single_value (@{ $value }) {
my $element_value;
if (blessed $single_value) {
my $method = 'get_' . $element->get_name();
$element_value = $single_value->$method();
}
else {
$element_value = $single_value->{ $element->get_name() }
}
$element_value = [ $element_value ]
if not ref $element_value eq 'ARRAY';
$xml .= join q{}
, map { $element->serialize( undef, $_, $opt ) }
@{ $element_value };
}
}
$opt->{ indent } =~s/\t$//;
}
else
{
die "sorry, we just handle all and sequence types yet...";
}
$xml .= $opt->{ indent } if ( $opt->{ readable } ); # add indentation
$xml .= '</' . $name . '>';
$xml .= "\n" if ($opt->{ readable } ); # add linebreak
return $xml;
}
sub explain
{
my ($self, $opt, $name ) = @_;
my $flavor = $self->get_flavor();
my $xml = '';
$xml .= $opt->{ indent } if ($opt->{ readable }); # add indentation
$xml .= q{'} . $name . q{' => };
return q{} if not $flavor; # empty complexType
if ( ($flavor eq "sequence") or ($flavor eq "all") )
{
$xml .= "{\n";
$opt->{ indent } .= "\t";
$xml .= join q{}, map { $_->explain( $opt ) }
@{ $self->get_element() };
$opt->{ indent } =~s/\t$//; # step back
$xml .= $opt->{ indent } . "},\n";
}
elsif ($flavor eq "complexContent")
{
}
elsif ($flavor eq "simpleContent")
{
}
else
{
warn "unknown complexType definition $flavor";
}
$xml .= "\n" if ($opt->{ readable } ); # add linebreak
return $xml;
}
sub to_typemap {
my ($self, $opt, $name ) = @_;
my $flavor = $self->get_flavor();
my $txt;
return q{} if not $flavor; # empty complexType
my %nsmap = reverse %{ $opt->{ wsdl }->get_xmlns() };
if ( ($flavor eq "sequence") or ($flavor eq "all") )
{
for my $element (@{ $self->get_element() }) {
$txt .= $element->to_typemap( $opt );
}
}
return $txt;
}
sub toClass {
my $self = shift;
my $opt = shift;
my $template = <<'EOT';
package [% class_prefix %]::[% self.get_name %];
use strict;
use Class::Std::Storable;
use SOAP::WSDL::XSD::Typelib::ComplexType;
use base qw(
SOAP::WSDL::XSD::Typelib::ComplexType
);
[% FOREACH element=self.get_element -%]
my %[% element.get_name %]_of :ATTR(:get<[% element.get_name %]>);
[% END %]
__PACKAGE__->_factory(
[ qw([% FOREACH element=self.get_element -%]
[% element.get_name %]
[% END %]) ],
{
[% FOREACH element=self.get_element %] [% element.get_name %] => \%[% element.get_name %]_of,
[% END %]
},
{
[%- FOREACH element=self.get_element;
split_name = element.get_type.split(':');
prefix = split_name.0;
localname = split_name.1;
IF nsmap.$prefix == 'http://www.w3.org/2001/XMLSchema' -%]
[% element.get_name %] => 'SOAP::WSDL::XSD::Typelib::Builtin::[% localname %]',
[% ELSE %]
[% element.get_name %] => '[% class_prefix %]::[% localname %]',
[%- END;
END %]
}
);
sub get_xmlns { '[% self.get_targetNamespace %]' }
1;
EOT
$opt->{ base_path } ||= '.';
require Template;
my $tt = Template->new(
RELATIVE => 1,
);
my $code = $tt->process( \$template, {
class_prefix => $opt->{ prefix },
self => $self,
nsmap => { reverse %{ $opt->{ wsdl }->get_xmlns() } },
},
$opt->{ output },
) or die $tt->error();
}
sub _check_value {
my $self = shift;
}
1;
=pod
=head1 Bugs and limitations
=over
=item * attribute definitions
Attribute definitions are ignored
=item * abstract, block, final, mixed
Handling of these attribute is not implemented yet.
=item * simpleContent, complexContent, extension, restriction, group, choice
Handling of these child elements is not implemented yet
=item * explain may produce erroneous results
=over
=cut
+348
View File
@@ -0,0 +1,348 @@
package SOAP::WSDL::XSD::Element;
use strict;
use warnings;
use Class::Std::Storable;
use base qw(SOAP::WSDL::Base);
my %simpleType_of :ATTR(:name<simpleType> :default<()>);
my %complexType_of :ATTR(:name<complexType> :default<()>);
my %facet_of :ATTR(:name<facet> :default<()>);
my %type_of :ATTR(:name<type> :default<()>);
my %abstract_of :ATTR(:name<abstract> :default<()>);
my %block_of :ATTR(:name<block> :default<()>);
my %default_of :ATTR(:name<default> :default<()>);
my %final_of :ATTR(:name<final> :default<()>);
my %fixed_of :ATTR(:name<fixed> :default<()>);
my %form_of :ATTR(:name<form> :default<()>);
my %maxOccurs_of :ATTR(:name<maxOccurs> :default<()>);
my %minOccurs_of :ATTR(:name<minOccurs> :default<()>);
my %nillable_of :ATTR(:name<nillable> :default<()>);
my %ref_of :ATTR(:name<ref> :default<()>);
my %substitutionGroup_of :ATTR(:name<substitutionGroup> :default<()>);
sub first_type {
my $result_ref = $complexType_of{ ident shift };
return if not $result_ref;
return $result_ref if (not ref $result_ref eq 'ARRAY');
return $result_ref->[0];
}
sub first_simpleType {
my $result_ref = $simpleType_of{ ident shift };
return if not $result_ref;
return $result_ref if (not ref $result_ref eq 'ARRAY');
return $result_ref->[0];
}
sub first_complexType {
my $result_ref = $complexType_of{ ident shift };
return if not $result_ref;
return $result_ref if (not ref $result_ref eq 'ARRAY');
return $result_ref->[0];
}
# serialize type instead...
sub serialize
{
my ($self, $name, $value, $opt) = @_;
my $type;
my $typelib = $opt->{ typelib };
my %ns_map = reverse %{ $opt->{ namespace } };
my $ident = ident $self;
# abstract elements may only be serialized via ref - and then we have a
# name...
die "cannot serialize abstract element" if $abstract_of{ $ident }
and not $name;
# TODO: implement final and substitutionGroup - maybe never implement
# substitutionGroup ?
$name ||= $self->get_name();
# set default and fixed - fixed overrides everything,
# default only empty (undefined) values
if (not defined $value) {
$value = $default_of{ ident $self } if $default_of{ ident $self };
}
$value = $fixed_of{ ident $self } if $fixed_of{ ident $self };
# TODO check nillable and serialize empty data correctly
# return if minOccurs is 0 and we have no value
if (defined $minOccurs_of{ ident $self }
and $minOccurs_of{ ident $self } == 0) {
return q{} if not defined $value;
}
# handle direct simpleType and complexType here
if ($type = $self->first_simpleType() ) { # simpleType
return $type->serialize( $name, $value, $opt );
}
elsif ($type = $self->first_complexType() ) { # complexType
return $type->serialize( $name, $value, $opt );
}
elsif (my $ref_name = $ref_of{ $ident }) { # ref
my ($prefix, $localname) = split /:/ , $ref_name;
my $ns = $ns_map{ $prefix };
$type = $typelib->find_type( $ns, $localname );
die "no type for $prefix:$localname" if (not $type);
return $type->serialize( $self->get_name(), $value, $opt );
}
# lookup type
my ($prefix, $localname) = split /:/ , $self->get_type();
my $ns = $ns_map{ $prefix };
$type = $typelib->find_type(
$ns, $localname
);
# safety check
die "no type for $prefix:$localname $ns_map{$prefix}" if (not $type);
return $type->serialize( $self->get_name(), $value, $opt );
}
sub explain
{
my ($self, $opt, $name) = @_;
my $type;
my $text = q{};
# if we have a simple / complexType, explain right here
if ($type = $self->first_simpleType() )
{
$text .= $type->explain( $opt, $self->get_name() );
}
elsif ($type = $self->first_complexType() )
{
$text .= $type->explain( $opt, $self->get_name() )
}
# return if it's not a derived type - we don't handle
# other stuff yet.
return $text if (not $self->get_type() );
# if we have a derived type, fetch type and explain
my ($prefix, $localname) = split /:/ , $self->get_type();
my %ns_map = reverse %{ $opt->{ namespace } };
my $ns = $ns_map{ $prefix };
$type = $opt->{ wsdl }->first_types()->find_type(
$ns, $localname
);
die "no type for $prefix:$localname ($ns)" if (not $type);
return $text .= $type->explain( $opt, $self->get_name() );
return 'ERROR: '. $@;
}
sub to_typemap {
my ($self, $opt) = @_;
my $txt = q{};
my %nsmap = reverse %{ $opt->{ wsdl }->get_xmlns() };
my $type;
push @{ $opt->{path} }, $self->get_name();
if ( my $typename = $self->get_type() ) {
my ($prefix, $localname) = split /:/, $self->get_type();
my $ns = $nsmap{ $prefix };
my $typeclass;
# builtin type
if ( $nsmap{ $prefix } eq 'http://www.w3.org/2001/XMLSchema') {
$typeclass = "SOAP::WSDL::XSD::Typelib::Builtin::$localname";
}
else
{
$type = $opt->{ wsdl }->first_types()->find_type( $ns, $localname );
$typeclass = $opt->{ prefix } . $type->get_name();
$txt .= $type->to_typemap($opt);
}
$txt .= q{'} . join( q{/}, @{ $opt->{path} } ) . "' => '$typeclass',\n";
}
elsif ($type = $self->first_simpleType() ) {
my $flavor = $type->get_flavor();
if ( $flavor eq 'sequence' ) {
$txt .= "# atomic simple type (sequence)\n";
$txt .= $type->to_typemap($opt). "\n";
$txt .= "# end atomic simple type (sequence)\n";
}
elsif ( $flavor eq 'all' ) {
$txt .= "# atomic simple type (all)\n";
$txt .= $type->to_typemap($opt). "\n";
$txt .= "# end atomic simple type (all)\n";
}
}
elsif ($type = $self->first_complexType() ) {
my $typeclass = $opt->{ prefix } . $self->get_name();
$txt .= q{'} . join( q{/}, @{ $opt->{path} } ) . "' => '$typeclass',\n";
my $flavor = $type->get_flavor();
if ( $flavor eq 'sequence' ) {
$txt .= "# atomic complex type (sequence)\n";
$txt .= $type->to_typemap($opt). "\n";;
$txt .= "# end atomic complex type (sequence)\n";
}
elsif ( $flavor eq 'all' ) {
$txt .= "# atomic complex type (all)\n";
$txt .= $type->to_typemap($opt). "\n";
$txt .= "# end atomic complex type (all)\n";
}
}
pop @{ $opt->{ path } };
return $txt;
}
sub toClass {
my $self = shift;
my $opt = shift;
my $template = <<'EOT';
package [% class_prefix %]::[% self.get_name %];
use strict;
use Class::Std::Storable;
use SOAP::WSDL::XSD::Typelib::Element;
[% IF (type = self.first_simpleType) %]
# <element name="[% self.get_name %]"><simpleType> definition
use SOAP::WSDL::XSD::Typelib::SimpleType;
use base qw(
SOAP::WSDL::XSD::Typelib::Element
SOAP::WSDL::XSD::Typelib::SimpleType
[% type.flavor_class %]
[% type.base_class($class_prefix) %]
);
[% ELSIF (type = self.first_complexType) %]
# <element name="[% self.get_name %]"><complexType> definition
use SOAP::WSDL::XSD::Typelib::ComplexType;
use base qw(
SOAP::WSDL::XSD::Typelib::Element
SOAP::WSDL::XSD::Typelib::ComplexType
);
[% FOREACH element = type.get_element %]
my %[% element.get_name %]_of :ATTR(get:<[% element.get_name %]>);
[% END %]
__PACKAGE__->_factory(
[ qw([% FOREACH element = type.get_element %]
[% element.get_name %]
[% END %]) ],
{
[% FOREACH element = type.get_element %] [% element.get_name %] => \%[% element.get_name %]_of,
[% END %]
},
{
[% FOREACH element = type.get_element;
split_name = element.get_type.split(':');
prefix = split_name.0;
localname = split_name.1;
IF nsmap.$prefix == 'http://www.w3.org/2001/XMLSchema' %]
[% element.get_name %] => 'SOAP::WSDL::XSD::Typelib::Builtin::[% localname %]',
[% ELSE %]
[% element.get_name %] => '[% class_prefix %]::[% localname %]',
[% END %]
[% END %]
}
);
[%# END complexType %]
[% ELSIF (type = self.get_type) %]
# <element name="[% self.get_name %]" type="[% self.get_type %]"/> definition
[% (typename = self.get_type);
split_name = element.type.split(':');
prefix = split_name.0;
localname = split_name.1;
-%]
[% IF (nsmap.$prefix == 'http://www.w3.org/2001/XMLSchema');
base_class = 'SOAP::WSDL::XSD::Typelib::Builtin::' _ localname ;
ELSE;
base_class = class_prefix _ '::' _ localname;
-%]
use [% base_class %];
[% END %]
use base qw(
SOAP::WSDL::XSD::Typelib::Element
[% base_class %]
);
[% ELSIF (element = self.get_ref)
%]
# element ref"element" definition
# Sorry, we don't handle this yet...
[% END %]
sub get_xmlns { '[% self.get_targetNamespace %]' }
__PACKAGE__->__set_name('[% self.get_name %]');
__PACKAGE__->__set_nillable([% self.get_nillable %]);
__PACKAGE__->__set_minOccurs([% self.get_minOccurs %]);
__PACKAGE__->__set_maxOccurs([% self.get_maxOccurs %]);
__PACKAGE__->__set_ref('[% self.get_ref %]');
1;
EOT
require Template;
my $tt = Template->new(
RELATIVE => 1,
);
my $code = $tt->process( \$template, {
class_prefix => $opt->{ prefix },
self => $self,
nsmap => { reverse %{ $opt->{ wsdl }->get_xmlns() } },
},
$opt->{ output },
)
or die $tt->error();
}
1;
__END__
=pod
=head1 NAME
SOAP::WSDL::XSD::Element - XSD Element representation
=head1 DESCRIPTION
Represents a XML Schema Definition's Element element for serializing into
various forms.
=head1 Bugs and limitations
=over
=item * block
Handling of the block attribute is not implemented yet.
=item * final
Handling of the final attribute is not implemented yet.
=item * substitutionGroup
Handling of the substitutionGroup attribute is not implemented yet
=item * explain may produce erroneous results
=over
=head1 COPYING
This module is part of SOAP-WSDL. See SOAP::WSDL for license.
=head1 AUTHOR
Insert @ instead of whitespace into e-mail.
Martin Kutter E<lt>martin.kutter fen-net.deE<gt>
=cut
+53
View File
@@ -0,0 +1,53 @@
package SOAP::WSDL::XSD::Primitive;
use strict;
use warnings;
use Class::Std::Storable;
use base qw(SOAP::WSDL::Base);
use Data::Dumper;
sub serialize
{
my ($self, $name, $value, $opt) = @_;
my $xml;
$opt->{ indent } ||= "";
$xml .= $opt->{ indent } if ($opt->{ readable });
$xml .= '<' . $name;
if ( $opt->{ autotype })
{
my $ns = $self->get_targetNamespace();
my $prefix = $opt->{ namespace }->{ $ns }
|| die 'No prefix found for namespace '. $ns;
$xml .= ' type="' . $prefix . ':'
. $self->get_name() . '"' if ($self->get_name() );
}
if (defined $value)
{
$xml .= '>';
$xml .= "$value";
$xml .= '</' . $name . '>' ;
}
else
{
$xml .= '/>';
}
$xml .= "\n" if ($opt->{ readable });
return $xml;
}
sub explain
{
my ($self, $opt, $name ) = @_;
my $perl;
$opt->{ indent } ||= "";
$perl .= $opt->{ indent } if ($opt->{ readable });
$perl .= q{'} . $name . q{' => $someValue };
$perl .= "\n" if ($opt->{ readable });
return $perl;
}
sub toClass {
warn "# primitive";
}
1;
+72
View File
@@ -0,0 +1,72 @@
package SOAP::WSDL::XSD::Schema;
use strict;
use warnings;
use Class::Std::Storable;
use base qw(SOAP::WSDL::Base);
# child elements
my %type_of :ATTR(:name<type> :default<[]>);
my %element_of :ATTR(:name<element> :default<[]>);
# attributes
my %attributeFormDefault_of :ATTR(:name<attributeFormDefault> :default<()>);
my %blockDefault_of :ATTR(:name<blockDefault> :default<()>);
my %elementFormDefault_of :ATTR(:name<elementFormDefault> :default<()>);
my %finalDefault_of :ATTR(:name<finalDefault> :default<()>);
my %version_of :ATTR(:name<version> :default<()>);
# id
# name
# targetNamespace inherited from Base
# xmlns
#
# attributeFormDefault = (qualified | unqualified) : unqualified
# blockDefault = (#all | List of (extension | restriction | substitution)) : ''
# elementFormDefault = (qualified | unqualified) : unqualified
# finalDefault = (#all | List of (extension | restriction | list | union)) : ''
# id = ID
# targetNamespace = anyURI
# version = token
# xml:lang = language
#
#
# alias type with all variants
# AUTOMETHOD is WAY too slow..
{
no strict qw/refs/;
for my $name (qw(simpleType complexType) ) {
*{ "set_$name" } = \&set_type;
*{ "get_$name" } = \&get_type;
*{ "push_$name" } = \&push_type;
*{ "find_$name" } = \&find_type;
}
}
sub push_type {
# use $_[n] for performance -
# we're called on each and every type inside WSDL
push @{ $type_of{ ident $_[0]} }, $_[1];
}
sub find_element {
my ($self, @args) = @_;
my @found_at = grep {
$_->get_targetNamespace() eq $args[0] &&
$_->get_name() eq $args[1]
}
@{ $element_of{ ident $self } };
return $found_at[0];
}
sub find_type {
my ($self, @args) = @_;
my @found_at = grep {
$_->get_targetNamespace() eq $args[0] &&
$_->get_name() eq $args[1]
}
@{ $type_of{ ident $self } };
return $found_at[0];
}
1;
+64
View File
@@ -0,0 +1,64 @@
package SOAP::WSDL::XSD::Schema::Builtin;
use strict;
use warnings;
use Class::Std::Storable;
use SOAP::WSDL::XSD::Schema;
use SOAP::WSDL::XSD::Primitive;
use base qw(SOAP::WSDL::XSD::Schema);
# all builtin types - add validation (e.g. content restrictions) later...
my %PRIMITIVES = (
'string' => {},
'boolean' => {},
'decimal' => {},
'dateTime' => {},
'float' => {},
'double' => {},
'duration' => {},
'time' => {},
'date' => {},
'gYearMonth' => {},
'gYear' => {},
'gMonthDay' => {},
'gDay' => {},
'gMonth' => {},
'hexBinary' => {},
'base64Binary' => {},
'anyURI' => {},
'QName' => {},
'NOTATION' => {},
'integer' => {},
'non-positive-integer' => {},
'non-negative-integer' => {},
'positiveInteger' => {},
'negativeInteger' => {},
'long' => {},
'int' => {},
'unsignedInt' => {},
'short' => {},
'unsignedShort' => {},
'byte' => {},
'unsignedByte' => {},
'normalizedString' => {},
'token' => {},
'NMTOKEN' => {},
);
# TODO place into appropriate schema and push on schema definitions list
# in types
sub START {
my $self = shift;
my @args = @_;
while (my ($name, $value) = each %PRIMITIVES )
{
$self->push_type( SOAP::WSDL::XSD::Primitive->new({
name => $name,
targetNamespace => 'http://www.w3.org/2001/XMLSchema',
} )
);
}
return $self;
}
1;
+178
View File
@@ -0,0 +1,178 @@
package SOAP::WSDL::XSD::SimpleType;
use strict;
use warnings;
use Class::Std::Storable;
use base qw(SOAP::WSDL::Base);
my %base_of :ATTR(:name<base> :default<()>);
my %flavor_of :ATTR(:name<flavor> :default<()>);
my %itemType_of :ATTR(:name<itemType> :default<()>);
my %enumeration_of :ATTR(:name<enumeration> :default<()>);
# is set to simpleContent/complexContent
my %content_Model_of :ATTR(:name<contentModel> :default<()>);
sub set_restriction {
my $self = shift;
my @attributes = @_;
$self->set_flavor( 'restriction' );
foreach my $attr (@attributes)
{
next if (not $attr->{ LocalName } eq 'restriction');
$self->base( $attr->{ Value } );
}
}
sub set_list {
my $self = shift;
my @attributes = @_;
$self->set_flavor( 'list' );
foreach my $attr (@attributes)
{
next if (not $attr->{ LocalName } eq 'list');
$self->set_base( $attr->{ Value } );
}
}
sub set_union {
my $self = shift;
my @attributes = @_;
$self->set_flavor( 'union' );
foreach my $attr (@attributes)
{
next if (not $attr->{ LocalName } eq 'memberTypes');
$self->set_base( [ split /\s/, $attr->{ Value } ] );
}
}
sub push_enumeration
{
my $self = shift;
my @attr = @_;
my @attributes = @_;
$self->set_flavor( 'enumeration' );
foreach my $attr (@attributes)
{
next if (not $attr->{ LocalName } eq 'value');
push @{ $enumeration_of{ ident $self } }, $attr->{ 'Value' };
}
}
sub serialize
{
my $self = shift;
my $name = shift;
my $value = shift;
my $opt = shift;
my $ident = ident $self;
$self->_check_value( $value );
return $self->_serialize_single($name, $value , $opt)
if ( $flavor_of{ $ident } eq 'restriction'
or $flavor_of{ $ident } eq 'union'
or $flavor_of{ $ident } eq 'enumeration');
if ($flavor_of{ $ident } eq 'list' )
{
$value ||= [];
$value = [ $value ] if ( ref( $value) ne 'ARRAY' );
return $self->_serialize_single($name, join( q{ }, @{ $value } ), $opt);
}
}
sub _serialize_single
{
my ($self, $name, $value, $opt) = @_;
my $xml = '';
$xml .= $opt->{ indent } if ($opt->{ readable }); # add indentation
$xml .= '<' . $name;
if ( $opt->{ autotype })
{
my $ns = $self->get_targetNamespace();
my $prefix = $opt->{ namespace }->{ $ns }
|| die 'No prefix found for namespace '. $ns;
$xml .= ' type="' . $prefix . ':'
. $self->get_name() .'"';
}
$xml .= '>';
$xml .= $value;
$xml .= '</' . $name . '>' ;
$xml .= "\n" if ($opt->{ readable });
return $xml;
}
sub explain
{
my ($self, $opt, $name) = @_;
my $perl;
$opt->{ indent } ||= "";
$perl .= $opt->{ indent } if ($opt->{ readable });
$perl .= q{'} . $name . q{' => $someValue };
$perl .= "\n" if ($opt->{ readable });
return $perl;
}
sub _check_value {
my $self = shift;
}
sub toClass {
my $self = shift;
my $opt = shift;
my $class_prefix = $opt->{ prefix };
my $name = $opt->{name} || $self->get_name();
my $flavor = $self->get_flavor() eq 'list'
? 'SOAP::WSDL::XSD::Typelib::Builtin::list'
: $self->get_flavor() eq 'restriction'
? 'SOAP::WSDL::XSD::Typelib::SimpleType::restriction'
: q{};
my $base = $self->get_base();
my $targetNamespace = $self->get_targetNamespace;
my ($prefix, $localname) = split /:/, $base;
my %ns_map = reverse %{ $opt->{ wsdl }->get_xmlns() };
$base = ($ns_map{ $prefix} eq 'http://www.w3.org/2001/XMLSchema')
? "SOAP::WSDL::XSD::Typelib::Builtin::$localname"
: "$opt->{prefix}::$localname";
my $code =<<"EOT";
package $class_prefix::$name;
use strict;
use Class::Std::Storable;
use SOAP::WSDL::XSD::Typelib::Builtin;
use SOAP::WSDL::XSD::Typelib::SimpleType;
use base qw(
SOAP::WSDL::XSD::Typelib::SimpleType
$flavor
$base
);
1;
sub get_xmlns { '$targetNamespace' }
EOT
return $code;
}
1;
=pod
=head1 Bugs and limitations
=over
=item * simpleContent, complexContent
These child elements are not implemented yet
=item * union
union simpleType definitions probalbly serialize wrong
=item * explain may produce erroneous results
=over
=cut
+514
View File
@@ -0,0 +1,514 @@
package SOAP::WSDL::XSD::Typelib::Builtin;
use strict;
use warnings;
use Class::Std::Storable;
# derivation classes first...
package SOAP::WSDL::XSD::Typelib::Builtin::list;
use strict;
use warnings;
use Class::Std::Storable;
sub serialize {
my ($self, $opt) = @_;
my $value = $self->get_value();
return $self->start_tag({ %$opt, nil => 1 }) if not defined $value;
$value = [ $value ] if not ref $value;
return join q{}, $self->start_tag($opt, $value)
, join( q{ }, @{ $value } )
, $self->end_tag($opt, $value);
}
# Builtin classes
# Every XML schema type inherits from anyType...
package SOAP::WSDL::XSD::Typelib::Builtin::anyType;
use strict;
use warnings;
use Class::Std::Storable;
sub start_tag {
my ($self, $opt) = @_;
$opt ||= {};
return '<' . $opt->{name} . ' >' if $opt->{ name };
return q{}
}
sub end_tag {
my ($self, $opt) = @_;
$opt ||= {};
return '</' . $opt->{name} . ' >' if $opt->{ name };
return q{}
};
sub serialize_qualified :STRINGIFY {
return $_[0]->serialize( { qualified => 1 } );
}
# All builtin and all simpleType types inherit from anySimpleType
package SOAP::WSDL::XSD::Typelib::Builtin::anySimpleType;
use strict;
use warnings;
use Class::Std::Storable;
use base qw(SOAP::WSDL::XSD::Typelib::Builtin::anyType);
my %value_of :ATTR(:name<value> :default<()>);
sub serialize {
my ($self, $opt) = @_;
my $ident = ident $self;
$opt ||= {};
return $self->start_tag({ %$opt, nil => 1})
if not defined $value_of{ $ident };
return join q{}, $self->start_tag($opt, $value_of{ $ident })
, $value_of{ $ident }
, $self->end_tag($opt);
}
sub as_bool :BOOLIFY {
return $value_of { ident $_[0] };
}
package SOAP::WSDL::XSD::Typelib::Builtin::dateTime;
use strict;
use warnings;
use Class::Std::Storable;
use base qw(SOAP::WSDL::XSD::Typelib::Builtin::anySimpleType);
package SOAP::WSDL::XSD::Typelib::Builtin::duration;
use strict;
use warnings;
use Class::Std::Storable;
use base qw(SOAP::WSDL::XSD::Typelib::Builtin::anySimpleType);
package SOAP::WSDL::XSD::Typelib::Builtin::date;
use strict;
use warnings;
use Class::Std::Storable;
use base qw(SOAP::WSDL::XSD::Typelib::Builtin::anySimpleType);
package SOAP::WSDL::XSD::Typelib::Builtin::time;
use strict;
use warnings;
use Class::Std::Storable;
use base qw(SOAP::WSDL::XSD::Typelib::Builtin::anySimpleType);
package SOAP::WSDL::XSD::Typelib::Builtin::gYearMonth;
use strict;
use warnings;
use Class::Std::Storable;
use base qw(SOAP::WSDL::XSD::Typelib::Builtin::anySimpleType);
package SOAP::WSDL::XSD::Typelib::Builtin::gYear;
use strict;
use warnings;
use Class::Std::Storable;
use base qw(SOAP::WSDL::XSD::Typelib::Builtin::anySimpleType);
package SOAP::WSDL::XSD::Typelib::Builtin::gMonthDay;
use strict;
use warnings;
use Class::Std::Storable;
use base qw(SOAP::WSDL::XSD::Typelib::Builtin::anySimpleType);
package SOAP::WSDL::XSD::Typelib::Builtin::gMonth;
use strict;
use warnings;
use Class::Std::Storable;
use base qw(SOAP::WSDL::XSD::Typelib::Builtin::anySimpleType);
package SOAP::WSDL::XSD::Typelib::Builtin::gDay;
use strict;
use warnings;
use Class::Std::Storable;
use base qw(SOAP::WSDL::XSD::Typelib::Builtin::anySimpleType);
package SOAP::WSDL::XSD::Typelib::Builtin::boolean;
use strict;
use warnings;
use Class::Std::Storable;
use base qw(SOAP::WSDL::XSD::Typelib::Builtin::anySimpleType);
sub serialize {
my ($self, $opt) = @_;
my $ident = ident $self;
$opt ||= {};
return $self->start_tag({ %$opt, nil => 1})
if not defined $value_of{ $ident };
return join q{}
, $self->start_tag($opt)
, $value_of{ $ident } ? 'true' : 'false'
, $self->end_tag($opt);
}
sub as_num :NUMERIFY {
return $_[0]->get_value();
}
sub set_value {
my ($self, $value) = @_;
$value_of{ ident $self } = defined $value
? ($value ne 'false' or ($value))
? 1 : 0
: 0;
}
package SOAP::WSDL::XSD::Typelib::Builtin::string;
use strict;
use warnings;
use Class::Std::Storable;
use base qw(SOAP::WSDL::XSD::Typelib::Builtin::anySimpleType);
package SOAP::WSDL::XSD::Typelib::Builtin::normalizedString;
use strict;
use warnings;
use Class::Std::Storable;
use base qw(SOAP::WSDL::XSD::Typelib::Builtin::string);
package SOAP::WSDL::XSD::Typelib::Builtin::token;
use strict;
use warnings;
use Class::Std::Storable;
use base qw(SOAP::WSDL::XSD::Typelib::Builtin::normalizedString);
package SOAP::WSDL::XSD::Typelib::Builtin::language;
use strict;
use warnings;
use Class::Std::Storable;
use base qw(SOAP::WSDL::XSD::Typelib::Builtin::token);
package SOAP::WSDL::XSD::Typelib::Builtin::Name;
use strict;
use warnings;
use Class::Std::Storable;
use base qw(SOAP::WSDL::XSD::Typelib::Builtin::token);
package SOAP::WSDL::XSD::Typelib::Builtin::NCName;
use strict;
use warnings;
use Class::Std::Storable;
use base qw(SOAP::WSDL::XSD::Typelib::Builtin::Name);
package SOAP::WSDL::XSD::Typelib::Builtin::ID;
use strict;
use warnings;
use Class::Std::Storable;
use base qw(SOAP::WSDL::XSD::Typelib::Builtin::NCName);
package SOAP::WSDL::XSD::Typelib::Builtin::IDREF;
use strict;
use warnings;
use Class::Std::Storable;
use base qw(SOAP::WSDL::XSD::Typelib::Builtin::ID);
package SOAP::WSDL::XSD::Typelib::Builtin::IDREFS;
use strict;
use warnings;
use Class::Std::Storable;
use base qw(
SOAP::WSDL::XSD::Typelib::Builtin::list
SOAP::WSDL::XSD::Typelib::Builtin::IDREF);
package SOAP::WSDL::XSD::Typelib::Builtin::ENTITY;
use strict;
use warnings;
use Class::Std::Storable;
use base qw(SOAP::WSDL::XSD::Typelib::Builtin::NCName);
package SOAP::WSDL::XSD::Typelib::Builtin::NMTOKEN;
use strict;
use warnings;
use Class::Std::Storable;
use base qw(SOAP::WSDL::XSD::Typelib::Builtin::token);
package SOAP::WSDL::XSD::Typelib::Builtin::NMTOKENS;
use strict;
use warnings;
use Class::Std::Storable;
use base qw(
SOAP::WSDL::XSD::Typelib::Builtin::list
SOAP::WSDL::XSD::Typelib::Builtin::token);
package SOAP::WSDL::XSD::Typelib::Builtin::decimal;
use strict;
use warnings;
use Class::Std::Storable;
use base qw(SOAP::WSDL::XSD::Typelib::Builtin::anySimpleType);
sub as_num :NUMERIFY :BOOLIFY {
return $_[0]->get_value();
}
package SOAP::WSDL::XSD::Typelib::Builtin::base64Binary;
use strict;
use warnings;
use Class::Std::Storable;
use base qw(SOAP::WSDL::XSD::Typelib::Builtin::anySimpleType);
package SOAP::WSDL::XSD::Typelib::Builtin::hex64Binary;
use strict;
use warnings;
use Class::Std::Storable;
use base qw(SOAP::WSDL::XSD::Typelib::Builtin::anySimpleType);
package SOAP::WSDL::XSD::Typelib::Builtin::float;
use strict;
use warnings;
use Class::Std::Storable;
use base qw(SOAP::WSDL::XSD::Typelib::Builtin::anySimpleType);
sub as_num :NUMERIFY {
return $_[0]->get_value();
}
package SOAP::WSDL::XSD::Typelib::Builtin::double;
use strict;
use warnings;
use Class::Std::Storable;
use base qw(SOAP::WSDL::XSD::Typelib::Builtin::anySimpleType);
sub as_num :NUMERIFY {
return $_[0]->get_value();
}
package SOAP::WSDL::XSD::Typelib::Builtin::anyURI;
use strict;
use warnings;
use Class::Std::Storable;
use base qw(SOAP::WSDL::XSD::Typelib::Builtin::anySimpleType);
package SOAP::WSDL::XSD::Typelib::Builtin::qName;
use strict;
use warnings;
use Class::Std::Storable;
use base qw(SOAP::WSDL::XSD::Typelib::Builtin::anySimpleType);
package SOAP::WSDL::XSD::Typelib::Builtin::NOTATION;
use strict;
use warnings;
use Class::Std::Storable;
use base qw(SOAP::WSDL::XSD::Typelib::Builtin::anySimpleType);
package SOAP::WSDL::XSD::Typelib::Builtin::integer;
use strict;
use warnings;
use Class::Std::Storable;
use base qw(SOAP::WSDL::XSD::Typelib::Builtin::decimal);
package SOAP::WSDL::XSD::Typelib::Builtin::nonPositiveInteger;
use strict;
use warnings;
use Class::Std::Storable;
use base qw(SOAP::WSDL::XSD::Typelib::Builtin::integer);
package SOAP::WSDL::XSD::Typelib::Builtin::negativeInteger;
use strict;
use warnings;
use Class::Std::Storable;
use base qw(SOAP::WSDL::XSD::Typelib::Builtin::nonPositiveInteger);
package SOAP::WSDL::XSD::Typelib::Builtin::nonNegativeInteger;
use strict;
use warnings;
use Class::Std::Storable;
use base qw(SOAP::WSDL::XSD::Typelib::Builtin::integer);
package SOAP::WSDL::XSD::Typelib::Builtin::unsignedLong;
use strict;
use warnings;
use Class::Std::Storable;
use base qw(SOAP::WSDL::XSD::Typelib::Builtin::nonNegativeInteger);
package SOAP::WSDL::XSD::Typelib::Builtin::positiveInteger;
use strict;
use warnings;
use Class::Std::Storable;
use base qw(SOAP::WSDL::XSD::Typelib::Builtin::nonNegativeInteger);
package SOAP::WSDL::XSD::Typelib::Builtin::unsignedInt;
use strict;
use warnings;
use Class::Std::Storable;
use base qw(SOAP::WSDL::XSD::Typelib::Builtin::unsignedLong);
package SOAP::WSDL::XSD::Typelib::Builtin::unsignedShort;
use strict;
use warnings;
use Class::Std::Storable;
use base qw(SOAP::WSDL::XSD::Typelib::Builtin::unsignedInt);
package SOAP::WSDL::XSD::Typelib::Builtin::unsignedByte;
use strict;
use warnings;
use Class::Std::Storable;
use base qw(SOAP::WSDL::XSD::Typelib::Builtin::unsignedShort);
package SOAP::WSDL::XSD::Typelib::Builtin::long;
use strict;
use warnings;
use Class::Std::Storable;
use base qw(SOAP::WSDL::XSD::Typelib::Builtin::integer);
package SOAP::WSDL::XSD::Typelib::Builtin::int;
use strict;
use warnings;
use Class::Std::Storable;
use base qw(SOAP::WSDL::XSD::Typelib::Builtin::long);
package SOAP::WSDL::XSD::Typelib::Builtin::short;
use strict;
use warnings;
use Class::Std::Storable;
use base qw(SOAP::WSDL::XSD::Typelib::Builtin::int);
package SOAP::WSDL::XSD::Typelib::Builtin::byte;
use strict;
use warnings;
use Class::Std::Storable;
use base qw(SOAP::WSDL::XSD::Typelib::Builtin::short);
1;
__END__
=pod
=head1 NAME
SOAP::WSDL::XSD::Typelib::Builtin - Built-in XML Schema datatypes
=head1 DESCRIPTION
This module implements all builtin Types from the XML schema specification.
Objects of a class may be filled with values and serialize correctly.
These basic type classes are most useful when used as element or simpleType
base classes.
=head1 EXAMPLES
my $bool = SOAP::WSDL::XSD::Typelib::Builtin::bool->new({ value => 0} );
print $bool; # prints "true"
# implements <simpleType name="MySimpleType">
# <list itemType="xsd:string" />
# </simpleType>
package MySimpleType;
use SOAP::WSDL::XSD::Typelib::Builtin;
use SOAP::WSDL::XSD::Typelib::SimpleType;
use base qw(SOAP::WSDL::XSD::Typelib::SimpleType
SOAP::WSDL::XSD::Typelib::Builtin::list
SOAP::WSDL::XSD::Typelib::Builtin::string
);
1;
# somewhere else
my $list = MySimpleType->new({ value => [ 'You', 'shall', 'overcome' ] });
print $list; # prints "You shall overcome"
=head1 CLASS HIERARCHY
This is the inheritance graph for builtin types.
Types with [] marker describe types derived via the item in [] in the XML
Schema specs.
Derivation is implemented via multiple inheritance with the derivation method
as first item in the base class list.
anyType
- anySimpleType
- duration
- dateTime
- date
- time
- gYearMonth
- gYear
- gMonthDay
- gDay
- gMonth
- boolean
- base64Binary
- hexBinary
- float
- decimal
- integer
- nonPositiveInteger
- negativeInteger
- nonNegativeInteger
- positiveInteger
- unsignedLong
- unsignedInt
- unsignedShort
- unsignedByte
- long
- int
- short
- byte
- double
- anyURI
- string
- normalizedString
- language
- Name
- NCName
- ID
- IDREF
- IDREFS [list]
- ENTITY
- NMTOKEN
- NMTOKENS [list]
=head1 OVERLOADED OPERATORS
Overloading is implemented via Class::Std's trait mechanism.
The following behaviours apply:
=over
=item * string context
All classes use the C<serialize> method for stringification.
=item * bool context
All classes derived from anySimpleType return their value in bool context
=item * numeric context
The boolean class returns 0 or 1 in numeric context.
decimal, float and double (and derived classes) return their value in
numeric context.
=back
=head1 BUGS AND LIMITATIONS
=over
=item * Thread safety
SOAP::WSDL::XSD::Typelib::Builtin uses Class::Std::Storable which uses
Class::Std. Class::Std is not thread safe, so
SOAP::WSDL::XSD::Typelib::Builtin is neither.
=item * XML Schema facets
No facets are implemented yet.
=back
=head1 AUTHOR
Replace whitespace by @ in e-mail address.
Martin Kutter E<gt>martin.kutter fen-net.deE<lt>
=head1 COPYING
This library is free software, you may distribute/modify it under the
same terms as perl itself
=cut
+184
View File
@@ -0,0 +1,184 @@
#!/usr/bin/perl
package SOAP::WSDL::XSD::Typelib::ComplexType;
use strict;
use Carp;
use SOAP::WSDL::XSD::Typelib::Builtin;
use Scalar::Util qw(blessed);
use Class::Std::Storable;
use Data::Dumper;
use base qw(SOAP::WSDL::XSD::Typelib::Builtin::anyType);
my %ELEMENTS_FROM;
my %ATTRIBUTES_OF;
my %CLASSES_OF;
# we store per-class elements.
# call as __PACKAGE__->_factory
sub _factory {
my $class = shift;
$ELEMENTS_FROM{ $class } = shift;
$ATTRIBUTES_OF{ $class } = shift;
$CLASSES_OF{ $class } = shift;
no strict qw(refs);
while (my ($name, $attribute_ref) = each %{ $ATTRIBUTES_OF{ $class } } )
{
my $type = $CLASSES_OF{ $class }->{ $name }
or die "No class given for $name";
eval "require $type" if not eval { $type->isa('UNIVERSAL') };
croak $@ if $@;
*{ "$class\::set_$name" } = sub {
my ($self, $value) = @_;
# set to a) value if it's an object
# b) New object with value for simple vlues
# c) New object with value for list values and list type
# d) List ref of new objects with value for list values and non-list type
$attribute_ref->{ ident $self } = (blessed $value)
? $value
: (ref $value && ref $value eq 'ARRAY')
? $type->isa('SOAP::WSDL::XSD::Typelib::Builtin::list')
? $type->new({ value => $value })
: [ map { $type->new({ value => $_ }) } @{ $value } ]
: $type->new({ value => $value });
};
*{ "$class\::add_$name" } = sub {
my ($self, $value) = @_;
my $ident = ident $self;
if (not defined $value) {
warn "attempting to add empty value to " . ref $self;
}
return $attribute_ref->{ $ident } = $value
if not defined $attribute_ref->{ $ident };
# listify previous value if it's no list
$attribute_ref->{ $ident } = [ $attribute_ref->{ $ident } ]
if not ref $attribute_ref->{ $ident } eq 'ARRAY';
# add to list
return push @{ $attribute_ref->{ $ident } }, $value;
};
}
# we need our own destructor - on the fly generated classes
# don't necessarily go through Clas::Std::Storable's DEMOLISH
# calls.
# my $destructor_ref = *{ "$class\::DESTROY" };
# no warnings qw(redefine);
# *{ "$class\::DESTROY" } = sub {
# my $self = shift;
# my $class = shift;
# for (@{ $ELEMENTS_FROM{ $class } }) {
# delete $_->{ ident $self };
# }
# call original destructor.
# $destructor_ref->( $self, @_) if ref ($destructor_ref);
# };
#
}
sub START {
my ($self, $ident, $args_of) = @_;
my $class = ref $self;
for my $name (keys %{ $ATTRIBUTES_OF{ $class } } ) {
my $method = "set_$name";
$self->$method( $args_of->{ $name } )
if $args_of->{ $name };
}
}
sub _get_elements {
my $self = shift;
my $class = ref $self;
my $ident = ident $self;
return map { $_->{ $ident } } @{ $ELEMENTS_FROM{ $class } };
}
# this serialize method works fine for <all> and <sequence>
# complextypes, as well as for <restriction><all> or
# <restriction><sequence>.
# But what about choice, group, extension ?
#
sub _serialize {
my $self = shift;
my $ident = ident $self;
my $class = ref $self;
# return concatenated return value of serialize call of all
# elements retrieved from get_elements expanding list refs.
# get_elements is inlined for performance.
return join q{} , map {
my $element = $ATTRIBUTES_OF{ $class }->{ $_ }->{$ident };
$element = [ $element ]
if not ref $element eq 'ARRAY';
my $name = $_;
map {
# skip empty elements - complexTypes may have empty elements
# (minOccurs 0).
if (not $_) {
q{}
}
# serialize element elements with their own serializer
# but name them like they're named here.
elsif ( $_->isa( 'SOAP::WSDL::XSD::Typelib::Element' ) ) {
$_->serialize( { name => $_ } );
}
# serialize complextype elments (of other types) with their
# serializer, but add element tags around.
else {
join q{}, $_->start_tag({ name => $name })
, $_->serialize()
, $_->end_tag({ name => $name });
}
} @{ $element }
} (@{ $ELEMENTS_FROM{ $class } });
}
sub serialize {
my ($self, $opt) = @_;
my $class = ref $self;
$opt ||= {};
# do we have a empty element ?
return $self->start_tag({ %$opt, empty => 1 })
if not @{ $ELEMENTS_FROM{ $class } };
return join q{}, $self->start_tag($opt),
$self->_serialize(), $self->end_tag();
}
1;
=pod
=head1 Bugs and limitations
=over
=item * Thread safety
SOAP::WSDL::XSD::Typelib::Builtin uses Class::Std::Storable which uses
Class::Std. Class::Std is not thread safe, so
SOAP::WSDL::XSD::Typelib::Builtin is neither.
=item * XML Schema facets
No facets are implemented yet.
=back
=head1 AUTHOR
Replace whitespace by @ in e-mail address.
Martin Kutter E<gt>martin.kutter fen-net.deE<lt>
=head1 COPYING
This library is free software, you may distribute/modify it under the
same terms as perl itself
=cut
+101
View File
@@ -0,0 +1,101 @@
#!/usr/bin/perl
package SOAP::WSDL::XSD::Typelib::Element;
use strict;
use Class::Std::Storable;
use Data::Dumper;
my %NAME;
my %NILLABLE;
my %REF;
my %MIN_OCCURS;
my %MAX_OCCURS;
# Class data - remember, we're the base class for a class factory or for
# generated code...
# use BLOCK: for scoping
BLOCK: {
my %method_lookup = (
_name => \%NAME,
_nillable => \%NILLABLE,
_ref => \%REF,
_minOccurs => \%MIN_OCCURS,
_maxOccurs => \%MAX_OCCURS,
);
no strict qw(refs);
while (my ($name, $value) = each %method_lookup ) {
*{ "__set$name" } = sub {
my $class = ref $_[0] || $_[0];
$value->{ $class } = $_[1];
};
*{ "__get$name" } = sub {
my $class = ref $_[0] || $_[0];
return $value->{ $class };
};
}
};
sub start_tag {
my ($self, $opt, $value) = @_;
my $class = ref $self;
my $ending = '>';
my @attr_from = ();
$ending = '/>' if ($opt->{ empty });
if ($opt->{qualified}) {
push @attr_from, 'xmlns="' . $self->get_xmlns . '"';
}
if ($opt->{ nil }) {
return q{} if not $NILLABLE{ $class };
push @attr_from, 'xsi:nil="true"';
$ending = '/>';
}
return join q{ }, "<$NAME{$class}" , @attr_from , $ending;
}
sub end_tag {
my ($self, $class) = ($_[0], ref $_[0]);
return "</$NAME{$class}>";
}
1;
=pod
=head1 SYNOPSIS
package MyElement;
use strict;
use Class::Std::Storable;
use base (
'SOAP::WSDL::XSD::Typelib::Element',
);
__PACKAGE__->__set_name('MyElementName');
__PACKAGE__->__set_nillable(1);
__PACKAGE__->__set_minOccurs(1);
__PACKAGE__->__set_maxOccurs(1);
__PACKAGE__->__set_ref(1);
=head1 BUGS AND LIMITATIONS
=over
=item * minOccurs maxOccurs ref not implemented
These attributes are not yet supported, though they may be set as class
properties via __PACKAGE__->__set_FOO methods.
=item * 'http://www.w3.org/2001/XMLSchema-instance prefix is hardcoded
The prefix for 'http://www.w3.org/2001/XMLSchema-instance (used as namespace
for the {http://www.w3.org/2001/XMLSchema-instance}nil="true" attribute
is hardcoded as 'xsi'.
You should definitly provide your XML envelope generator with the same prefix
namespace combination (Default for SOAP::WSDL::Envelope).
=back
=cut
+130
View File
@@ -0,0 +1,130 @@
#!/usr/bin/perl
package SOAP::WSDL::XSD::Typelib::SimpleType;
use strict;
use Class::Std::Storable;
use SOAP::WSDL::XSD::Typelib::Builtin;
package SOAP::WSDL::XSD::Typelib::SimpleType::restriction;
use strict;
use Class::Std::Storable;
use SOAP::WSDL::XSD::Typelib::Builtin;
use base qw(SOAP::WSDL::XSD::Typelib::SimpleType);
1;
__END__
=pod
=head1 NAME
SOAP::WSDL::XSD::Typelib::SimpleType - simple type base class
=head1 DESCRIPTION
This module implements a base class for designing simple type classes
modelling XML Schema simpleType definitions.
=head1 SYNOPSIS
# example simpleType derived by restriction
# XSD would be:
# <simpleType name="MySimpleType">
# <restriction base="xsd:string" />
# </simpleType>
package MySimpleType;
use Class::Std::Storable;
# restriction base implemented via inheritance
use SOAP::WSDL::XSD::Typelib::Builtin;
use SOAP::WSDL::XSD::Typelib::SimpleType;
use base qw(
# derive by restriction
'SOAP::WSDL::XSD::Typelib::SimpleType::restriction',
# restriction base
'SOAP::WSDL::XSD::Typelib::Builtin::string'
);
# example simpleType derived by list.
# XSD would be:
# <simpleType name="MySimpleListType">
# <list itemTipe="xsd:string" />
# </simpleType>
package MySimpleListType;
use Class::Std::Storable;
# restriction base implemented via inheritance
use SOAP::WSDL::XSD::Typelib::Builtin;
use base ('SOAP::WSDL::XSD::Typelib::SimpleType',
'SOAP::WSDL::XSD::Typelib::Builtin::list', # derive by list
'SOAP::WSDL::XSD::Typelib::Builtin::string' # list itemType
);
=head1 How to write your own simple type
Writing a simple type class is easy - all you have to do is setting up the
base classes correctly.
The following rules apply:
=over
=item * simpleType derived via list
simpleType classes derived via list must inherit from these classes in
exactly this order:
SOAP::WSDL::XSD::Typelib::SimpleType
SOAP::WSDL::XSD::Typelib::Builtin::list # derive by list
The::List::ItemType::Class # list itemType
The::List::ItemType::Class can either be a builtin class (see
SOAP::WSDL::XSD::Builtin) or another simpleType class (any other class
implementing the right methods is supported too, but not for the
faint of heart).
=item * simpleType derived via restriction
simpleType classes derived via restriction must inherit from these classes in
exactly this order:
SOAP::WSDL::XSD::Typelib::SimpleType # you may leave out this
SOAP::WSDL::XSD::Typelib::SimpleType::restriction # derive by retriction
The::Restriction::Base::Class # resytriction base
The::Restriction::Base::Class can either be a builtin class (see
SOAP::WSDL::XSD::Builtin) or another simpleType class.
The slight inconsistency between the these variants is caused by the
restriction element, which has different meanings for simpleType and
complexType definitions.
=head1 Bugs and limitations
=over *
=item * Thread safety
SOAP::WSDL::XSD::Typelib::SimpleType uses Class::Std::Storable which uses
Class::Std. Class::Std is not thread safe, so
SOAP::WSDL::XSD::Typelib::SimpleType is neither.
=item * union
union simple types are not supported yet.
=item * XML Schema facets
No facets are implemented yet.
=back
=head1 AUTHOR
Replace whitespace by @ in e-mail address.
Martin Kutter E<gt>martin.kutter fen-net.deE<lt>
=head1 COPYING
This library is free software, you may distribute/modify it under the
same terms as perl itself
=cut