/*--- formatted by Jindent 2.1, (www.c-lab.de/~jindent) ---*/

// Copyright 2000, CERN, Geneva, Switzerland and SLAC, Stanford, California, U.S.A.
package org.freehep.jaco.rtti;

import java.util.*;

import org.freehep.jaco.*;

/**
 * Defines a type in the RTTI.
 *
 * This type may be cascaded (typedef in C++) to another type.
 * The methods isPrimitive, getDimension and getPointer accomodate for this.
 *
 * @author Mark Donszelmann
 * @version $Id: IType.java,v 1.18 2002/11/03 21:50:13 duns Exp $
 */
public class IType {
    private String name;
    private boolean primitive;
    private boolean konst;
    private IType alias;
    private int pointer;
    private boolean reference;
    private int dimension;
    private IType[] types;

    IType(String name, boolean konst, boolean primitive,
          boolean reference, int pointer, int dimension, IType[] types) {
        this.name = name;
        this.konst = konst;
        this.primitive = primitive;
        this.alias = null;
        this.reference = reference;
        this.pointer = pointer;
        this.dimension = dimension;
        this.types = types;
    }

    IType(String name, IType alias, boolean reference, int pointer, int dimension) {
        this.name = name;
        this.primitive = false;
        this.konst = false;
        this.alias = alias;
        this.reference = reference;
        this.pointer = pointer;
        this.dimension = dimension;
    }

    /**
     * Returns the name of the type. This name may be fully qualified. Otherwise it belongs
     * to the current package.
     *
     *
     * @return name of the type
     */
    public String getName() {
        return (alias == null) ? name : alias.getName();
    }

    /**
     * Indicates if this type is a primitive type.
     *
     *
     * @return true if this type is a primitive
     */
    public boolean isPrimitive() {
        return (alias == null) ? primitive : alias.isPrimitive();
    }

    /**
     * Indicates if this type is a const type.
     *
     *
     * @return true if this type is a const
     */
    public boolean isConst() {
        return (alias == null) ? konst : alias.isConst();
    }

    /**
     * Indicates if this type is a reference.
     *
     *
     * @return true if reference
     */
    public boolean isReference() {
        if (alias == null) {
            return reference;
        }

        return alias.isReference();
    }

    /**
     * Indicates the number of pointer postfixes this type has.
     *
     *
     * @return pointer postfixes of this type (*)
     */
    public int getPointer() {
        if (alias == null) {
            return pointer;
        }

        return alias.getPointer();
    }

    /**
     * Indicates the dimension of this type.
     * <p>1 = [], 2 = [][], etc...
     *
     * @return dimension of this type, 0 is no dimension.
     */
    public int getDimension() {
        return (alias == null) ? dimension : alias.getDimension()+dimension;
    }

    /**
     * Indicates the template of this type.
     *
     * @return type of template
     */
    public IType[] getTypes() {
        return types;
    }

    public boolean isVoid()
    {
        return getName().equals("void");
    }

    public String getSignature(String packageName, Properties imports) {
        StringBuffer s = new StringBuffer();
        if (alias == null) {
            if (isPrimitive() || isVoid()) {
                s.append(getName());
            } else {
                // FIXME, if not found, add packagename of this class
                String name = getName();
                for (int i=getDimension(); i>0; i--) {
                    name = getTypes()[0].getName();
                }
                name = imports.getProperty(name,null);
                s.append( name != null ? name : packageName+"."+getName());
            }
        } else {
            s.append(alias.getSignature(packageName, imports));
        }
        for (int i = 0; i < getDimension(); i++) {
            s.append("[]");
        }

        return org.apache.bcel.classfile.Utility.getSignature(s.toString());
    }

    /**
     * Returns a string representation of this type.
     *
     *
     * @return string representatoon of this type
     */
    public String toString() {
        StringBuffer s = new StringBuffer();

        if (isConst()) {
            s.append("const ");
        }

        if (isPrimitive()) {
            s.append("#");
        }

        s.append(getName());

        IType[] types = getTypes();
        if (types.length > 0) {
            s.append("<");
            s.append(types[0].toString());
            for (int i=1; i<types.length; i++) {
                s.append(", ");
                s.append(types[i].toString());
            }
            s.append(">");
        }

        if (isReference()) {
            s.append("&");
        }

        for (int i=0; i<getPointer(); i++) {
            s.append("*");
        }

        for (int i = 0; i < getDimension(); i++) {
            s.append("[]");
        }

        if (alias != null) {
            s.append("{"+name+"}");
        }

        return s.toString();
    }

    /**
     * returns true is the two types are equivalent, that is after all aliasing
     * has been done
     */
    public boolean equals(Object o) {
       if (o instanceof IType) {
          IType other = (IType) o;
          if (!getName().equals(other.getName())) return false;
          if (getDimension() != other.getDimension()) return false;
          if (isReference() != other.isReference()) return false;
          else return getPointer() == other.getPointer();
       }
       return false;
    }

}

