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

org.assertj.assertions.generator.util.ClassUtil Maven / Gradle / Ivy

/**
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 *
 * Copyright 2012-2015 the original author or authors.
 */
package org.assertj.assertions.generator.util;

import static com.google.common.collect.Sets.newLinkedHashSet;
import static java.lang.reflect.Modifier.isPublic;
import static org.apache.commons.lang3.StringUtils.substringAfter;
import static org.apache.commons.lang3.StringUtils.uncapitalize;

import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang3.ClassUtils;
import org.apache.commons.lang3.StringUtils;

import com.google.common.collect.ImmutableSet;
import com.google.common.reflect.ClassPath;
import com.google.common.reflect.ClassPath.ClassInfo;

/**
 * Some utilities methods related to classes and packages.
 *
 * @author Joel Costigliola
 */
public class ClassUtil {

  public static final String IS_PREFIX = "is";
  public static final String GET_PREFIX = "get";
  private static final String CLASS_SUFFIX = ".class";
  private static final Comparator GETTER_COMPARATOR = new Comparator() {
    @Override
    public int compare(Method m1, Method m2) {
      return m1.getName().compareTo(m2.getName());
    }
  };

  /**
   * Call {@link #collectClasses(ClassLoader, String...)} with Thread.currentThread().getContextClassLoader()
   * 
   */
  public static Set> collectClasses(String... classOrPackageNames) {
    return collectClasses(Thread.currentThread().getContextClassLoader(), classOrPackageNames);
  }

