All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.thoughtworks.paranamer.JavaFileParanamer Maven / Gradle / Ivy

There is a newer version: 0.63
Show newest version
package com.thoughtworks.paranamer;

import japa.parser.JavaParser;
import japa.parser.ast.CompilationUnit;
import japa.parser.ast.PackageDeclaration;
import japa.parser.ast.body.ClassOrInterfaceDeclaration;
import japa.parser.ast.body.ConstructorDeclaration;
import japa.parser.ast.body.MethodDeclaration;
import japa.parser.ast.body.Parameter;
import japa.parser.ast.visitor.GenericVisitor;
import japa.parser.ast.visitor.GenericVisitorAdapter;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * .java parser implementation of Paranamer. It relies on javaparser library 1.0.8+ to parse java files
 * retrieved by a {@link JavaFileFinder} implementation.
 *
 * @author Gael Lazzari
 */
public class JavaFileParanamer implements Paranamer {

    /**
     * Interface in charge of retrieving .java file where a specific {@link Method} or
     * {@link Constructor} is declared.
     *
     * @author Gael Lazzari
     */
    public interface JavaFileFinder {

        /**
         * Retrieve and open the .java file where the specified {@link Method} or {@link Constructor}
         * is declared
         *
         * @param methodOrConstructor the {@link Method} or {@link Constructor} for which the
         *                            parameter names are looked up.
         * @return an opened input stream on the corresponding .java file, or null if not found.
         */
        InputStream openJavaFile(AccessibleObject methodOrConstructor);

    }

    /**
     * Paranamer internal implementation of javaparser {@link GenericVisitor}.
     *
     * @author Gael Lazzari
     */
    private static class MethodParametersVisitor extends
            GenericVisitorAdapter> {

        private final StringBuilder currentClassName = new StringBuilder();
        private String packageName;

        @Override
        public Void visit(ClassOrInterfaceDeclaration n, Map arg) {

            currentClassName.append("$").append(n.getName());

            super.visit(n, arg);

            currentClassName.delete(currentClassName.length() - n.getName().length() - 1,
                    currentClassName.length());

            return null;
        }

        @Override
        public Void visit(ConstructorDeclaration n, Map arg) {
            Constructor c = findConstructor(n);

            String[] paramNames = extractParameterNames(n.getParameters());
            arg.put(c, paramNames);

            return super.visit(n, arg);

        }

        @Override
        public Void visit(MethodDeclaration n, Map arg) {

            Method m = findMethod(n);
            String[] paramNames = extractParameterNames(n.getParameters());
            arg.put(m, paramNames);

            return super.visit(n, arg);
        }

        @Override
        public Void visit(PackageDeclaration n, Map arg) {

            packageName = n.getName().toString();
            return super.visit(n, arg);
        }

        private boolean argsMatch(Class[] parameterTypes, List parameters) {

            if (parameters == null) {
                return parameterTypes.length == 0;
            } else if (parameters.size() != parameterTypes.length) {
                return false;
            }

            for (int i = 0; i < parameterTypes.length; i++) {
                Class paramType = parameterTypes[i];
                Parameter param = parameters.get(i);

                if (!paramType.getSimpleName().equals(param.getType().toString())) {
                    return false;
                }
            }

            return true;
        }

        private String[] extractParameterNames(List parameters) {
            int length = (parameters == null) ? 0 : parameters.size();
            String[] params = new String[length];

            for (int i = 0; i < params.length; i++) {
                params[i] = parameters.get(i).getId().getName();
            }

            return params;
        }

        private Constructor findConstructor(ConstructorDeclaration n) {
            Class currentVisitedClass = getCurrentVisitedClass();

            for (Constructor constructor : currentVisitedClass.getDeclaredConstructors()) {
                if (argsMatch(constructor.getParameterTypes(), n.getParameters())) {
                    return constructor;
                }
            }

            return null;
        }

        private Method findMethod(MethodDeclaration n) {
            Class currentVisitedClass = getCurrentVisitedClass();

            for (Method method : currentVisitedClass.getDeclaredMethods()) {
                if (!method.getName().equals(n.getName())) {
                    continue;
                } else if (argsMatch(method.getParameterTypes(), n.getParameters())) {
                    return method;
                }
            }

            return null;
        }

        private Class getCurrentVisitedClass() {
            String className = packageName + "." + currentClassName.substring(1);

            try {
                return Class.forName(className);
            } catch (ClassNotFoundException e) {
                throw new ParameterNamesNotFoundException("Error while trying to retrieve class "
                        + className + " :", e);
            }
        }

        ;

    }

    private final Map cache;
    private final JavaFileFinder javaFileFinder;

    /**
     * @param javaFileFinder Object responsible for opening .java files where requested
     *                       {@link Method} or {@link Constructor} are declared.
     */
    public JavaFileParanamer(JavaFileFinder javaFileFinder) {
        this.javaFileFinder = javaFileFinder;
        this.cache = new HashMap();
    }

    /*
     * (non-Javadoc)
     *
     * @see com.thoughtworks.paranamer.Paranamer#lookupParameterNames(java.lang.reflect
     * .AccessibleObject)
     */
    public String[] lookupParameterNames(AccessibleObject methodOrConstructor) {
        return lookupParameterNames(methodOrConstructor, true);
    }

    /*
     * (non-Javadoc)
     *
     * @see com.thoughtworks.paranamer.Paranamer#lookupParameterNames(java.lang.reflect
     * .AccessibleObject, boolean)
     */
    public String[] lookupParameterNames(AccessibleObject methodOrConstructor,
                                         boolean throwExceptionIfMissing) {

        if (methodOrConstructor == null) {
            throw new NullPointerException("method or constructor to inspect cannot be null");
        }

        String[] result = cache.get(methodOrConstructor);

        if (result == null) {
            visitJavaFileToPopulateCache(methodOrConstructor);
            result = cache.get(methodOrConstructor);
        }

        if (result == null && throwExceptionIfMissing) {
            throw new ParameterNamesNotFoundException("Cannot retrieve parameter names for method "
                    + methodOrConstructor.toString());
        }

        return result;
    }

    private void visitJavaFileToPopulateCache(AccessibleObject methodOrConstructor) {

        InputStream is = null;
        try {
            is = javaFileFinder.openJavaFile(methodOrConstructor);
            if (is != null) {
                // visit .java file using our custom GenericVisitorAdapter
                CompilationUnit cu = JavaParser.parse(is);
                MethodParametersVisitor visitor = new MethodParametersVisitor();
                cu.accept(visitor, cache);
            }
        } catch (Exception e) {
            throw new ParameterNamesNotFoundException(
                    "Error while trying to read parameter names from the Java file which contains the declaration of "
                            + methodOrConstructor.toString(), e);
        } finally {
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    // should never happen
                }
            }
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy