
#
#           Interface Definition Language (OMG IDL CORBA v3.0)
#

package CORBA::IDL::UidVisitor;

use strict;
use warnings;

our $VERSION = '2.60';

use Digest::SHA1 qw(sha1_hex);

sub new {
    my $proto = shift;
    my $class = ref($proto) || $proto;
    my $self = {};
    bless($self, $class);
    my ($parser) = @_;
    $self->{symbtab} = $parser->YYData->{symbtab};
    return $self;
}

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

sub _get_uid {
    my $self = shift;
    my ($str) = @_;
    return uc(substr(sha1_hex($str),0, 16));
}

#
#   3.5     OMG IDL Specification
#

sub visitSpecification {
    my $self = shift;
    my ($node) = @_;
    if (exists $node->{list_import}) {
        foreach (@{$node->{list_import}}) {
            $self->_get_defn($_)->visit($self);
        }
    }
    foreach (@{$node->{list_export}}) {
        $self->_get_defn($_)->visit($self);
    }
}

#
#   3.6     Import Declaration
#

sub visitImport {
    my $self = shift;
    my ($node) = @_;
    foreach (@{$node->{list_decl}}) {
        $self->_get_defn($_)->visit($self);
    }
}

#
#   3.7     Module Declaration
#

sub visitModules {
    my $self = shift;
    my ($node) = @_;
    foreach (@{$node->{list_export}}) {
        $self->_get_defn($_)->visit($self);
    }
}

#
#   3.8     Interface Declaration
#

sub visitBaseInterface {
    my $self = shift;
    my ($node) = @_;
    return if (exists $node->{serial_uid});
    my $uid_str = $node->{idf};
    foreach ($node->getInheritance()) {
        my $base = $self->_get_defn($_);
        $uid_str .= $base->{idf} . $base->{serial_uid};
    }
    $node->{serial_uid} = $self->_get_uid($uid_str);
    foreach (@{$node->{list_export}}) {
        $self->_get_defn($_)->visit($self);
    }
    if (exists $node->{list_member}) {
        foreach (@{$node->{list_member}}) {
            my $defn = $self->_get_defn($_);
            my $type = $self->_get_defn($defn->{type});
            $type->visit($self);
            $uid_str .= $defn->{idf};
            $uid_str .= $type->{serial_uid} || $type->{value};
        }
    }
    $node->{serial_uid} = $self->_get_uid($uid_str);
}

sub visitForwardBaseInterface {
#   empty
}

#
#   3.9     Value Declaration
#

sub visitStateMember {
    # empty
}

sub visitInitializer {
    # empty
}

sub visitBoxedValue {
    my $self = shift;
    my ($node) = @_;
    return if (exists $node->{serial_uid});

    my $type = $self->_get_defn($node->{type});
    $type->visit($self);
    my $uid_str = $node->{idf};
    $uid_str .= $type->{serial_uid} || $type->{value};
    $node->{serial_uid} = $self->_get_uid($uid_str);
}

#
#   3.10    Constant Declaration
#

sub visitConstant {
    # empty
}

#
#   3.11    Type Declaration
#

sub visitTypeDeclarator {
    my $self = shift;
    my ($node) = @_;
    return if (exists $node->{serial_uid});
    my $type = $self->_get_defn($node->{type});
    $type->visit($self);
    my $uid_str = $node->{idf};
    $uid_str .= $type->{serial_uid} || $type->{value};
    if (exists $node->{array_size}) {
        foreach (@{$node->{array_size}}) {
            $uid_str .= '[' . $_->{value} . ']';
        }
    }
    $node->{serial_uid} = $self->_get_uid($uid_str);
}

sub visitNativeType {
    my $self = shift;
    my ($node) = @_;
    my $uid_str = $node->{idf};
    $node->{serial_uid} = $self->_get_uid($uid_str);
}

#
#   3.11.1  Basic Types
#

sub visitBasicType {
    # empty
}

#
#   3.11.2  Constructed Types
#
#   3.11.2.1    Structures
#