  /**
   * Collects all the public classes from given classes names or classes belonging to given a package name
   * (recursively).
   * 

* Note that anonymous and local classes are excluded from the returned classes. * * @param classLoader {@link ClassLoader} used to load classes defines in classOrPackageNames * @param classOrPackageNames classes names or packages names we want to collect classes from (recursively for * packages) * @return the set of {@link Class}es found * @throws RuntimeException if any error occurs */ public static Set> collectClasses(ClassLoader classLoader, String... classOrPackageNames) { Set> classes = newLinkedHashSet(); for (String classOrPackageName : classOrPackageNames) { Class clazz = tryToLoadClass(classOrPackageName, classLoader); if (clazz != null) { classes.add(clazz); } else { // should be a package classes.addAll(getClassesInPackage(classOrPackageName, classLoader)); } } return classes; } /** * Retrieves recursively all the classes belonging to a package. * * @param packageName package name we want to load classes from * @param classLoader the class loader used to load the classes in the given package * @return the list of Class found * @throws RuntimeException if any error occurs */ private static Set> getClassesInPackage(String packageName, ClassLoader classLoader) { if (classLoader == null) { throw new IllegalArgumentException("Null class loader."); } // load classes from classpath file system, this won't load classes in jars Set> packageClasses = getPackageClassesFromClasspathFiles(packageName, classLoader); // load classes from classpath jars try { packageClasses.addAll(getPackageClassesFromClasspathJars(packageName, classLoader)); } catch (IOException e) { throw new RuntimeException(e); } return packageClasses; } private static Set> getPackageClassesFromClasspathJars(String packageName, ClassLoader classLoader) throws IOException { ImmutableSet classesInfo = ClassPath.from(classLoader).getTopLevelClassesRecursive(packageName); Set> classesInPackage = new HashSet>(); for (ClassInfo classInfo : classesInfo) { classesInPackage.add(classInfo.load()); } Set> filteredClassesInPackage = new HashSet>(); for (Class classFromJar : classesInPackage) { if (isClassCandidateToAssertionsGeneration(classFromJar)) { filteredClassesInPackage.add(classFromJar); } } return filteredClassesInPackage; } private static Set> getPackageClassesFromClasspathFiles(String packageName, ClassLoader classLoader) { try { String packagePath = packageName.replace('.', File.separatorChar); // Ask for all resources for the path Enumeration resources = classLoader.getResources(packagePath); Set> classes = newLinkedHashSet(); while (resources.hasMoreElements()) { File directory = new File(URLDecoder.decode(resources.nextElement().getPath(), "UTF-8")); if (directory.canRead()) { classes.addAll(getClassesInDirectory(directory, packageName, classLoader)); } } return classes; } catch (UnsupportedEncodingException encex) { throw new RuntimeException(packageName + " does not appear to be a valid package (Unsupported encoding)", encex); } catch (IOException ioex) { throw new RuntimeException("IOException was thrown when trying to get all classes for " + packageName, ioex); } } /** * Get public classes in given directory (recursively). *

* Note that anonymous and local classes are excluded from the resulting set. * * @param directory directory where to look for classes * @param packageName package name corresponding to directory * @param classLoader used classloader * @return * @throws UnsupportedEncodingException */ private static Set> getClassesInDirectory(File directory, String packageName, ClassLoader classLoader) throws UnsupportedEncodingException { Set> classes = newLinkedHashSet(); // Capture all the .class files in this directory // Get the list of the files contained in the package File[] files = directory.listFiles(); for (File currentFile : files) { String currentFileName = currentFile.getName(); if (isClass(currentFileName)) { // CHECKSTYLE:OFF try { // removes the .class extension String className = packageName + '.' + StringUtils.remove(currentFileName, CLASS_SUFFIX); Class loadedClass = loadClass(className, classLoader); // we are only interested in public classes that are neither anonymous nor local if (isClassCandidateToAssertionsGeneration(loadedClass)) { classes.add(loadedClass); } } catch (Throwable e) { // do nothing. this class hasn't been found by the loader, and we don't care. } // CHECKSTYLE:ON } else if (currentFile.isDirectory()) { // It's another package String subPackageName = packageName + ClassUtils.PACKAGE_SEPARATOR + currentFileName; // Ask for all resources for the path URL resource = classLoader.getResource(subPackageName.replace('.', File.separatorChar)); File subDirectory = new File(URLDecoder.decode(resource.getPath(), "UTF-8")); Set> classesForSubPackage = getClassesInDirectory(subDirectory, subPackageName, classLoader); classes.addAll(classesForSubPackage); } } return classes; } /** * @param loadedClass * @return */ private static boolean isClassCandidateToAssertionsGeneration(Class loadedClass) { return loadedClass != null && isPublic(loadedClass.getModifiers()) && !loadedClass.isAnonymousClass() && !loadedClass.isLocalClass(); } private static boolean isClass(String fileName) { return fileName.endsWith(CLASS_SUFFIX); } private static Class tryToLoadClass(String className, ClassLoader classLoader) { try { return loadClass(className, classLoader); } catch (ClassNotFoundException e) { return null; } } private static Class loadClass(String className, ClassLoader classLoader) throws ClassNotFoundException { return Class.forName(className, true, classLoader); } /** * Returns the property name of given getter method, examples : *

* *

   * getName -> name
   * 
*

* *

   * isMostValuablePlayer -> mostValuablePlayer
   * 
* * @param getter getter method to deduce property from. * @return the property name of given getter method */ public static String propertyNameOf(Method getter) { String prefixToRemove = isPredicate(getter) ? IS_PREFIX : GET_PREFIX; String propertyWithCapitalLetter = substringAfter(getter.getName(), prefixToRemove); return uncapitalize(propertyWithCapitalLetter); } public static boolean inheritsCollectionOrIsIterable(Class returnType) { return Collection.class.isAssignableFrom(returnType) || Iterable.class.equals(returnType); } public static boolean isArray(Class returnType) { return returnType.isArray(); } public static boolean isStandardGetter(Method method) { return isValidStandardGetterName(method.getName()) && !Void.TYPE.equals(method.getReturnType()) && method.getParameterTypes().length == 0; } public static boolean isPredicate(Method method) { return isValidPredicateName(method.getName()) && (Boolean.TYPE.equals(method.getReturnType()) || Boolean.class.equals(method.getReturnType())) && method.getParameterTypes().length == 0; } public static boolean isValidGetterName(String methodName) { return PREFIX_PATTERN.matcher(methodName).find(); } static private final Pattern PREFIX_PATTERN; static private final Map PREDICATE_PREFIXES; static private final Comparator LONGEST_TO_SHORTEST = new Comparator() { @Override public int compare(String o1, String o2) { final int lengthComp = o2.length() - o1.length(); return lengthComp == 0 ? o1.compareTo(o2) : lengthComp; } }; static { String[][] predicates = new String[][] { { "is", "isNot" }, { "was", "wasNot" }, { "can", "cannot" }, { "should", "shouldNot" }, { "has", "doesNotHave" }, { "will", "willNot" }, }; StringBuilder pattern = new StringBuilder("^(?:get"); Map map = new HashMap(); for (String[] pair : predicates) { map.put(pair[0], pair[1]); map.put(pair[1], pair[0]); } TreeSet sort = new TreeSet(LONGEST_TO_SHORTEST); sort.addAll(map.keySet()); for (String prefix : sort) { pattern.append('|').append(prefix); } // next should be an Upper case letter pattern.append(")(?=\\p{Upper})"); PREFIX_PATTERN = Pattern.compile(pattern.toString()); PREDICATE_PREFIXES = Collections.unmodifiableMap(map); } private static boolean isValidStandardGetterName(String name) { Matcher m = PREFIX_PATTERN.matcher(name); return m.find() && m.group().equals(GET_PREFIX); } public static String getPredicatePrefix(String name) { Matcher m = PREFIX_PATTERN.matcher(name); return m.find() ? m.group() : null; } public static boolean isValidPredicateName(String name) { Matcher m = PREFIX_PATTERN.matcher(name); return m.find() && PREDICATE_PREFIXES.containsKey(m.group()); } public static String getNegativePredicateFor(String name) { Matcher m = PREFIX_PATTERN.matcher(name); if (m.find()) { return m.replaceFirst(PREDICATE_PREFIXES.get(m.group())); } return null; } public static Set declaredGetterMethodsOf(Class clazz) { return filterGetterMethods(clazz.getDeclaredMethods()); } public static Set getterMethodsOf(Class clazz) { return filterGetterMethods(clazz.getMethods()); } private static Set filterGetterMethods(Method[] methods) { Set getters = new TreeSet(GETTER_COMPARATOR); for (int i = 0; i < methods.length; i++) { Method method = methods[i]; if (isPublic(method.getModifiers()) && isNotDefinedInObjectClass(method) && isGetter(method)) { getters.add(method); } } return getters; } private static boolean isGetter(Method method) { return isStandardGetter(method) || isPredicate(method); } public static List nonStaticPublicFieldsOf(Class clazz) { Field[] fields = clazz.getFields(); List nonStaticPublicFields = new ArrayList(); for (Field field : fields) { if (isNotStaticPublicField(field)) { nonStaticPublicFields.add(field); } } return nonStaticPublicFields; } public static List declaredPublicFieldsOf(Class clazz) { Field[] fields = clazz.getDeclaredFields(); List nonStaticPublicFields = new ArrayList(); for (Field field : fields) { if (isNotStaticPublicField(field)) { nonStaticPublicFields.add(field); } } return nonStaticPublicFields; } private static boolean isNotStaticPublicField(Field field) { final int modifiers = field.getModifiers(); return !Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers); } private static boolean isNotDefinedInObjectClass(Method method) { return !method.getDeclaringClass().equals(Object.class); } public static Set> getClassesRelatedTo(Type type) { Set> classes = new HashSet>(); // non generic type : just add current type. if (type instanceof Class) { classes.add((Class) type); return classes; } // generic type : add current type and its parameter types if (type instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType) type; for (Type actualTypeArgument : parameterizedType.getActualTypeArguments()) { if (actualTypeArgument instanceof ParameterizedType) { classes.addAll(getClassesRelatedTo(actualTypeArgument)); } else if (actualTypeArgument instanceof Class) { classes.add((Class) actualTypeArgument); } else if (actualTypeArgument instanceof GenericArrayType) { classes.addAll(getClassesRelatedTo(actualTypeArgument)); } } Type rawType = parameterizedType.getRawType(); if (rawType instanceof Class) { classes.add((Class) rawType); } } return classes; } /** * Gets the simple name of the class but, unlike {@link Class#getSimpleName()}, it includes the name of the outer * class when clazz is an inner class. * * @param clazz * @return */ public static String getSimpleNameWithOuterClass(Class clazz) { if (isNotNestedClass(clazz)) { return clazz.getSimpleName(); } String nestedClassName = null; nestedClassName = clazz.getName(); nestedClassName = nestedClassName.substring(clazz.getPackage().getName().length() + 1); nestedClassName = nestedClassName.replace('$', '.'); return nestedClassName; } /** * Gets the simple name of the class but, unlike {@link Class#getSimpleName()}, it includes the name of the outer * class when clazz is an inner class, both class names are concatenated. *

* Example: * *

   * Outer.Inner -> OuterInner 
   * 
* * @param clazz * @return */ public static String getSimpleNameWithOuterClassNotSeparatedByDots(Class clazz) { if (isNotNestedClass(clazz)) { return clazz.getSimpleName(); } String nestedClassName = null; nestedClassName = clazz.getName(); nestedClassName = nestedClassName.substring(clazz.getPackage().getName().length() + 1); nestedClassName = StringUtils.remove(nestedClassName, '$'); return nestedClassName; } private static boolean isNotNestedClass(Class clazz) { return clazz.getDeclaringClass() == null; } /** * Get the underlying class for a type, or null if the type is a variable type. * * @param type the type * @return the underlying class */ public static Class getClass(final Type type) { if (type instanceof Class) return (Class) type; if (type instanceof ParameterizedType) return getClass(((ParameterizedType) type).getRawType()); if (type instanceof GenericArrayType) { final Type componentType = ((GenericArrayType) type).getGenericComponentType(); final Class componentClass = getClass(componentType); return componentClass == null ? null : Array.newInstance(componentClass, 0).getClass(); } else if (type instanceof WildcardType) { final WildcardType wildcardType = (WildcardType) type; return wildcardType.getUpperBounds() != null ? getClass(wildcardType.getUpperBounds()[0]) : wildcardType.getLowerBounds() != null ? getClass(wildcardType.getLowerBounds()[0]) : null; } else if (type instanceof TypeVariable) { final TypeVariable typeVariable = (TypeVariable) type; final Type[] bounds = typeVariable.getBounds(); return bounds.length > 0 ? getClass(bounds[0]) : Object.class; } return null; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy