/*--- 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.Enumeration;
import java.util.Iterator;
import java.util.Hashtable;
import java.util.Vector;
import org.freehep.jaco.*;

/**
 * Factory class to build the RTTI (RunTime Type Identification) needed for JACO.
 * <p>
 * Classes are registered under their fully qualified name (without the packagePrefix,
 * but with the already defined package name).
 *
 * @author Mark Donszelmann
 * @version $Id: RTTI.java,v 1.1.1.1 2004/05/31 05:08:50 kuromaru Exp $
 */
public class RTTI {
    private Hashtable classes = new Hashtable();

    /**
     * Creates an empty RTTI
     *
     */
    public RTTI() {
    }

    /**
     * Returns the list of fully qualified class names, which can be used for lookups.
     *
     *
     * @return array of fully qualified class names
     */
    public String[] getClassNames() {
        String[] classNames = new String[classes.size()];
        int i = 0;

        for (Enumeration e = classes.keys(); e.hasMoreElements(); ) {
            classNames[i++] = (String)e.nextElement();
        }

        return classNames;
    }

    /**
     * Returns the class for a fully qualified name: "packageName.name"
     *
     *
     * @param packageName name of the package
     * @param name name of the class
     *
     * @return class definition
     */
    public IClass getClass(String packageName, String name) {
        return getClass(packageName + "." + name);
    }

    /**
     * Returns the class with fully qualified name.
     *
     *
     * @param fullname fully qualified name
     *
     * @return class definition
     */
    public IClass getClass(String fullname) {
        return (IClass)classes.get(fullname);
    }

    /**
     * Creates a class and registers it under "packageName.name".
     * <p>
     * A warning is issued if a class definition already exists under this name.
     * The class definition is overridden.
     *
     * @param name name of the class
     * @param packageName package name
     * @param comments list of comments (String)
     * @param interfaces list of interface names (String: fully qualified)
     * @param constructors list of constructors (IConstructor)
     * @param destructors list of destructors (IDestructor)
     * @param methods list of methods (IMethod)
     *
     * @return class definition
     */
    public IClass createClass(String name, boolean isClass,
                              String packageName, Vector packageComments,
                              Vector comments, Vector eocComments,
                              Vector interfaces, Vector constructors,
                              IDestructor destructor, Vector methods, Vector fields) {

        if (packageComments == null) packageComments = new Vector();
        if (comments == null) comments = new Vector();
        if (eocComments == null) eocComments = new Vector();

        String[] a1;

        if (interfaces == null) {
            a1 = new String[0];
        } else {
            a1 = new String[interfaces.size()];

            interfaces.copyInto(a1);
        }

        IConstructor[] a2;

        if (constructors == null) {
            a2 = new IConstructor[0];
        } else {
            a2 = new IConstructor[constructors.size()];

            constructors.copyInto(a2);
        }

        IMethod[] a3;

        if (methods == null) {
            a3 = new IMethod[0];
        } else {
            a3 = new IMethod[methods.size()];

            methods.copyInto(a3);
        }

        IField[] a4;

        if (fields == null) {
            a4 = new IField[0];
        } else {
            a4 = new IField[fields.size()];

            fields.copyInto(a4);
        }

        if (classes.containsKey(packageName + "." + name)) {
            System.err.println("Warning: Class " + ((packageName.equals("")) ? "" : packageName + ".")
                                                 + name + " already exists.");
        }

        IClass clazz = new IClass(name, isClass, packageName, packageComments,
                                  comments, eocComments, a1, a2, destructor, a3, a4);

        classes.put(packageName + "." + name, clazz);

        return clazz;
    }

    /**
     * Creates a constructor.
     *
     * @param name name of the constructor
     * @param comments list of comments (String)
     * @param parameterTypes list of parameter types (InamedType)
     * @param exceptionTypes list of exception names (String)
     *
     * @return constructor definition
     */
    public IConstructor createConstructor(String name, Vector comments, Vector parameterTypes, Vector exceptionTypes) {

        if (comments == null) comments = new Vector();

        INamedType[] a1;

        if (parameterTypes == null) {
            a1 = new INamedType[0];
        } else {
            a1 = new INamedType[parameterTypes.size()];

            parameterTypes.copyInto(a1);
        }

        String[] a2;

        if (exceptionTypes == null) {
            a2 = new String[0];
        } else {
            a2 = new String[exceptionTypes.size()];

            exceptionTypes.copyInto(a2);
        }

        return new IConstructor(name, comments, a1, a2);
    }

    /**
     * Creates a destructor.
     *
     * @param name name of the destructor
     * @param comments list of comments (String)
     * @param exceptionTypes list of exception names (String)
     *
     * @return destructor definition
     */
    public IDestructor createDestructor(String name, Vector comments, Vector exceptionTypes) {

        if (comments == null) comments = new Vector();

        String[] a1;

        if (exceptionTypes == null) {
            a1 = new String[0];
        } else {
            a1 = new String[exceptionTypes.size()];

            exceptionTypes.copyInto(a1);
        }

        return new IDestructor(name, comments, a1);
    }

