
#
#           Interface Definition Language (OMG IDL CORBA v3.0)
#
#           C Language Mapping Specification, New Edition June 1999
#

package CORBA::C::SkeletonVisitor;

use strict;
use warnings;

our $VERSION = '2.60';

use File::Basename;
use POSIX qw(ctime);

# needs $node->{c_name} (CnameVisitor) and $node->{c_arg} (CincludeVisitor)

sub new {
    my $proto = shift;
    my $class = ref($proto) || $proto;
    my $self = {};
    bless $self, $class;
    my ($parser, $prefix) = @_;
    $prefix = 'skel_' if (!defined $prefix);
    $self->{prefix} = $prefix;
    $self->{srcname} = $parser->YYData->{srcname};
    $self->{srcname_size} = $parser->YYData->{srcname_size};
    $self->{srcname_mtime} = $parser->YYData->{srcname_mtime};
    $self->{symbtab} = $parser->YYData->{symbtab};
    my $filename = $prefix . basename($self->{srcname}, '.idl') . '.c';
    $self->parse($filename);
    $self->open_stream($filename);
    $self->{done_hash} = {};
    $self->{num_key} = 'num_skel_c';
    return $self;
}

sub parse {
    my $self = shift;
    my ($filename) = @_;
    $self->{merge} = {};
    $self->{merge_comment} = {};
    return unless ( -r $filename);
    open my $IN, '<', $filename
            or die "can't open $filename ($!).\n";
    while (<$IN>) {
        if (/\/\* START_EDIT (\(([^\)]+)\) )?\*\//) {
            my $key = $2 || $self->{srcname};
            my $code = q{};
            while (<$IN>) {
                last if (/\/\* STOP_EDIT/);
                $code .= $_;
            }
            $self->{merge}->{$key} = $code;
        }
        elsif (/\/\* START_COMMENT (\(([^\)]+)\) )?\*\//) {
            my $key = $2 || $self->{srcname};
            my $code = q{};
            while (<$IN>) {
                last if (/\/\* STOP_COMMENT/);
                $code .= $_;
            }
            $self->{merge_comment}->{$key} = $code;
        }
    }
    close $IN;
}

sub merge {
    my $self = shift;
    my ($key, $default) = @_;
    $default = q{} unless ($default);
    $key = $self->{srcname} unless ($key);
    if (exists $self->{merge}->{$key}) {
        return $self->{merge}->{$key};
    }
    else {
        return $default . "\n"
    }
}

sub merge_comment {
    my $self = shift;
    my ($key) = @_;
    $key = $self->{srcname} unless ($key);
    if (exists $self->{merge_comment}->{$key}) {
        return $self->{merge_comment}->{$key};
    }
    else {
        return q{};
    }
}

sub open_stream {
    my $self = shift;
    my ($filename) = @_;
    open $self->{out}, '>', $filename
            or die "can't open $filename ($!).\n";
    $self->{filename} = $filename;
}

sub _get_defn {
    my $self = shift;
    my ($defn) = @_;
    if (ref $defn) {
        return $defn;
    }
    else {
        return $self->{symbtab}->Lookup($defn);
    }
}

#
#   3.5     OMG IDL Specification
#

sub visitSpecification {
    my $self = shift;
    my ($node) = @_;
    my $filename = $self->{prefix} . basename($self->{srcname}, '.idl') . '.h';
    my $FH = $self->{out};
    print $FH "/* This file was partialy generated (by ",basename($0),").*/\n";
    print $FH "/* From file : ",$self->{srcname},", ",$self->{srcname_size}," octets, ",POSIX::ctime($self->{srcname_mtime});
    print $FH " */\n";
    print $FH "\n";
    print $FH "/* START_EDIT */\n";
    print $FH $self->merge();
    print $FH "/* STOP_EDIT */\n";
    print $FH "\n";
    print $FH "#include \"",$filename,"\"\n";
    print $FH "\n";
    foreach (@{$node->{list_decl}}) {
        $self->_get_defn($_)->visit($self);
    }
    print $FH "\n";
    print $FH "/* end of file : ",$self->{filename}," */\n";
    close $FH;
}

#
#   3.7     Module Declaration
#

sub visitModules {
    my $self = shift;
    my ($node) = @_;
    unless (exists $node->{$self->{num_key}}) {
        $node->{$self->{num_key}} = 0;
    }
    my $module = ${$node->{list_decl}}[$node->{$self->{num_key}}];
    $module->visit($self);
    $node->{$self->{num_key}} ++;
}

