/*
 *        JacORB - a free Java ORB
 *
 *   Copyright (C) 1997-2000  Gerald Brose.
 *
 *   This library is free software; you can redistribute it and/or
 *   modify it under the terms of the GNU Library General Public
 *   License as published by the Free Software Foundation; either
 *   version 2 of the License, or (at your option) any later version.
 *
 *   This library is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *   Library General Public License for more details.
 *
 *   You should have received a copy of the GNU Library General Public
 *   License along with this library; if not, write to the Free
 *   Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

package jacorb.idl;

/**
 * A table of defined names
 *
 * @author Gerald Brose
 * @version $Id: NameTable.java,v 1.8 2000/06/23 12:51:50 brose Exp $
 *
 */

import java.util.*;
import java.io.*;

class NameTable 
{
    private static java.util.Hashtable h = new java.util.Hashtable(5000);
    private static java.util.Hashtable shadows = 
        new java.util.Hashtable();
    private static java.util.Hashtable ancestors = 
        new java.util.Hashtable();
    private static String s = "";

    public static java.util.Hashtable parsed_interfaces = 
        new java.util.Hashtable();

    static
    {
        h.put("char", "type");
        h.put("boolean", "type");
        h.put("long", "type");
        h.put("long", "type");
        h.put("short", "type");
        h.put("int", "type");
        h.put("float", "type");
        h.put("double", "type");
        h.put("byte", "type");
        h.put("void", "type");
        h.put("org.omg.CORBA.Any", "type");
        h.put("org.omg.CORBA.Object", "interface");
    }


    /**
     *  define a name. If it has already been defined in this scope, 
     *  an exception is thrown 
     * 
     *  @param String name The name to be defined
     *  @param String kind the type of name, e.g. "type"
     *  @throw NameAlreadyDefined if the name is already defined
     */

    public static void define( String name, String kind ) 
        throws NameAlreadyDefined 
    {
        Environment.output( 3,
                            "NameTable.define2: putting " + 
                            name + " kind " + kind + " hash: " + name.hashCode() );

        if( h.containsKey( name ) )
        {
            // if this name has been inherited, it is "shadowed"
            // in this case, it is redefined if it is not an operation
            // or interface name. If it has been
            // explicitly defined in this scope, we have an error

            if( !shadows.containsKey( name ) || 
                kind.equals("operation") || kind.equals("interface"))
                throw new NameAlreadyDefined();
            else 
            {
                // redefine
                shadows.remove( name );
                h.remove( name );
            }
        } 
        h.put( name, kind );
    }

    /**
     *  define a shadowed name, i.e. an inherited name
     *  @throw NameAlreadyDefined if the name is already defined
     */

    private static void defineShadows(Hashtable shadowEntries) 
        throws NameAlreadyDefined 
    {
        for( Enumeration e = shadowEntries.keys(); e.hasMoreElements();)
        {
            String name = (String)e.nextElement();
            if( h.containsKey( name ) )
            {
                throw new NameAlreadyDefined(name);
            } 
            else 
            {
                h.put( name, shadowEntries.get(name) );
                shadows.put( name, "" );
            }
        }
    }

    /**
     * copy names declared in an ancestor interface to the local scope
     */

    public static synchronized void inheritFrom( String name, SymbolList ancestors )
    {
        Hashtable shadowNames = new Hashtable();
        for( Enumeration e = h.keys(); e.hasMoreElements();)
        {
            String key = (String)e.nextElement();
            String s = null;
            if( key.indexOf('.') > 0)
                s = key.substring(0,key.lastIndexOf('.'));
            else
                continue;

            for(Enumeration i=ancestors.v.elements(); i.hasMoreElements();)
            {
                String anc = ((ScopedName)(i.nextElement())).resolvedName();
                if( s.equals( anc ))
                {
                    String kind = (String)h.get(key);
                    Environment.output( 4, "NameTable.inheritFrom ancestor " +  
                                        anc +  " : key " + key + " kind " + kind);

                    shadowNames.put( name + key.substring(key.lastIndexOf('.')),kind);

                    // if the name we inherit is a typedef'd name, we need to
                    // to typedef the inherited name as well
                    if( kind.equals("type"))
                    {
                        Environment.output(4,"- NameTable.inheritFrom: key " + key );

                        try 
                        {
                            TypeSpec t = TypeMap.map(anc+key.substring(key.lastIndexOf('.')));

                            // t can be null for some cases where we had to put
                            // Java type names (e.g. for sequence s) into the
                            // name table. These need not be typedef'd again here

                            if( t != null )
                            {
                                TypeMap.typedef(name+
                                                key.substring(key.lastIndexOf('.')),t);
                            }
                        } 
                        catch ( NameAlreadyDefined nad )
                        {
                            System.err.println("Problem " + name + " inherits (multiple inh.)");                                        
                            nad.printStackTrace();
                        }
                    }
                    if( !defined( key ))
                        throw new RuntimeException("ComilerError!");
                }
            }
        }

        /* update the hashtable */

        try
        {
            defineShadows( shadowNames );
        } 
        catch ( NameAlreadyDefined nad )
        {
            // System.err.println( nad.getMessage + " already defined, " + name + " inherits (multiple inh.)");                                 
        }
    }

    /** 
     * check whether name is already defined
     */

    public static boolean defined( String name )
    {
        return( h.containsKey( name ));
    }

    public static boolean defined( String name, String kind )
    {
        if( !h.containsKey( name )){
            return false;
        }
        String k = (String)h.get(name);
        return( k.compareTo(kind) == 0 );
    }

    static boolean baseType(String _s)
    {
        return ( _s.equals("int") || _s.equals("short") || _s.equals("long") || 
                 _s.equals("float") || _s.equals("boolean") || 
                 _s.equals("double") || _s.equals("byte") || _s.equals("char") || 
                 _s.equals("void") || _s.equals("<anon>"));
    }

    static Enumeration getGlobalTypes()
    {
        Vector v = new Vector();
        for( Enumeration e = h.keys(); e.hasMoreElements(); )
        {          
            String str =  (String)e.nextElement();
            if( str.indexOf('.')== -1 && !baseType(str) && 
                ( ((String)h.get( str )).equals("type") ) || ((String)h.get( str )).equals("interface"))
                v.addElement(str);
        }

        return v.elements();
    }


    /** 
     * for debugging purposes only
     * 
     */
    public static void print( PrintWriter ps)
    {
        //      for( Enumeration e = h.keys(); e.hasMoreElements();)
        //  ps.println( (String)e.nextElement());
    }

}





