#
#           Interface Definition Language (OMG IDL CORBA v3.0)
#
#           CORBA to WSDL/SOAP Interworking Specification, Version 1.1 February 2005
#

package CORBA::XMLSchemas::WsdlVisitor;

use strict;
use warnings;

our $VERSION = '2.60';

use CORBA::XMLSchemas::BaseVisitor;
use base qw(CORBA::XMLSchemas::BaseVisitor);

use File::Basename;
use POSIX qw(ctime);
use XML::DOM;
use CORBA::XMLSchemas::XsdImportVisitor;
use CORBA::XMLSchemas::RelaxngImportVisitor;

# needs $node->{xsd_name} $node->{xsd_qname} (XsdNameVisitor)

sub new {
    my $proto = shift;
    my $class = ref($proto) || $proto;
    my $self = {};
    bless $self, $class;
    my ($parser, $ext_schema) = @_;
    $self->{beautify} = $parser->YYData->{opt_t};
    $self->{ext_schema} = 'xsd:';
    $self->{ext_schema} = $ext_schema . ':' if (defined $ext_schema);
    $self->{_tns} = 'tns';
    $self->{tns} = 'tns:';
    $self->{_xsd} = 'xs';
    $self->{xsd} = 'xs:';
    $self->{_wsdl} = 'wsdl';
    $self->{wsdl} = $parser->YYData->{opt_q} ? 'wsdl:' : q{};
    $self->{_corba} = 'corba';
    $self->{corba} = 'corba:';
    $self->{srcname} = $parser->YYData->{srcname};
    $self->{srcname_size} = $parser->YYData->{srcname_size};
    $self->{srcname_mtime} = $parser->YYData->{srcname_mtime};
    $self->{symbtab} = $parser->YYData->{symbtab};
    $self->{base} = $parser->YYData->{opt_b} || q{};
    $self->{root} = $parser->YYData->{root};
    my $filename = basename($self->{srcname}, '.idl') . '.wsdl';
    $self->open_stream($filename);
    $self->{done_hash} = {};
    $self->{num_key} = 'num_inc_wsdl';
    if ($self->{ext_schema} eq 'rng:') {
        $self->{schema_visitor} = new CORBA::XMLSchemas::RelaxngImportVisitor($parser);
    }
    else {
        $self->{schema_visitor} = new CORBA::XMLSchemas::XsdImportVisitor($parser);
    }
    $self->{ports} = [];
    return $self;
}

#
#   3.5     OMG IDL Specification
#

sub visitSpecification {
    my $self = shift;
    my ($node) = @_;
    my $FH = $self->{out};

    $self->{dom_doc} = new XML::DOM::Document();
    $self->{dom_parent} = $self->{dom_doc};

    my $definitions = $self->{dom_doc}->createElement($self->{wsdl} . 'definitions');
    $definitions->setAttribute('targetNamespace', 'http://www.omg.org/IDL-Mapped/');
    $definitions->setAttribute('xmlns:' . $self->{_tns}, 'http://www.omg.org/IDL-Mapped/');
    $definitions->setAttribute('xmlns:' . $self->{_xsd}, 'http://www.w3.org/2001/XMLSchema');
    if ($self->{wsdl}) {
        $definitions->setAttribute('xmlns:' . $self->{_wsdl}, 'http://schemas.xmlsoap.org/wsdl/');
    }
    else {
        $definitions->setAttribute('xmlns', 'http://schemas.xmlsoap.org/wsdl/');
    }
    $definitions->setAttribute('xmlns:' . $self->{_corba}, 'http://www.omg.org/IDL-WSDL/1.0/');
    $self->{dom_parent}->appendChild($definitions);

    my $import = $self->{dom_doc}->createElement($self->{wsdl} . 'import');
    $import->setAttribute('namespace', 'http://www.omg.org/IDL-WSDL/1.0/');
    $import->setAttribute('location', $self->{base} . 'corba.wsdl');
    $definitions->appendChild($import);

    my $types = $self->{dom_doc}->createElement($self->{wsdl} . 'types');
    $definitions->appendChild($types);

    $node->visit($self->{schema_visitor}, $self->{dom_doc}, $types);

    foreach (@{$node->{list_decl}}) {
        $self->_get_defn($_)->visit($self, $definitions);
    }
    foreach (@{$self->{ports}}) {
        $definitions->appendChild($_);
    }

    if ($self->{beautify}) {
        print $FH "<!-- This file was generated (by ", $0, "). DO NOT modify it -->\n";
        print $FH "<!-- From file : ", $self->{srcname}, ", ", $self->{srcname_size}, " octets, ", POSIX::ctime($self->{srcname_mtime});
        print $FH "-->\n";
        print $FH "\n";
        print $FH $self->_beautify($self->{dom_doc}->toString());
        print $FH "\n\n";
        print $FH "<!-- end of file : ", $self->{filename}, " -->\n";
    }
    else {
        print $FH $self->{dom_doc}->toString();
    }
    close $FH;
    $self->{dom_doc}->dispose();
}

#
#   3.8     Interface Declaration
#
#   See 1.2.8       Interfaces
#