sub visitStructType {
    my $self = shift;
    my ($node) = @_;
    return if (exists $node->{serial_uid});
    my $uid_str = $node->{idf};
    $node->{serial_uid} = $self->_get_uid($uid_str);
    foreach (@{$node->{list_member}}) {
        my $defn = $self->_get_defn($_);
        my $type = $self->_get_defn($defn->{type});
        $type->visit($self);
        $uid_str .= $defn->{idf};
        $uid_str .= $type->{serial_uid} || $type->{value};
        if (exists $defn->{array_size}) {
            foreach (@{$defn->{array_size}}) {
                $uid_str .= '[' . $_->{value} . ']';
            }
        }
    }
    $node->{serial_uid} = $self->_get_uid($uid_str);
}

#   3.11.2.2    Discriminated Unions
#

sub visitUnionType {
    my $self = shift;
    my ($node) = @_;
    return if (exists $node->{serial_uid});
    $self->_get_defn($node->{type})->visit($self);
    my $uid_str = $node->{idf};
    my $type = $self->_get_defn($node->{type});
    $uid_str .= $type->{serial_uid} || $type->{value};
    $node->{serial_uid} = $self->_get_uid($uid_str);
    foreach my $case (@{$node->{list_expr}}) {
        my $elt = $self->_get_defn($case->{element});
        foreach my $label (@{$case->{list_label}}) {
            if (ref $label eq 'Default') {
                $uid_str .= '_default_';
            }
            else {
                if (ref $label->{value} eq 'Enum') {
                    $uid_str .= $label->{value}->{idf};
                }
                else {
                    $uid_str .= $label->{value};
                }
            }
        }
        my $defn = $self->_get_defn($elt->{value});
        my $type = $self->_get_defn($defn->{type});
        $uid_str .= $defn->{idf};
        $uid_str .= ($type->{serial_uid} || $type->{value} || q{});
        if (exists $defn->{array_size}) {
            foreach (@{$defn->{array_size}}) {
                $uid_str .= '[' . $_->{value} . ']';
            }
        }
    }
    $node->{serial_uid} = $self->_get_uid($uid_str);
}

#   3.11.2.4    Enumerations
#

sub visitEnumType {
    my $self = shift;
    my ($node) = @_;
    my $uid_str = $node->{idf};
    foreach (@{$node->{list_expr}}) {
        $uid_str .= $_->{idf};
    }
    $node->{serial_uid} = $self->_get_uid($uid_str);
}

#
#   3.11.3  Template Types
#

sub visitSequenceType {
    my $self = shift;
    my ($node) = @_;
    return if (exists $node->{serial_uid});
    my $type = $self->_get_defn($node->{type});
    $type->visit($self);
    my $uid_str = '_seq_';
    $uid_str .= $type->{serial_uid} || $type->{value};
    if (exists $node->{max}) {
        $uid_str .= '_max_' . $node->{max}->{value};
    }
    $node->{serial_uid} = $self->_get_uid($uid_str);
}

sub visitStringType {
    my $self = shift;
    my ($node) = @_;
    my $uid_str = $node->{value};
    if (exists $node->{max}) {
        $uid_str .= '_max_' . $node->{max}->{value};
    }
    $node->{serial_uid} = $self->_get_uid($uid_str);
}

sub visitWideStringType {
    my $self = shift;
    my ($node) = @_;
    my $uid_str = $node->{value};
    if (exists $node->{max}) {
        $uid_str .= '_max_' . $node->{max}->{value};
    }
    $node->{serial_uid} = $self->_get_uid($uid_str);
}

sub visitFixedPtType {
    my $self = shift;
    my ($node) = @_;
    my $uid_str .= '_d_' . $node->{d}->{value};
    $uid_str .= '_s_' . $node->{s}->{value};
    $node->{serial_uid} = $self->_get_uid($uid_str);
}

#
#   3.12    Exception Declaration
#

sub visitException {
    shift->visitStructType(@_);
}

#
#   3.13    Operation Declaration
#

sub visitOperation {
    # empty
}

#
#   3.14    Attribute Declaration
#

sub visitAttribute {
    # empty
}

#
#   3.15    Repository Identity Related Declarations
#

sub visitTypeId {
    # empty
}

sub visitTypePrefix {
    # empty
}

#
#   3.16    Event Declaration
#

#
#   3.17    Component Declaration
#

sub visitProvides {
    # empty
}

sub visitUses {
    # empty
}

sub visitPublishes {
    # empty
}

sub visitEmits {
    # empty
}

sub visitConsumes {
    # empty
}

#
#   3.18    Home Declaration
#

sub visitFactory {
    # empty
}

sub visitFinder {
    # empty
}

1;

