package ilog.language.design.types;

/**
 * @author      <a href="mailto:pviry@ilog.fr">Patrick Viry</a>
 * @copyright   &copy; 2000-2002 <a href="http://www.ilog.fr/">ILOG, S.A.</a>
 * @version     Last modified on Wed Jun 26 12:54:05 2002 by hak
 * @version          modified on Fri Jun 14 17:41:28 2002 by pviry
 * @version          modified on Thu May 23 19:17:38 2002 by paulin
 */

import java.util.ArrayList;
import java.util.HashMap;

/**
 * This is a handle for <a href="ClassType.html"><tt>ClassType</tt></a>, used when a
 * <tt>ClassType</tt> is required before it is actually known. Class members can be
 * declared in an incremental manner using <tt>addField(String,Type)</tt> and
 * <tt>addField(String,Type)</tt>. When all members are declared, one must end the
 * declaration phase with a call to <tt>setDeclared()</tt>.
 *
 * Note: If <tt>ClassType</tt> is modified to allow an incremental definition of
 * fields and methods, then this handle class <tt>ClassTypeHandle</tt> may be
 * removed and all uses of the handle replaced by the class itself.
 */
public class ClassTypeHandle
{
    /**
     *  Creating a new ClassTypeHandle declares the corresponding class.
     */
    public ClassTypeHandle(Tables tables, String name)
    {
        _tables = tables;
        _classType = _tables.declareClass(name, new ArrayList());
    }

    private Tables _tables;
    private ClassType _classType;

    private ArrayList _memberNames = new ArrayList();
    private ArrayList _memberTypes = new ArrayList();
    private ArrayList _memberInits = new ArrayList();

    /**
     *  Add a field, ie. record the field name and type in the
     *  internal data structures and declare the global variable
     *  corresponding to this field.
     */
    public void addField(String name, Type type)
        throws ClassDeclarationException
    {
        _memberNames.add(name);
        _memberTypes.add(type);
        _memberInits.add(this); // anything not null will do ... see ClassInfo.fillClassInfo()
        
        Type globalType = new FunctionType(classType(), type).flatten();
        if(_tables.symbol(name).getCodeEntryForDomains(globalType) != null) {
            throw new ClassDeclarationException("multiple declaration for field " + name);
        }
        ((DefinedEntry)_tables.symbol(name).getCodeEntry(globalType, true)).setIsField();
    }

    /**
     *  Add a method, ie. record the method name and type in the
     *  internal data structures and declare the global variable
     *  corresponding to this method.
     */
    public void addMethod(String name, Type returnType, ArrayList paramTypes)
        throws ClassDeclarationException
    {
        Type methodType;
        if(paramTypes.size() == 0) {
            methodType = returnType;
        } else {
            methodType = new FunctionType(paramTypes, returnType);
        }

        _memberNames.add(name);
        _memberTypes.add(methodType);
        _memberInits.add(null);
        
        Type globalType = new FunctionType(classType(), methodType).flatten();
        if(_tables.symbol(name).getCodeEntryForDomains(globalType) != null) {
            throw new ClassDeclarationException("multiple declaration for method " + name + "(" + paramTypes + ")");
        }
        _tables.symbol(name).getCodeEntry(globalType, true);
    }

    public ClassType classType()
    {
        return _classType;
    }
    
    public String className()
    {
        return _classType.name();
    }
    

    /**
     *  This method is used in ClassDummy to determine whether a name
     *  is a field of this class or a global symbol. The determination
     *  should also be based on the type, not only on the name, hence
     *  this method should eventually disappear.
     */
    public boolean containsName(String name)
    {
        return _memberNames.contains(name);
    }

    /**
     *  End the declaration of members with a call to this method. This
     *  will fill the appropriate data structures in the symbol table.
     */
    public void setDeclared()
    {
        _classType.classInfo().fillClassInfo(_tables, _classType, 
                                             _memberNames, _memberTypes, _memberInits, 
                                             null);
    }

    public String toString()
    {
        StringBuffer buf = new StringBuffer();
        buf.append("{ ");
        for(int i=0; i<_memberNames.size(); i++) {
            buf.append(_memberNames.get(i));
            buf.append(" ");
        }
        buf.append("}");
        return buf.toString();
   }
}
