package org.freehep.jaco.rtti.cpp;

/**
 * Author: JavaCC and Mark Donszelmann
 *
 * added methods to get to namespaces
 * fixed getScopeOfFullyNamedScope for global scope override
 *
 * @author Mark Donszelmann
 * @version $Id: SymtabManager.java,v 1.1.1.1 2004/05/31 05:08:51 kuromaru Exp $
 */

import java.util.Hashtable;

/**
 * Manages the symbol table and scopes within a given compilation unit.
 */
public class SymtabManager
{
   /**
    * Global symbol table indexed by the name of the scope (class/function).
    */
   Hashtable scopeTable = new Hashtable();

   /**
    * Stack of scopes. Currently max. nesting allowed is 100.
    */
   Scope[] scopeStack = new Scope[100];

   /**
    * Current depth of scope nesting.
    */
   int depth = 0;

   /**
    * Dummy at the bottom of the stack so that no need to check for null.
    */
   public SymtabManager() {
      scopeStack[depth] = new Scope(null);
   }

   /**
    * Opens a new scope (with optional name and type flag).
    */
   public Scope openScope(String scopeName, boolean isType)
   {
      Scope newScope;

      if (scopeName != null)
      {
         if (isType)
         {
            newScope = new ClassScope(scopeName, scopeStack[depth]);
            scopeStack[depth].putTypeName(scopeName, newScope);
         }
         else
         {
            newScope = new Scope(scopeName, isType, scopeStack[depth]);
         }

         scopeTable.put(scopeName, newScope);
      }
      else
         newScope = new Scope(scopeStack[depth]);

      scopeStack[++depth] = newScope;
      return newScope;
   }

   public void openScope(Scope sc)
   {
      scopeStack[++depth] = sc;
   }

   public void putTypeName(String name)
   {
      scopeStack[depth].putTypeName(name);
   }

    public void putUsingName(String name) {
        scopeStack[depth].putUsingName(name);
    }

    /**
     * searches up the scope stack to find the name.
     * return full name if found, otherwise short name
     */
    public String getName(String name) {
        int i = depth;
        String s;
      
        while (i >= 0) {
            if ((s = scopeStack[i--].getName(name)) != null)
                return s;
        }
        
        return name;
    }  

   public boolean isFullyScopedTypeName(String name)
   {
      if (name == null)
         return false;

      if (name.indexOf("::") == -1)
         return isTypeName(name);

      Scope sc = getScopeOfFullyScopedName(name);

      if (sc != null)
         return sc.isTypeName(name.substring(name.lastIndexOf("::") + 2,
                                                     name.length()));

      return false;
   }

   public boolean isTypeName(String name)
   {
      int i = depth;

      while (i >= 0)
      {
         if (scopeStack[i--].isTypeName(name))
            return true;
      }

      return false;
   }

   public void closeScope()
   {
      depth--;
   }

   /**
    * For now, we just say if it is a class name, it is OK to call it a 
    * constructor.
    */
   public boolean isCtor(String name)
   {
      if (name == null)
         return false;

      if (name.indexOf("::") == -1)
         return getScope(name) != null;

      Scope sc = getScopeOfFullyScopedName(name);

      if (sc != null && sc.parent != null)
         return sc.parent.getScope(name.substring(name.lastIndexOf("::") + 2,
                                                     name.length())) == sc;

      return false;
   }

   public Scope getCurScope()
   {
      return scopeStack[depth];
   }

   public Scope getScope(String name)
   {
      int i = depth;
      Scope sc = null;

      while (i >= 0)
         if ((sc = scopeStack[i--].getScope(name)) != null)
            return sc;

      return null;
   }

   /**
    * Returns the Scope of B in A::B::C.
    */
   public Scope getScopeOfFullyScopedName(String name)
   {
      Scope sc;
      int i = 0, j = 0;

      // no scope_override provided
      if (name.indexOf("::") == -1)
         return getScope(name);

      // global scope override
      if (name.indexOf("::") == 0)
      {
         sc = scopeStack[1];
         j = 2;
      }
      else
         sc = getCurScope();

      int pos = name.lastIndexOf("::");
      String tmp = (pos == 0) ? name.substring(2) : name.substring(j, pos);
      
      while ((j = tmp.indexOf("::", i)) != -1)
      {
         sc = sc.getScope(tmp.substring(i, j));
         i = j + 2;

         if (sc == null)
            return null;
      }

      if (sc == getCurScope())
         return getScope(tmp.substring(i, tmp.length()));

      return sc.getScope(tmp.substring(i, tmp.length()));
   }

   public boolean isGlobalScope()
   {
      return depth == 1 || depth == 2;
   }
}
