/*
 * @(#)JPFBase.java  1.0  1 October 2002
 *
 * Copyright 2004
 * College of Computer and Information Science
 * Northeastern University
 * Boston, MA  02115
 *
 * The Java Power Tools software may be used for educational
 * purposes as long as this copyright notice is retained intact
 * at the top of all source files.
 *
 * To discuss possible commercial use of this software, 
 * contact Richard Rasala at Northeastern University, 
 * College of Computer and Information Science,
 * 617-373-2462 or rasala@ccs.neu.edu.
 *
 * The Java Power Tools software has been designed and built
 * in collaboration with Viera Proulx and Jeff Raab.
 *
 * Should this software be modified, the words "Modified from 
 * Original" must be included as a comment below this notice.
 *
 * All publication rights are retained.  This software or its 
 * documentation may not be published in any media either
 * in whole or in part without explicit permission.
 *
 * This software was created with support from Northeastern 
 * University and from NSF grant DUE-9950829.
 */

package edu.neu.ccs.jpf;

import edu.neu.ccs.gui.*;
import edu.neu.ccs.util.*;

import java.awt.*;
import java.awt.geom.*;
import java.lang.reflect.*;
import java.util.*;
import javax.swing.*;
import javax.swing.border.*;

/**
 * <P>Provides the base utility methods for use in the <I>Java Power Framework</I>.
 * These methods may be used to test if a class contains a required set of
 * constructors and/or methods and may be used to provide interactive feedback to
 * a user via a dialog box if desired.
 *
 * <P>In the following examples, <CODE>Foo</CODE> will represent the name of some
 * class being tested.  In all examples, a dialog will be shown if some required
 * constructor or method is missing.
 *
 * <P>Example 1: To test if Foo declares certain constructors, use:
 *
 * <PRE><CODE>
 *     return declaresConstructors(Foo.class,
 *         new String[] {
 *             "public Foo()",
 *             "public Foo(int x)",
 *             "public Foo(int x, int y)" } );
 * </CODE></PRE>
 *
 * <P>Example 2: To test if Foo declares certain methods, use:
 *
 * <PRE><CODE>
 *     return declaresMethods(Foo.class,
 *         new String[] {
 *             "public void setX(int x)",
 *             "public int xValue()",
 *             "public void setY(int y)",
 *             "public int yValue()" } );
 * </CODE></PRE>
 *
 * <P>Example 3: To test if Foo declares certain constructors and methods, use:
 *
 * <PRE><CODE>
 *     return declaresConstructorsAndMethods(Foo.class,
 *         new String[] {
 *             "public Foo()",
 *             "public Foo(int x)",
 *             "public Foo(int x, int y)" },
 *         new String[] {
 *             "public void setX(int x)",
 *             "public int xValue()",
 *             "public void setY(int y)",
 *             "public int yValue()" } );
 * </CODE></PRE>
 *
 * <P>Example 4: To test if Foo or a super class provides certain constructors
 * and methods, replaces "declares..." with "provides..." in Example 3.
 *
 * @author  Jeff Raab
 * @author  Richard Rasala
 * @version 2.2
 * @since 2.2
 */