sub visitRegularInterface {
    my $self = shift;
    my ($node, $dom_parent) = @_;

    my $str = ' interface: ' . $node->{xsd_name} . q{ };
    my $comment = $self->{dom_doc}->createComment($str);
    $dom_parent->appendChild($comment);

    foreach (@{$node->{list_decl}}) {
        $self->_get_defn($_)->visit($self, $dom_parent);
    }

    if (scalar keys %{$node->{hash_attribute_operation}}) {
        my $str = ' port for ' . $node->{xsd_name} . q{ };
        my $comment = $self->{dom_doc}->createComment($str);
        push @{$self->{ports}}, $comment;

        my $portType = $self->{dom_doc}->createElement($self->{wsdl} . 'portType');
        $portType->setAttribute('name', $node->{xsd_name});
        push @{$self->{ports}}, $portType;

        foreach (values %{$node->{hash_attribute_operation}}) {
            my $defn = $self->_get_defn($_);
            if ($defn->isa('Operation')) {
                $self->_operation($defn, $portType);
            }
            else {
                $self->_operation($defn->{_get}, $portType);
                $self->_operation($defn->{_set}, $portType)
                        if (exists $defn->{_set});
            }
        }
    }
}

sub _operation {
    my $self = shift;
    my ($node, $dom_parent) = @_;

    my $operation = $self->{dom_doc}->createElement($self->{wsdl} . 'operation');
    $operation->setAttribute('name', $node->{idf});
    $dom_parent->appendChild($operation);

    my $input = $self->{dom_doc}->createElement($self->{wsdl} . 'input');
    $input->setAttribute('message', $self->{tns} . $node->{xsd_name});
    $operation->appendChild($input);

    my $output = $self->{dom_doc}->createElement($self->{wsdl} . 'output');
    $output->setAttribute('message', $self->{tns} . $node->{xsd_name} . 'Response');
    $operation->appendChild($output);

    foreach (@{$node->{list_raise}}) {
        my $defn = $self->_get_defn($_);

        my $fault = $self->{dom_doc}->createElement($self->{wsdl} . 'fault');
        $fault->setAttribute('name', $defn->{xsd_name});
        $fault->setAttribute('message', $self->{tns} . '_exception.' . $defn->{xsd_name});
        $operation->appendChild($fault);
    }

    unless (exists $node->{modifier}) {     # oneway
        my $fault = $self->{dom_doc}->createElement($self->{wsdl} . 'fault');
        $fault->setAttribute('name', 'CORBA.SystemException');
        $fault->setAttribute('message', $self->{corba} . 'CORBA.SystemExceptionMessage');
        $operation->appendChild($fault);
    }
}

sub visitAbstractInterface {
    my $self = shift;
    my ($node, $dom_parent) = @_;

    my $str = ' abstract interface: ' . $node->{xsd_name} . q{ };
    my $comment = $self->{dom_doc}->createComment($str);
    $dom_parent->appendChild($comment);

    foreach (@{$node->{list_decl}}) {
        $self->_get_defn($_)->visit($self, $dom_parent);
    }
}

#
#   3.9     Value Declaration
#

sub visitValue {
    # empty
}

#
#   3.11    Type Declaration
#

sub visitTypeDeclarator {
    # empty
}

sub visitStructType {
    # empty
}


sub visitUnionType {
    # empty
}

sub visitEnumType {
    # empty
}

#
#   3.12    Exception Declaration
#

sub visitException {
    # empty
}

#
#   3.13    Operation Declaration
#
#   See 1.2.8.2     Interface as Binding Operations
#

sub visitOperation {
    my $self = shift;
    my ($node, $dom_parent) = @_;

    my $message = $self->{dom_doc}->createElement($self->{wsdl} . 'message');
    $message->setAttribute('name', $node->{xsd_name});
    $dom_parent->appendChild($message);

    foreach (@{$node->{list_param}}) {  # parameter
        if (       $_->{attr} eq 'in'
                or $_->{attr} eq 'inout' ) {
            $_->visit($self, $message);     # parameter
        }
    }

    my $type = $self->_get_defn($node->{type});
    $message = $self->{dom_doc}->createElement($self->{wsdl} . 'message');
    $message->setAttribute('name', $node->{xsd_name} . 'Response');
    $dom_parent->appendChild($message);

    unless ($type->isa('VoidType')) {
        my $part = $self->{dom_doc}->createElement($self->{wsdl} . 'part');
        $part->setAttribute('name', '_return');
        $part->setAttribute('type', $type->{xsd_qname});
        $message->appendChild($part);
    }

    foreach (@{$node->{list_param}}) {  # parameter
        if (       $_->{attr} eq 'inout'
                or $_->{attr} eq 'out' ) {
            $_->visit($self, $message);     # parameter
        }
    }

    foreach (@{$node->{list_raise}}) {
        my $defn = $self->_get_defn($_);

        my $message = $self->{dom_doc}->createElement($self->{wsdl} . 'message');
        $message->setAttribute('name', '_exception.' . $defn->{xsd_name});
        $dom_parent->appendChild($message);

        my $part = $self->{dom_doc}->createElement($self->{wsdl} . 'part');
        $part->setAttribute('name', 'exception');
        $part->setAttribute('type', $defn->{xsd_qname});
        $message->appendChild($part);
    }
}

sub visitParameter {
    my $self = shift;
    my ($node, $dom_parent) = @_;
    my $type = $self->_get_defn($node->{type});

    my $part = $self->{dom_doc}->createElement($self->{wsdl} . 'part');
    $part->setAttribute('name', $node->{xsd_name});
    $part->setAttribute('type', $type->{xsd_qname});
    $dom_parent->appendChild($part);
}

1;

