Files
SOAP-WSDL/lib/SOAP/WSDL/Expat/WSDLParser.pm
Martin Kutter 51db446428 import SOAP-WSDL 2.00_32 from CPAN
git-cpan-module:   SOAP-WSDL
git-cpan-version:  2.00_32
git-cpan-authorid: MKUTTER
git-cpan-file:     authors/id/M/MK/MKUTTER/SOAP-WSDL-2.00_32.tar.gz
2009-12-12 19:48:24 -08:00

254 lines
7.4 KiB
Perl

package SOAP::WSDL::Expat::WSDLParser;
use strict;
use warnings;
use Carp;
use SOAP::WSDL::TypeLookup;
use base qw(SOAP::WSDL::Expat::Base);
our $VERSION = q{2.00_32};
sub _import_children {
my ($self, $name, $imported, $importer, $import_namespace) = @_;
return if not $imported;
my $targetNamespace = $importer->get_targetNamespace();
my $push_method = "push_$name";
my $get_method = "get_$name";
no strict qw(refs);
my $value_ref = $imported->$get_method();
if ($value_ref) {
$value_ref = [ $value_ref ] if (not ref $value_ref eq 'ARRAY');
# set xmlns - can be different from parent
for (@{ $value_ref }) {
# fixup targetNamespace, but don't override
$_->set_targetNamespace( $import_namespace )
if ( ($import_namespace ne $targetNamespace) && ! $_->get_targetNamespace);
# update parent...
$_->set_parent( $importer );
}
# push elements into importing WSDL
$importer->$push_method(@{ $value_ref });
}
}
sub xml_schema_import {
my $self = shift;
my $schema = shift;
my $parser = $self->clone();
my %attr_of = @_;
my $import_namespace = $attr_of{ namespace };
my $uri = URI->new_abs($attr_of{schemaLocation}, $self->get_uri() );
my $import = $parser->parse_uri($uri);
for my $name ( qw(type element group) ) {
$self->_import_children( $name, $import, $schema, $import_namespace);
}
}
sub wsdl_import {
my $self = shift;
my $definitions = shift;
my $parser = $self->clone();
my %attr_of = @_;
my $import_namespace = $attr_of{ namespace };
my $uri = URI->new_abs($attr_of{location}, $self->get_uri() );
my $import = $parser->parse_uri($uri);
for my $name ( qw(types message binding portType service) ) {
$self->_import_children( $name, $import, $definitions, $import_namespace);
}
}
sub _initialize {
my ($self, $parser) = @_;
# init object data
$self->{ parser } = $parser;
delete $self->{ data };
# setup local variables for keeping temp data
my $characters = undef;
my $current = undef;
my $list = []; # node list
# TODO skip non-XML Schema namespace tags
$parser->setHandlers(
Start => sub {
my ($parser, $localname, %attrs) = @_;
$characters = q{};
my $action = SOAP::WSDL::TypeLookup->lookup(
$parser->namespace($localname),
$localname
);
return if not $action;
if ($action->{ type } eq 'CLASS') {
eval "require $action->{ class }";
croak $@ if ($@);
my $obj = $action->{ class }->new({ parent => $current,
xmlns => { '#default' => $parser->namespace($localname) }
})
->init( _fixup_attrs( $parser, %attrs ) );
if ($current) {
# inherit namespace, but don't override
$obj->set_targetNamespace( $current->get_targetNamespace() )
if not $obj->get_targetNamespace();
# push on parent's element/type list
my $method = "push_$localname";
no strict qw(refs);
$current->$method( $obj );
# remember element for stepping back
push @{ $list }, $current;
}
else {
$self->{ data } = $obj;
}
# set new element (step down)
$current = $obj;
}
elsif ($action->{ type } eq 'PARENT') {
$current->init( _fixup_attrs($parser, %attrs) );
}
elsif ($action->{ type } eq 'METHOD') {
my $method = $action->{ method };
no strict qw(refs);
# call method with
# - default value ($action->{ value } if defined,
# dereferencing lists
# - the values of the elements Attributes hash
# TODO: add namespaces declared to attributes.
# Expat consumes them, so we have to re-add them here.
$current->$method( defined $action->{ value }
? ref $action->{ value }
? @{ $action->{ value } }
: ($action->{ value })
: _fixup_attrs($parser, %attrs)
);
}
elsif ($action->{type} eq 'HANDLER') {
my $method = $self->can($action->{method});
$method->($self, $current, %attrs);
}
else {
# TODO replace by hash lookup of known namespaces.
my $namespace = $parser->namespace($localname) || q{};
my $part = $namespace eq 'http://schemas.xmlsoap.org/wsdl/'
? 'WSDL 1.1'
: 'XML Schema';
warn "$part element <$localname> is not implemented yet"
if ($localname !~m{ \A (:? annotation | documentation ) \z }xms );
}
return;
},
Char => sub { $characters .= $_[1]; return; },
End => sub {
my ($parser, $localname) = @_;
my $action = SOAP::WSDL::TypeLookup->lookup(
$parser->namespace( $localname ),
$localname
) || {};
return if not ($action->{ type });
if ( $action->{ type } eq 'CLASS' ) {
$current = pop @{ $list };
}
elsif ($action->{ type } eq 'CONTENT' ) {
my $method = $action->{ method };
# normalize whitespace
$characters =~s{ ^ \s+ (.+) \s+ $ }{$1}xms;
$characters =~s{ \s+ }{ }xmsg;
no strict qw(refs);
$current->$method( $characters );
}
return;
}
);
return $parser;
}
# make attrs SAX style
sub _fixup_attrs {
my ($parser, %attrs_of) = @_;
my @attrs_from = map { $_ =
{
Name => $_,
Value => $attrs_of{ $_ },
LocalName => $_
}
} keys %attrs_of;
# add xmlns: attrs. expat eats them.
push @attrs_from, map {
# ignore xmlns=FOO namespaces - must be XML schema
# Other nodes should be ignored somewhere else
($_ eq '#default')
? ()
:
{
Name => "xmlns:$_",
Value => $parser->expand_ns_prefix( $_ ),
LocalName => $_
}
} $parser->new_ns_prefixes();
return @attrs_from;
}
1;
=pod
=head1 NAME
SOAP::WSDL::Expat::WSDLParser - Parse WSDL files into object trees
=head1 SYNOPSIS
my $parser = SOAP::WSDL::Expat::WSDLParser->new();
$parser->parse( $xml );
my $obj = $parser->get_data();
=head1 DESCRIPTION
WSDL parser used by SOAP::WSDL.
=head1 AUTHOR
Replace the whitespace by @ for E-Mail Address.
Martin Kutter E<lt>martin.kutter fen-net.deE<gt>
=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 Repository information
$Id: $
$LastChangedDate: 2008-02-14 18:07:18 +0100 (Do, 14 Feb 2008) $
$LastChangedRevision: 534 $
$LastChangedBy: kutterma $
$HeadURL: http://soap-wsdl.svn.sourceforge.net/svnroot/soap-wsdl/SOAP-WSDL/trunk/lib/SOAP/WSDL/Expat/WSDLParser.pm $