public class JPFBase 
    implements JPTConstants
{
    /** GUI component representing a green check mark. */
    private static final PaintAction CHECK_ACTION = new PaintAction() {
        private Stroke stroke = new BasicStroke(2);

        public void paint(Component c, Graphics g) {
            int width13 = c.getWidth() / 3;
            int height12 = c.getHeight() / 2;
            
            Graphics2D g2 = (Graphics2D)g;
            g2.setRenderingHint(
                RenderingHints.KEY_ANTIALIASING, 
                RenderingHints.VALUE_ANTIALIAS_ON);
            g2.setStroke(stroke);
            g2.setColor(new Color(0, 128, 0));
            g2.draw(new Line2D.Double(
                0, height12, width13, c.getHeight() - 1));
            g2.draw(new Line2D.Double(
                width13, c.getHeight() - 1, height12 + 1, 0));
        }
    };

    /** GUI component representing a red X mark. */
    private static final PaintAction X_ACTION = new PaintAction() {
        private Stroke stroke = new BasicStroke(2);

        public void paint(Component c, Graphics g) {
            Graphics2D g2 = (Graphics2D)g;
            g2.setRenderingHint(
                RenderingHints.KEY_ANTIALIASING, 
                RenderingHints.VALUE_ANTIALIAS_ON);
            g2.setStroke(stroke);
            g2.setColor(Color.red);
            g2.draw(new Line2D.Double(
                0, 0, c.getWidth() - 1, c.getHeight() - 1));
            g2.draw(new Line2D.Double(
                c.getWidth() - 1, 0, 0, c.getHeight() - 1));
        }
    };

    ///////////////////
    // Protected API //
    ///////////////////

    /**
     * Returns <CODE>true</CODE> if the given class
     * declares the method with the given signature,
     * and, otherwise, shows a nonmodal dialog box
     * containing the given method signature
     * and an indication that the method is missing,
     * and then returns <CODE>false</CODE>.
     *
     * @param c class to test
     * @param method text signature of method
     * @throws JPTError if the method signature is malformed
     */
    protected boolean declaresMethod(Class c, String method) {
        return hasConstructorsAndMethods(
            c, null, new String[] { method }, false, true);
    }

    /**
     * Returns <CODE>true</CODE> if the given class
     * declares the method with the given signature,
     * and returns <CODE>false</CODE> otherwise.
     *
     * @param c class to test
     * @param method text signature of method
     * @throws JPTError if the method signature is malformed
     */
    protected boolean declaresMethodNoDialog(Class c, String method) {
        return hasConstructorsAndMethods(
            c, null, new String[] { method }, false, false);
    }

    /**
     * Returns <CODE>true</CODE> if the given class
     * or any of its superclass ancestors
     * declares the method with the given signature,
     * and, otherwise, shows a nonmodal dialog box
     * containing the given method signature
     * and an indication that the method is missing,
     * and then returns <CODE>false</CODE>.
     *
     * @param c class to test
     * @param method text signature of method
     * @throws JPTError if the method signature is malformed
     */
    protected boolean providesMethod(Class c, String method) {
        return hasConstructorsAndMethods(
            c, null, new String[] { method }, true, true);
    }

    /**
     * Returns <CODE>true</CODE> if the given class
     * or any of its superclass ancestors
     * declares the method with the given signature,
     * and returns <CODE>false</CODE> otherwise.
     *
     * @param c class to test
     * @param method text signature of method
     * @throws JPTError if the method signature is malformed
     */
    protected boolean providesMethodNoDialog(Class c, String method) {
        return hasConstructorsAndMethods(
            c, null, new String[] { method }, true, false);
    }

    /**
     * Returns <CODE>true</CODE> if the given class
     * declares all of the methods with the given signatures,
     * and, otherwise, shows a nonmodal dialog box containing
     * a list of all of the given method signatures
     * and an indication of which methods are missing,
     * and then returns <CODE>false</CODE>.
     *
     * @param c class to test
     * @param methods text signatures of methods
     * @throws JPTError if one of the method signatures is malformed
     */
    protected boolean declaresMethods(Class c, String[] methods) {
        return hasConstructorsAndMethods(
            c, null, methods, false, true);
    }
    
    /**
     * Returns <CODE>true</CODE> if the given class
     * declares all of the methods with the given signatures,
     * and returns <CODE>false</CODE> otherwise.
     *
     * @param c class to test
     * @param methods text signatures of methods
     * @throws JPTError if one of the method signatures is malformed
     */
    protected boolean declaresMethodsNoDialog(Class c, String[] methods) {
        return hasConstructorsAndMethods(
            c, null, methods, false, false);
    }
    
    /**
     * Returns <CODE>true</CODE> if all of the given signatures
     * represent methods declared in the given class
     * or any of its superclass ancestors,
     * and, otherwise, shows a nonmodal dialog box containing
     * a list of all of the given method signatures,
     * and an indication of which methods are missing,
     * and then returns <CODE>false</CODE>.
     *
     * @param c class to test
     * @param methods text signatures of methods
     * @throws JPTError if one of the method signatures is malformed
     */
    protected boolean providesMethods(Class c, String[] methods) {
        return hasConstructorsAndMethods(
            c, null, methods, true, true);
    }
    
    /**
     * Returns <CODE>true</CODE> if all of the given signatures
     * represent methods declared in the given class
     * or any of its superclass ancestors,
     * and returns <CODE>false</CODE> otherwise.
     *
     * @param c class to test
     * @param methods text signatures of methods
     * @throws JPTError if one of the method signatures is malformed
     */
    protected boolean providesMethodsNoDialog(Class c, String[] methods) {
        return hasConstructorsAndMethods(
            c, null, methods, true, false);
    }
    
    /**
     * Returns <CODE>true</CODE> if the given class
     * declares the constructor with the given signature,
     * and, otherwise, shows a nonmodal dialog box
     * containing the given constructor signature
     * and an indication that the constructor is missing,
     * and then returns <CODE>false</CODE>.
     *
     * @param c class to test
     * @param constructor text signature of constructor
     * @throws JPTError if the constructor signature is malformed
     */
    protected boolean declaresConstructor(Class c, String constructor) {
        return hasConstructorsAndMethods(
            c, new String[] { constructor }, null, false, true);
    }

    /**
     * Returns <CODE>true</CODE> if the given class
     * declares the constructor with the given signature,
     * and returns <CODE>false</CODE> otherwise.
     *
     * @param c class to test
     * @param constructor text signature of constructor
     * @throws JPTError if the constructor signature is malformed
     */
    protected boolean declaresConstructorNoDialog(Class c, String constructor) {
        return hasConstructorsAndMethods(
            c, new String[] { constructor }, null, false, false);
    }

    /**
     * Returns <CODE>true</CODE> if the given class
     * declares all of the constructors with the given signatures,
     * and, otherwise, shows a nonmodal dialog box containing
     * a list of all of the given constructor signatures,
     * and an indication of which constructors are missing,
     * and then returns <CODE>false</CODE>.
     *
     * @param c class to test
     * @param constructors text signatures of constructors
     * @throws JPTError if one of the constructor signatures is malformed
     */
    protected boolean declaresConstructors(Class c, String[] constructors) {
        return hasConstructorsAndMethods(
            c, constructors, null, false, true);
    }
    
    /**
     * Returns <CODE>true</CODE> if the given class
     * declares all of the constructors with the given signatures,
     * and returns <CODE>false</CODE> otherwise.
     *
     * @param c class to test
     * @param constructors text signatures of constructors
     * @throws JPTError if one of the constructor signatures is malformed
     */
    protected boolean declaresConstructorsNoDialog(
        Class c, 
        String[] constructors) 
    {
        return hasConstructorsAndMethods(
            c, constructors, null, false, false);
    }

    /**
     * Returns <CODE>true</CODE> if the given class
     * declares all of the constructors and methods
     * with the given signatures, and, otherwise, 
     * shows a nonmodal dialog box containing
     * a list of all of the given signatures and
     * an indication of which signatures are missing,
     * and then returns <CODE>false</CODE>.
     *
     * @param c class to test
     * @param constructors text signatures of constructors
     * @param method text signatures of methods
     * @throws JPTError if one of the signatures is malformed
     */
    protected boolean declaresConstructorsAndMethods(
        Class c, 
        String[] constructors, 
        String[] methods) 
    {
        return hasConstructorsAndMethods(
            c, constructors, methods, false, true);
    }
    
    /**
     * Returns <CODE>true</CODE> if the given class
     * declares all of the constructors and methods
     * with the given signatures, 
     * and otherwise returns <CODE>false</CODE>. 
     *
     * @param c class to test
     * @param constructors text signatures of constructors
     * @param method text signatures of methods
     * @throws JPTError if one of the signatures is malformed
     */
    protected boolean declaresConstructorsAndMethodsNoDialog(
        Class c, 
        String[] constructors, 
        String[] methods) 
    {
        return hasConstructorsAndMethods(
            c, constructors, methods, false, false);
    }

    /**
     * Returns <CODE>true</CODE> if the given class
     * or any of its superclass ancestors
     * declares all of the constructors and methods
     * with the given signatures, and, otherwise, 
     * shows a nonmodal dialog box containing
     * a list of all of the given signatures and
     * an indication of which signatures are missing,
     * and then returns <CODE>false</CODE>.
     *
     * @param c class to test
     * @param constructors text signatures of constructors
     * @param method text signatures of methods
     * @throws JPTError if one of the signatures is malformed
     */
    protected boolean providesConstructorsAndMethods(
        Class c, 
        String[] constructors, 
        String[] methods) 
    {
        return hasConstructorsAndMethods(
            c, constructors, methods, true, true);
    }
    
    /**
     * Returns <CODE>true</CODE> if the given class
     * or any of its superclass ancestors
     * declares all of the constructors and methods
     * with the given signatures, 
     * and otherwise returns <CODE>false</CODE>. 
     *
     * @param c class to test
     * @param constructors text signatures of constructors
     * @param method text signatures of methods
     * @throws JPTError if one of the signatures is malformed
     */
    protected boolean providesConstructorsAndMethodsNoDialog(
        Class c, 
        String[] constructors, 
        String[] methods) 
    {
        return hasConstructorsAndMethods(
            c, constructors, methods, true, false);
    }

    /**
     * Reports using a dialog whether or not the given class declares
     * constructors and methods with each of the given signatures,
     * where the each array of booleans represents whether or not
     * each constructor or method is declared.
     *
     * @param c the class to contain required methods
     * @param constructors text signatures of the required constructors
     * @param isConstructorDeclared whether or not 
     *      each required constructor is declared
     * @param methods text signatures of the required methods
     * @param isMethodDeclared whether or not 
     *      each required method is declared
     */
    protected void report(
        Class c,
        String[] constructors,
        boolean[] isConstructorDeclared, 
        String[] methods,
        boolean[] isMethodDeclared) 
    {
        // build table containing method sigantures
        TablePanel methodsPanel = new TablePanel(
            constructors.length + methods.length, 2, 20, 5, WEST);

        PaintAction action = null;
        int row = 0;
        
        // add constructor signatures
        for (int i = 0; i < constructors.length; i++) {
            if (isConstructorDeclared[i])
                action = CHECK_ACTION;
            else
                action = X_ACTION;

            methodsPanel.addObject(
                new PaintActionCapsule(action, 10, 15), row, 0);

            methodsPanel.addObject(
                new Annotation(constructors[i]), row, 1);
            
            row++;
        }
        
        // add method signatures
        for (int i = 0; i < methods.length; i++) {
            if (isMethodDeclared[i])
                action = CHECK_ACTION;
            else
                action = X_ACTION;

            methodsPanel.addObject(
                new PaintActionCapsule(action, 10, 15), row, 0);

            methodsPanel.addObject(
                new Annotation(methods[i]), row, 1);
            
            row++;
        }
        
        // create the insert string
        String insert = "";
        
        if ((constructors.length > 0) && (methods.length > 0))
            insert = "constructors and methods";
        else if (constructors.length > 0)
            insert = "constructors";
        else if (methods.length > 0)
            insert = "methods";
        
        // build panel containing caption and method panel
        TablePanel mainPanel = new TablePanel(
            new Object[] {
                "The following " + insert + " are required\n" + 
                    "in class " + JPFApplication.className(c) + ":",
                methodsPanel
            }, VERTICAL, 0, 10, NORTH);
        
        mainPanel.setBorder(new EmptyBorder(10, 10, 10, 10));
        
        // show the user the feedback
        GeneralDialog.showOKDialog(mainPanel, "Required " + insert);
    }

    /////////////////////
    // Private methods //
    /////////////////////
    
    /**
     * Returns <CODE>true</CODE> if all of the given signatures
     * represent methods and constructors declared in the given class,
     * or if the recurse option is set,
     * if all of the given signatures represent methods and constructors
     * declared in the class and its superclasses,
     * and returns <CODE>false</CODE> otherwise. 
     *
     * If the dialog option is set, as a side effect
     * this method shows a nonmodal dialog box containing
     * a list of all of the given method signatures,
     * and an indication of which methods are missing.
     *
     * @param c class to test
     * @param methods text signatures of methods
     * @param recurse whether or not to search superclasses
     * @throws JPTError if one of the method signatures is malformed
     */
    private boolean hasConstructorsAndMethods(
        Class c, 
        String[] constructors,
        String[] methods,
        boolean recurse,
        boolean showDialog) 
    {
        if (constructors == null)
            constructors = new String[0];
        boolean[] isConstructorDeclared = new boolean[constructors.length];

        if (methods == null)
            methods = new String[0];
        boolean[] isMethodDeclared = new boolean[methods.length];

        boolean allDeclared = true;

        // for each constructor signature
        for (int i = 0; i < constructors.length; i++) {
            isConstructorDeclared[i] = 
                hasConstructorImpl(c, constructors[i]);
        
            if (isConstructorDeclared[i] == false)
                allDeclared = false;
        }
        
        // for each method signature
        for (int i = 0; i < methods.length; i++) {
            isMethodDeclared[i] = 
                hasMethodImpl(c, methods[i], recurse);
        
            if (isMethodDeclared[i] == false)
                allDeclared = false;
        }
        
        if (!allDeclared && showDialog) {
            report(
                c, 
                constructors, 
                isConstructorDeclared, 
                methods, 
                isMethodDeclared);
        }
        
        return allDeclared;
    }
    
    /**
     * Returns <CODE>true</CODE> if the given class
     * declares the method with the given signature,
     * or <CODE>false</CODE> otherwise.
     *
     * @param c class to test
     * @param method text signature of method
     * @throws JPTError if the method signature is malformed
     */
    private boolean hasMethodImpl(Class c, String method, boolean recurse) {

        // base case for recursion
        if (c == null)
            return false;

        // get signature object for text signature
        MethodSignature sig = new MethodSignature(method);
            
        // get the list of declared methods
        try {
            Method[] methods = c.getDeclaredMethods();
            for (int i = 0; i < methods.length; i++) {
            
                // return if we found it
                if (sig.equals(methods[i]))
                    return true;
            }
            
            // recurse if option set
            if (recurse)
                return hasMethodImpl(c.getSuperclass(), method, true);
            
            // otherwise return false for sure
            return false;
        }
            
        // if we can't peek in the class, assume it's declared
        catch (SecurityException secEx) {
            return true;
        }
    }
    
    /**
     * Returns <CODE>true</CODE> if the given class
     * declares the constructor with the given signature,
     * or <CODE>false</CODE> otherwise.
     *
     * @param c class to test
     * @param constructor text signature of constructor
     * @throws JPTError if the method signature is malformed
     */
    private boolean hasConstructorImpl(Class c, String constructor) {
        if (c == null)
            return false;

        // get signature object for text signature
        ConstructorSignature sig = new ConstructorSignature(constructor);
            
        // get the list of declared methods
        try {
            Constructor[] constructors = c.getDeclaredConstructors();
            for (int i = 0; i < constructors.length; i++) {
            
                // return if we found it
                if (sig.equals(constructors[i]))
                    return true;
            }
            
            // does not exist
            return false;
        }
            
        // if we can't peek in the class, assume it's declared
        catch (SecurityException secEx) {
            return true;
        }
    }
    
    /** Encapsulates signature attributes for a class member. */
    private abstract static class MemberSignature {

        /** Modifers for this member. */
        protected int modifiers = 0;
        
        /** Name of this member. */
        protected String name = null;
        
        /**
         * Parses the given text signature 
         * and sets member data for this object accordingly.
         *
         * @param s text signature to parse
         * @throws JPTError if the text signature is malformed
         */
        protected abstract void fromString(String s) throws JPTError;

        /**
         * Adds the modifier represented by the given keyword
         * to the list of modifiers for this method.
         *
         * If the given text is not a modifier,
         * no changes are made to the modifier list.
         *
         * @param token possible keyword for modifier to be added
         */
        protected void addModifier(String token) {
            int newModifier = 0;
            
            if (token.equals("public"))
                newModifier = Modifier.PUBLIC;
            else if (token.equals("protected"))
                newModifier = Modifier.PROTECTED;
            else if (token.equals("private"))
                newModifier = Modifier.PRIVATE;
            else if (token.equals("abstract"))
                newModifier = Modifier.ABSTRACT;
            else if (token.equals("static"))
                newModifier = Modifier.STATIC;
            else if (token.equals("final"))
                newModifier = Modifier.FINAL;
            else if (token.equals("synchronized"))
                newModifier = Modifier.SYNCHRONIZED;
               
            modifiers |= newModifier;
        }

        /**
         * Returns <CODE>true</CODE> if the given <CODE>String</CODE>
         * equals a method modifier keyword, 
         * or <CODE>false</CODE> otherwise.
         *
         * @param token possible method modifier keyword
         */
        protected boolean isModifier(String token) {
            return token.equals("public") ||
                   token.equals("protected") ||
                   token.equals("private") ||
                   token.equals("abstract") ||
                   token.equals("static") ||
                   token.equals("final") ||
                   token.equals("synchronized");
        }

        /**
         * Returns the unqualified name of the given class.
         *
         * @param c class from which to extract name
         */
        protected String className(Class c) {
            String s = c.getName();

            int pos = s.lastIndexOf(".");

            if (pos < 0)
                return s;

            return s.substring(pos + 1);
        }
        
        /**
         * Returns a <CODE>String</CODE> representation
         * of the modifiers for this member.
         */
        protected String modifierString() {
            String s = "";
            
            if (Modifier.isPublic(modifiers))
                s += "public ";
            if (Modifier.isProtected(modifiers))
                s += "protected ";
            if (Modifier.isPrivate(modifiers))
                s += "private ";
            if (Modifier.isAbstract(modifiers))
                s += "abstract ";
            if (Modifier.isStatic(modifiers))
                s += "static ";
            if (Modifier.isFinal(modifiers))
                s += "final ";
            if (Modifier.isSynchronized(modifiers))
                s += "synchronized ";
            
            return s;
        }
    }

    /** Encapsulates a parametrized member signature. */
    private abstract static class ParametrizedSignature 
        extends MemberSignature 
    {
        /** Names of the types of parameters. */
        private ArrayList parameters = new ArrayList();
        
        /** Names of parameters. */
        private ArrayList parameterNames = new ArrayList();

        /**
         * Adds an parameter to this object with the given name,
         * whose type is named by the given <CODE>String</CODE>.
         *
         * @param type name of the type of the parameter
         * @param name name of the parameter
         */
        protected void addParameter(String type, String name) {
            parameters.add(type);
            parameterNames.add(name);
        }

        /** Returns an array containing the types of the parameters. */
        public String[] getParameterTypes() {
            return (String[])parameters.toArray(new String[0]);
        }

        /** Returns an array containing the names of the parameters. */
        public String[] getParameterNames() {
            return (String[])parameterNames.toArray(new String[0]);
        }
        
        /** 
         * Returns a <CODE>String</CODE> representation
         * of the parameters for this object.
         */
        protected String parameterString() {
            String s = "(";

            String[] pTypes = getParameterTypes();
            String[] pNames = getParameterNames();
            if (pTypes.length > 0) {
                s += pTypes[0] + " " + pNames[0];

                for (int i = 1; i < pTypes.length; i++)
                    s += "," + pTypes[i] + " " + pTypes[i];
            }

            s += ")";
        
            return s;
        }
    }

    /** Encapsulates a method signature. */
    private static class MethodSignature 
        extends ParametrizedSignature 
    {
        /** Name of the return type for this method. */
        protected String returnType = null;

        /**
         * Constructs a new signature object representing
         * the given text method signature.
         *
         * @param s text signature for this method
         * @throws JPTError if the text signature is malformed
         */
        public MethodSignature(String s) throws JPTError {
            fromString(s);
        }

        /**
         * Parses the given text method signature 
         * and sets member data for this object accordingly.
         *
         * @param s text method signature to parse
         * @throws JPTError if the text signature is malformed
         */
        protected void fromString(String s) throws JPTError {
            StringTokenizer t = new StringTokenizer(s);
            
            try {
                // parse modifiers in any order
                String s1 = t.nextToken();
                while (isModifier(s1)) {
                    addModifier(s1);
                    s1 = t.nextToken();
                }
                
                // parse return type
                returnType = s1;
                
                // parse method name
                name = t.nextToken("(,) ");
                
                // parse argument list
                String s2;
                while(t.hasMoreTokens()) {
                    s1 = t.nextToken();
                    s2 = t.nextToken();

                    addParameter(s1, s2);
                }
            }
            catch (Exception ex) {
                throw new JPTError("Method signature malformed: " + s);
            }
        }


        /** 
         * Returns a text method signature 
         * represented by this object. 
         */
        public String toString() {
            String s = "";
        
            // start with the modifiers
            s += modifierString();
            
            // add the return type and name
            s += returnType + " " + name;
            
            // add the parameter types and names
            s += parameterString();
            
            return s;
        }
        
        /**
         * Returns <CODE>true</CODE> if this method signature
         * is correct for the given method.
         *
         * @param m method with which to compare
         */
        public boolean equals(Method m) {
            if (m.getModifiers() != modifiers)
                return false;

            if (!m.getName().equals(name))
                return false;

            if (!className(m.getReturnType()).equals(returnType))
                return false;

            Class[] mTypes = m.getParameterTypes();
            String[] sTypes = getParameterTypes();
            
            if (mTypes.length != sTypes.length)
                return false;

            for (int i = 0; i < mTypes.length; i++) {
                if (!className(mTypes[i]).equals(sTypes[i]))
                    return false;
            }

            return true;
        }
    }

    /** Encapsulates constructor signature attributes. */
    private static class ConstructorSignature 
        extends ParametrizedSignature 
    {
        /**
         * Constructs a new signature object representing
         * the given text constructor signature.
         *
         * @param s text signature for this constructor
         * @throws JPTError if the text signature is malformed
         */
        public ConstructorSignature(String s) throws JPTError {
            fromString(s);
        }

        /**
         * Parses the given text constructor signature 
         * and sets member data for this object accordingly.
         *
         * @param s text method signature to parse
         * @throws JPTError if the text signature is malformed
         */
        protected void fromString(String s) throws JPTError {
            StringTokenizer t = new StringTokenizer(s);
            
            try {
                // parse modifier
                String s1 = t.nextToken();
                if (isModifier(s1))
                    addModifier(s1);
                
                // parse method name
                String s2 = t.nextToken("(,) ");
                name = s2;
                
                // parse argument list
                while(t.hasMoreTokens()) {
                    s1 = t.nextToken();
                    s2 = t.nextToken();

                    addParameter(s1, s2);
                }
            }
            catch (Exception ex) {
                throw new JPTError("Constructor signature malformed: " + s);
            }
        }
        
        /** 
         * Returns a text method signature 
         * represented by this object. 
         */
        public String toString() {
            String s = "";
            
            // start with the modifiers
            s += modifierString();
            
            // add the constructor name
            s += name;
            
            // add the parameter types and names
            s += parameterString();
            
            return s;
        }
        
        /**
         * Returns <CODE>true</CODE> if this constructor signature
         * is correct for the given constructor.
         *
         * @param c constructor with which to compare
         */
        public boolean equals(Constructor c) {
            if (c.getModifiers() != modifiers)
                return false;

            if (!c.getName().equals(name))
                return false;
            
            Class[] cTypes = c.getParameterTypes();
            String[] sTypes = getParameterTypes();
            
            if (cTypes.length != sTypes.length)
                return false;
            
            for (int i = 0; i < cTypes.length; i++) {
                if (!className(cTypes[i]).equals(sTypes[i]))
                    return false;
            }
            
            return true;
        }

        /**
         * Returns <CODE>true</CODE> if the given <CODE>String</CODE>
         * equals a method modifier keyword, 
         * or <CODE>false</CODE> otherwise.
         *
         * @param token possible method modifier keyword
         */
        protected boolean isModifier(String token) {
            return token.equals("public") ||
                   token.equals("protected") ||
                   token.equals("private");
        }
    }
}
