/*
 * Copyright (c) Eric D. Friedman 1998. All Rights Reserved.
 * Copyright (c) Paul Kinnucan 1998. All Rights Reserved.
 *
 * $Revision: 1.5 $ 
 * $Date: 2002/09/14 03:20:48 $ 
 *
 * InterfaceFactory is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2, or (at
 * your option) any later version.
 *
 * InterfaceFactory is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * To obtain a copy of the GNU General Public License write to the
 * Free Software Foundation, Inc.,  59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.  
 */

package jde.wizards;

import java.beans.Introspector;
import java.lang.reflect.Method;

/**
 * Defines a default parameter name factory for the InterfaceFactory.
 * Tries hard to find a meaningfull name.
 *
 * @author Eric D. Friedman
 * @author Ole Arndt
 * @version $Revision: 1.5 $
 */

public class DefaultNameFactory implements NameFactory {

    /**
     * Make up a name for the nth parameter of a method.
     * First this method tries to recognize bean methods, if the method is named
     * "setName" and has one parameter, the parameter returned is the
     * lowercased method name with "set" stripped: "name".
     *
     * Index Bean Properties are methods starting with "set" and having two
     * parameters, the first one of which is an int, or methods starting with
     * "get" and having only one int parameter. In both cases the first
     * parameter is given the name "index". 
     *     
     * Examples:
     *
     *   void setName(String name);
     *   void setNames(String[] names);
     *   void setName(int index, String name);
     *   String getName(int index);
     *
     * For other method names the parameters are named according to their
     * type. 
     *
     * Primitive types are named after the following mapping (now
     * hardcoded, can perhaps be made configurable):
     *
     *   boolean: flag
     *   byte: b
     *   char: c
     *   short: s
     *   int: n
     *   long: l
     *   float: f
     *   double: d
     * 
     * Array types are named like their base type with the String "Array"
     * appended, so a "byte[]" parameter will be named "byteArray":
     *
     *    void fill(char[] charArray);
     *
     * All other parameters will be named according to their type's unqualified
     * lowercased classname:
     *
     *    void actionPerformed(ActionEvent actionEvent);
     *
     * To avoid any name clashes, the parameters will have a number appended
     * if there is another parameter of the same type in the methods
     * parameterlist:
     *
     *   void write(byte[] byteArray, int n, int n1);
     *   void put(String string, String string1);
     *
     * @param sig  - signature of the declaring method
     * @param num     - the parameter number whose name we want.
     * @return a name for the n'th parameter of the method.
     */
    public String getParameterName(Signature sig, int num) {
        Method method = sig.getMethod();
        String  methodName = method.getName();
        Class[] parameters = method.getParameterTypes();
        Class   type = parameters[num];
        String  className = type.getName();

        // special handling for bean methods
        if (methodName.length() > 3) {
            if (methodName.startsWith("set")) {
                if (parameters.length == 2) {
                    // an indexed propery setter
                    if (num == 1 && className.equals("int"))
                        return "index";

                    if (num == 2)
                        return methodToParamName(methodName, 3);
                }
      
                // a normal propery setter
                if (parameters.length == 1 && num == 1)
                    return methodToParamName(methodName, 3);
            }

            if (methodName.startsWith("get")) {
                // indexed getter
                if (parameters.length == 1 && num == 1 && className.equals("int"))
                    return "index";
            }
        }

        return unique(parameters, type, num, getNameFromClass(className));
    }


    /**
     * Generate a name base on the type of the parameter.
     *
     * @param type  the parameters type
     * @return a more or less fitting name.
     */
    private final String getNameFromClass(String className) {

        int i = className.lastIndexOf('.');
        // if this is an classname starting with an upper case letter
        // downcase it and use class name as parameter name
        if (Character.isUpperCase(className.charAt(i + 1)))
            return methodToParamName(className, i + 1);

        // handle arrays
        if (className.equals("[Z"))
            return "boolArray";
        if (className.equals("[B"))
            return "byteArray";
        if (className.equals("[C"))
            return "charArray";
        if (className.equals("[S"))
            return "shortArray";
        if (className.equals("[I"))
            return "intArray";
        if (className.equals("[J"))
            return "longArray";
        if (className.equals("[F"))
            return "floatArray";
        if (className.equals("[D"))
            return "doubleArray";

        // handle primitives
        if (className.equals("boolean"))
            return "flag";
        if (className.equals("byte"))
            return "b";
        if (className.equals("char"))
            return "c";
        if (className.equals("short"))
            return "s";
        if (className.equals("int"))
            return "n";
        if (className.equals("long"))
            return "l";
        if (className.equals("float"))
            return "f";
        if (className.equals("double"))
            return "d";

        // give up
        return "arg";
    }
    
    /**
     * Make name unique, look if there a more parameters of this type
     * before us. In this case append a number.
     *
     * @param parameters all parameter types of the method
     * @param type the type of the current parameter
     * @param num the position of the current parameter
     * @param name parameter basename
     * @return a unique parameter name
     */
    private final String unique(Class[] parameters, Class type, int num, String name) {
        if (parameters.length > 1) {
            int i = 0;
            for (int j = 0; j < num; ++j)
                if (parameters[j] == type)
                    ++i;

            if (i != 0)
                name += i;
        }

        return name;    
    }
    
    /**
     * Strip the first characters from the string and decapitalize
     * the rest.
     *
     * @param name the original name.
     * @param strip number of characters to strip from the front of the name.
     * @return the decapitalized and shortened name.
     */
    private final String methodToParamName(String name, int strip) {
        return Introspector.decapitalize(name.substring(strip));
    }
}

// End of DefaultNameFactory.java

/*
 * $Log: DefaultNameFactory.java,v $
 * Revision 1.5  2002/09/14 03:20:48  andyp
 * sync to jde 2.2.9beta12
 *
 * Revision 1.4  2002/06/06 05:12:44  paulk
 * DefaultNameFactory now generates meaningful method parameter names based
 * on the parameter type or the method name. Thanks to Ole Arndt.
 *
 */
