package ca.janeg.cb;

import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;

/**
 *  A CBClassInfo object used to load a class and store pertinent class
 *  information: superclasses, fields, methods, constructor names.
 *
 *  @author     Jane Griscti jane@janeg.ca
 *  @created    January 8, 2002
 */
public class CBClassInfo {
    private final static String NAME_DELIMITER  = ".";
    private final String fullyQualifiedName;
    private final ParsedClassName pcn;

    private Class thisClass;
    private String[] superClasses;
    private FieldGroup flds;
    private MethodGroup methods;
    private ConstructorGroup ctors;
    private Class[] memberClasses;
    private Class[] memberInterfaces;

    private boolean memberPermission            = true;


    /**
     *  Constructs a new CBClassInfo object. Checks for a fully qualified class
     *  name; however, this does not guarantee that the class is available to be
     *  loaded. <p>
     *
     *  A 'fully qualified name' consists of the classes package name and simple
     *  name given in dot-notation format. For example, java.lang.Object<p>
     *
     *  A class may only be loaded, and its information retreived, if it is
     *  available to the JVM via the bootstrap loader or the system classpath.
     *
     * @param       name    a fully qualified class name
     * @exception   ClassNotFoundException  if name is not a fully qualified class
     *      name
     */
    public CBClassInfo( final String name ) throws ClassNotFoundException {
        if( !isFullyQualifiedName( name ) ) {
            throw new ClassNotFoundException( " '" + name + "' is not a fully qualified class name." );
        }

        fullyQualifiedName = name;
        pcn = new ParsedClassName( name, NAME_DELIMITER );
        loadClassData();
    }


    private boolean isFullyQualifiedName( final String name ) {
        return name.indexOf( NAME_DELIMITER ) > 0;
    }


    private void loadSuperClasses() {

        Class subclass    = thisClass;
        Class superclass  = subclass.getSuperclass();

        ArrayList tmp     = new ArrayList();

        while( superclass != null ) {
            String className  = superclass.getName();
            tmp.add( className );

            subclass = superclass;
            superclass = subclass.getSuperclass();
        }
        Collections.sort( tmp );
        superClasses = (String[])tmp.toArray( new String[0] );

        tmp = null;
    }


    private void loadMemberClasses() throws SecurityException {
        Class[] members  = thisClass.getDeclaredClasses();

        if( members.length > 0 ) {
            ArrayList mInter  = new ArrayList();
            ArrayList mClass  = new ArrayList();

            for( int i = 0; i < members.length; i++ ) {
                if( members[i].isInterface() ) {
                    mInter.add( members[i] );
                } else {
                    mClass.add( members[i] );
                }
            }

            if( !mClass.isEmpty() ) {
                memberClasses = (Class[])mClass.toArray( new Class[0] );
            }

            if( !mInter.isEmpty() ) {
                memberInterfaces = (Class[])mInter.toArray( new Class[0] );
            }
        }
    }

    private void loadClassData() throws ClassNotFoundException {

        thisClass = Class.forName( fullyQualifiedName );
        loadSuperClasses();
        flds = new FieldGroup( thisClass );
        methods = new MethodGroup( thisClass );
        ctors = new ConstructorGroup( thisClass );

        try {
            loadMemberClasses();
        } catch( SecurityException e ) {
            memberPermission = false;
        }
    }


    /**
     *  Returns the simpleName attribute of the CBClassInfo object
     *
     *@return    The simpleName value
     */
    public String getSimpleName() {
        return pcn.getSimpleName();
    }


    /**
     *  Returns the fullyQualifiedName attribute of the CBClassInfo object
     *
     *@return    The fullyQualifiedName value
     */
    public String getFullyQualifiedName() {
        return fullyQualifiedName;
    }


    /**
     *  Returns the packageName attribute of the CBClassInfo object
     *
     *@return    The packageName value
     */
    public String getPackageName() {
        return pcn.getPackageName();
    }


    /**
     *  Returns the package names associated with the class represented by
     *  this object.
     *
     *@return    The packages value
     */
    public String[] getPackages() {
        return pcn.getPackages();
    }


    /**
     *  Returns all the fields declared in the class represented by this object.
     *
     *@return    an object array containing Field objects
     */
    public Field[] getAllFields() {
        return flds.getAllFields();
    }


    /**
     *  Returns all the public fields declared in the class represented by this
     *  object.
     *
     *@return    an object array containing Field objects
     */
    public Field[] getPublicFields() {
        return flds.getPublicFields();
    }


    /**
     *  Returns all the private fields declared in the class represented by this
     *  object.
     *
     *@return    an object array containing Field objects
     */
    public Field[] getPrivateFields() {
        return flds.getPrivateFields();
    }


