Package com.thoughtworks.paranamer

Source Code of com.thoughtworks.paranamer.JavaFileParanamer$JavaFileFinder

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 <a
* href="http://code.google.com/p/javaparser/">javaparser library</a> 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<Void, Map<AccessibleObject, String[]>> {

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

      @Override
      public Void visit(ClassOrInterfaceDeclaration n, Map<AccessibleObject, String[]> 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<AccessibleObject, String[]> 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<AccessibleObject, String[]> 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<AccessibleObject, String[]> arg) {

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

      private boolean argsMatch(Class<?>[] parameterTypes, List<Parameter> 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<Parameter> 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<AccessibleObject, String[]> 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<AccessibleObject, String[]>();
   }

   /*
    * (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
            }
         }
      }
   }

}
TOP

Related Classes of com.thoughtworks.paranamer.JavaFileParanamer$JavaFileFinder

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.