sub visitModule {
    my $self = shift;
    my ($node) = @_;
    if ($self->{srcname} eq $node->{filename}) {
        my $defn = $self->{symbtab}->Lookup($node->{full});
        my $FH = $self->{out};
        print $FH "/*\n";
        print $FH " * begin of module ",$defn->{c_name},"\n";
        print $FH " */\n";
        foreach (@{$node->{list_decl}}) {
            $self->_get_defn($_)->visit($self);
        }
        print $FH "/*\n";
        print $FH " * end of module ",$defn->{c_name},"\n";
        print $FH " */\n";
    }
}

#
#   3.8     Interface Declaration
#

sub visitRegularInterface {
    my $self = shift;
    my ($node) = @_;
    if ($self->{srcname} eq $node->{filename}) {
        my $FH = $self->{out};
        print $FH "/* START_EDIT (",$node->{c_name},") */\n";
        print $FH $self->merge($node->{c_name});
        print $FH "/* STOP_EDIT (",$node->{c_name},") */\n";
        print $FH "\n";
        print $FH "/*\n";
        print $FH " * begin of interface ",$node->{c_name},"\n";
        print $FH " */\n";
        $self->{itf} = $node->{c_name};
        foreach (sort keys %{$node->{hash_attribute_operation}}) {
            my $elt = ${$node->{hash_attribute_operation}}{$_};
            $self->_get_defn($elt)->visit($self);
        }
        print $FH "/*\n";
        print $FH " * end of interface ",$node->{c_name},"\n";
        print $FH " */\n";
    }
}

sub visitForwardRegularInterface {
    # empty
}

sub visitBaseInterface {
    # C mapping is aligned with CORBA 2.1
}

sub visitForwardBaseInterface {
    # C mapping is aligned with CORBA 2.1
}

#
#   3.10    Constant Declaration
#

sub visitConstant {
    # empty
}

#
#   3.11    Type Declaration
#

sub visitTypeDeclarators {
    # empty
}

sub visitTypeDeclarator {
    # empty
}

sub visitNativeType {
    # C mapping is aligned with CORBA 2.1
}

sub visitStructType {
    # empty
}

sub visitUnionType {
    # empty
}

sub visitForwardStructType {
    # empty
}

sub visitForwardUnionType {
    # empty
}

sub visitEnumType {
    # empty
}

#
#   3.12    Exception Declaration
#

sub visitException {
    # empty
}

#
#   3.13    Operation Declaration
#

sub visitOperation {
    my $self = shift;
    my ($node) = @_;
    my $name = $self->{prefix} . $self->{itf} . '_' . $node->{c_name};
    my $FH = $self->{out};
    print $FH "\n";
    print $FH "/* START_COMMENT (",$name,") */\n";
    print $FH $self->merge_comment($name);
    print $FH "/* STOP_COMMENT (",$name,") */\n";
    print $FH "/* ARGSUSED */\n";
    if (exists $node->{modifier}) {
        print $FH $node->{c_arg}," // oneway\n";
    }
    else {
        print $FH $node->{c_arg},"\n";
    }
    print $FH $name,"(\n";
    print $FH "\t",$self->{itf}," _o,\n";
    foreach (@{$node->{list_param}}) {  # parameter
        my $type = $self->_get_defn($_->{type});
        print $FH "\t",$_->{c_arg},", // ",$_->{attr};
            print $FH " (variable length)\n" if (defined $type->{length});
            print $FH " (fixed length)\n" unless (defined $type->{length});
    }
    if (exists $node->{list_context}) {
        print $FH "\tCORBA_Context _ctx,\n";
    }
    print $FH "\tCORBA_Environment * _ev\n";
    print $FH ")\n";
    print $FH "{\n";
    print $FH "/* START_EDIT (",$name,") */\n";
    my $except;
    if (exists $node->{list_raise}) {
        foreach (@{$node->{list_raise}}) {  # exception
            my $defn = $self->_get_defn($_);
            $except = "\tstatic " . $defn->{c_name} . " _" . $defn->{c_name} . ";\n";
        }
    }
    print $FH $self->merge($name, $except);
    print $FH "/* STOP_EDIT (",$name,") */\n";
    print $FH "}\n";
    print $FH "\n";
}

#
#   3.14    Attribute Declaration
#

sub visitAttribute {
    my $self = shift;
    my ($node) = @_;
    $node->{_get}->visit($self);
    $node->{_set}->visit($self) if (exists $node->{_set});
}

#
#   3.15    Repository Identity Related Declarations
#

sub visitTypeId {
    # empty
}

sub visitTypePrefix {
    # empty
}

1;