    /**
     *  Returns all the package fields declared in the class represented by this
     *  object. *
     *
     *@return    an object array containing Field objects
     */
    public Field[] getPackageFields() {
        return flds.getPackageFields();
    }



    /**
     *  Returns all the protected fields declared in the class represented by
     *  this object.
     *
     *@return    an object array containing Field objects
     */
    public Field[] getProtectedFields() {
        return flds.getProtectedFields();
    }



    /**
     *  Returns all the super classes the class represented by this object
     *  inherits from.
     *
     *@return    an object array containing Class objects
     */
    public String[] getSuperClasses() {
        return superClasses;
    }


    /**
     *  Returns all the methods declared in the class represented by this
     *  object.
     *
     *@return    an object array containing Method objects
     */
    public Method[] getAllMethods() {
        return methods.getAllMethods();
    }


    /**
     *  Returns all the public methods declared in the class represented by this
     *  object.
     *
     *@return    an object array containing Method objects
     */
    public Method[] getPublicMethods() {
        return methods.getPublicMethods();
    }


    /**
     *  Returns all the private methods declared in the class represented by
     *  this object.
     *
     *@return    an object array containing Method objects
     */
    public Method[] getPrivateMethods() {
        return methods.getPrivateMethods();
    }


    /**
     *  Returns all the package methods declared in the class represented by
     *  this object. *
     *
     *@return    an object array containing Method objects
     */
    public Method[] getPackageMethods() {
        return methods.getPackageMethods();
    }



    /**
     *  Returns all the protected methods declared in the class represented by
     *  this object.
     *
     *@return    an object array containing Method objects
     */
    public Method[] getProtectedMethods() {
        return methods.getProtectedMethods();
    }


    /**
     *  Returns all the constructors declared in the class represented by this
     *  object.
     *
     *@return    an object array containing Constructor objects
     */
    public Constructor[] getAllConstructors() {
        return ctors.getAllConstructors();
    }


    /**
     *  Returns all the public constructors declared in the class represented by
     *  this object.
     *
     *@return    an object array containing Constructor objects
     */
    public Constructor[] getPublicConstructors() {
        return ctors.getPublicConstructors();
    }


    /**
     *  Returns all the private constructors declared in the class represented
     *  by this object.
     *
     *@return    an object array containing Constructor objects
     */
    public Constructor[] getPrivateConstructors() {
        return ctors.getPrivateConstructors();
    }


    /**
     *  Returns all the package constructors declared in the class represented
     *  by this object. *
     *
     *@return    an object array containing Constructor objects
     */
    public Constructor[] getPackageConstructors() {
        return ctors.getPackageConstructors();
    }



    /**
     *  Returns all the protected constructors declared in the class represented
     *  by this object.
     *
     *@return    an object array containing Constructor objects
     */
    public Constructor[] getProtectedConstructors() {
        return ctors.getProtectedConstructors();
    }


    /**
     *  Returns all the classes declared as members of the class represented by
     *  this object if the package security allows access to the information.
     *
     *@return    an object array of Class objects
     *@see       isMemberAccessAllowed()
     */
    public Class[] getMemberClasses() {
        return memberClasses;
    }


    /**
     *  Returns all the interfaces declared as members of the class represented
     *  by this object if the package security allows access to the information.
     *
     *  @return    an object array of Class objects
     *  @see       isMemberAccessAllowed()
     */
    public Class[] getMemberInterfaces() {
        return memberInterfaces;
    }

    /**
     *  Returns true if the class has declared fields.
     */
    public boolean hasFields(){
        return flds.hasFields ? true : false;
    }

    /**
     *  Returns true if the class has declared methods.
     */
     public boolean hasMethods() {
        return methods.hasMethods ? true : false;
     }

    /**
     *  Returns true if the class has declared constructors.
     */
     public boolean hasCtors() {
        return ctors.hasCtors ? true : false;
     }

    /**
     *  Returns true if the class has super classes.
     */
     public boolean hasSuperClasses() {
        return Array.getLength( superClasses ) > 0;
     }

    /**
     *  Gets the interface attribute of the CBClassInfo object
     *
     *@return    The interface value
     */
    public boolean isInterface() {
        return thisClass.isInterface();
    }


    /**
     *  Gets the memberAccessAllowed attribute of the CBClassInfo object
     *
     *@return    The memberAccessAllowed value
     */
    public boolean isMemberAccessAllowed() {
        return memberPermission;
    }


    /**
     *  Returns a textual description of the object.
     *
     *@return    the name of the class represented by this object
     */
    public String toString() {
        return "A ClassInfo object for the '" + fullyQualifiedName +
            "' class.";
    }

}