    /**
     * Creates a method.
     *
     *
     * @param name name of the method
     * @param comments list of comments (String)
     * @param isStatic true if method is static
     * @param returnType type of return parameter (IType)
     * @param parameterTypes list of parameter types (INamedType)
     * @param exceptionTypes list of exception names (String)
     *
     * @return method definition
     */
    public IMethod createMethod(String name, Vector comments, boolean isStatic, IType returnType, boolean isConst, Vector parameterTypes,
                                Vector exceptionTypes) {

        if (comments == null) comments = new Vector();

        INamedType[] a1;

        if (parameterTypes == null) {
            a1 = new INamedType[0];
        } else {
            a1 = new INamedType[parameterTypes.size()];

            parameterTypes.copyInto(a1);
        }

        String[] a2;

        if (exceptionTypes == null) {
            a2 = new String[0];
        } else {
            a2 = new String[exceptionTypes.size()];

            exceptionTypes.copyInto(a2);
        }

        return new IMethod(name, comments, isStatic, returnType, isConst, a1, a2);
    }

    /**
     * Creates a named field.
     *
     *
     * @param namedType named type (INamedType)
     *
     * @return field definition
     */
    public IField createField(INamedType namedType, Vector comments) {

        if (comments == null) comments = new Vector();

        return new IField(namedType, comments);
    }

    /**
     * Creates a named type.
     *
     *
     * @param name name of the parameter (String)
     * @param type type of the parameter (IType)
     * @param init init of the parameter (String)
     *
     * @return named type definition
     */
    public INamedType createNamedType(String name, IType type, String init) {
        return new INamedType(name, type, init);
    }

    /**
     * Creates a type (primitive or Class)
     *
     *
     * @param name name of the type
     * @param primitive true if this type is a primitive
     * @param reference true if this type is a reference
     * @param pointer number of pointer postfixes (*)
     * @param dimension dimension of the type, 0 for no dimension.
     *
     * @return type definition
     */
    public IType createType(String name, boolean konst, boolean primitive,
                            boolean reference, int pointer, int dimension, Vector types) {
        IType[] a2;
        if (types == null) {
            a2 = new IType[0];
        } else {
            a2 = new IType[types.size()];

            types.copyInto(a2);
        }
        return new IType(name, konst, primitive, reference, pointer, dimension, a2);
    }

    /**
     * Creates an aliased type
     *
     *
     * @param name name of the type
     * @param alias pointing to aliased type (typedef)
     * @param reference true if this type is a reference
     * @param pointer number of pointer postfixes (*)
     * @param dimension dimension of the type, 0 for no dimension.
     *
     * @return type definition
     */
    public IType createType(String name, IType alias, boolean reference, int pointer, int dimension) {
        return new IType(name, alias, reference, pointer, dimension);
    }

    /**
     * String representation of the RTTI, which lists the full RTTI in semi-java format, without
     * package prefix.
     *
     *
     * @return String representation of the RTTI.
     */
    public String toString() {
        return toString(null);
    }

    /**
     * String representation of the RTTI, which lists the full RTTI in semi-java format, with
     * package prefix.
     *
     * @param packagePrefix name to prefix to the package name
     *
     * @return String representation of the RTTI.
     */
    public String toString(String packagePrefix) {
        StringBuffer s = new StringBuffer("Repository RTTI:\n");

        s.append("================\n");

        for (Enumeration e = classes.keys(); e.hasMoreElements(); ) {
            String key = (String)e.nextElement();
            IClass clazz = (IClass)classes.get(key);

            s.append(clazz.toString(packagePrefix));
            s.append("\n");
            s.append("================\n");
        }

        return s.toString();
    }

    /**
     * returns language specific and general comments
     *
     * @param comments list of all comments, each may be preceded by @xxx: to specify language xxx
     * @param language to be used to filter comments
     * @return array of comments
     */
    public static String[] getComments(Vector comments, String language) {
        Vector result = new Vector();
        for (Iterator i = comments.iterator(); i.hasNext(); ) {
            String comment = (String)i.next();
            if (comment.startsWith("@")) {
                // language specific clause "@java: ...."
                if (comment.startsWith("@"+language+":")) {
                    result.add(comment.substring(1+language.length()+1));
                }
            } else {
                if (language.equals("java") && comment.startsWith("/** @")) {
                    // filter "/** @xxx " doxygen comments for java, not to confuse JavaDoc
                    result.add("/**\n");
                } else {
                    result.add(comment);
                }
            }
        }

        String[] r = new String[result.size()];
        result.copyInto(r);
        return r;
    }
}

