package de.alpharogroup.lang; import java.io.File; import java.io.FileFilter; import java.io.IOException; import java.io.InputStream; import java.net.URISyntaxException; import java.net.URL; import java.net.URLDecoder; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import lombok.experimental.ExtensionMethod; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import de.alpharogroup.file.FileExtension; import de.alpharogroup.file.FilenameUtils; import de.alpharogroup.file.filter.ClassFileFilter; import de.alpharogroup.string.StringExtensions; /** * The Class ClassExtensions provides extension methods for the class {@link Class}. */ @ExtensionMethod(StringExtensions.class) public final class ClassExtensions { /** The Constant LOGGER. */ protected static final Logger LOGGER = Logger.getLogger(ClassExtensions.class); /** * Equal the given class objects by they qualified class names. * * @param oneClass * one class for equation by the qualified class name. * @param otherClass * the other class for equation by the qualified class name. * @return s true if the qualified class names are equal otherwise false. */ public static boolean equalsByClassName(final Class<?> oneClass, final Class<?> otherClass) { final String oneNormalizedClassName = ClassExtensions.normalizeQualifiedClassName(oneClass .getName()); final String otherNormalizedClassName = ClassExtensions .normalizeQualifiedClassName(otherClass.getName()); if (otherNormalizedClassName.equals(oneNormalizedClassName)) { return true; } return false; } /** * Look up the class in the "current" ClassLoader. * * @param className * The class name to load * @return the class * @throws ClassNotFoundException * if the Class was not found. */ public static Class<?> forName(final String className) throws ClassNotFoundException { final ClassLoader classLoader = getClassLoader(); Class<?> clazz = null; try { clazz = Class.forName(className); } catch (final Throwable throwable) { clazz = Class.forName(className, true, classLoader); } return clazz; } /** * Gets the parent base class from the given child class. * * @param childClass * the child class * @return the parent base class from the given child class. */ public static Class<?> getBaseClass(final Class<?> childClass) { if (childClass == null) { return childClass; } Class<?> superClass = childClass.getSuperclass(); while ((superClass != null) && !superClass.getSuperclass().equals(Object.class)) { superClass = superClass.getSuperclass(); } if (superClass == null) { return childClass; } return superClass; } /** * Gets the current class loader. * * @return 's the current class loader */ public static ClassLoader getClassLoader() { return ClassExtensions.getClassLoader(null); } /** * Gets the ClassLoader from the given object. * * @param obj * The object. * @return the ClassLoader from the given object. */ public static ClassLoader getClassLoader(final Object obj) { ClassLoader classLoader = null; if (null != obj) { if (isDerivate(Thread.currentThread().getContextClassLoader(), obj.getClass() .getClassLoader())) { classLoader = obj.getClass().getClassLoader(); } else { classLoader = Thread.currentThread().getContextClassLoader(); } if (isDerivate(classLoader, ClassLoader.getSystemClassLoader())) { classLoader = ClassLoader.getSystemClassLoader(); } } else { if (isDerivate(Thread.currentThread().getContextClassLoader(), ClassLoader.getSystemClassLoader())) { classLoader = ClassLoader.getSystemClassLoader(); } else { classLoader = Thread.currentThread().getContextClassLoader(); } } return classLoader; } /** * Gets the classname and concats the suffix ".class" from the class. * * @param clazz * The class. * @return The classname and concats the suffix ".class". */ public static String getClassnameWithSuffix(final Class<?> clazz) { String className = clazz.getName(); className = className.substring(className.lastIndexOf('.') + 1) + FileExtension.CLASS.getExtension(); return className; } /** * Gets the classname and concats the suffix ".class" from the object. * * @param obj * The object. * @return The classname and concats the suffix ".class". */ public static String getClassnameWithSuffix(final Object obj) { return getClassnameWithSuffix(obj.getClass()); } /** * Gets the directories from the given path. * * @param path * the path * @param isPackage * If the Flag is true than the given path is a package. * @return the directories from resources * @throws IOException * Signals that an I/O exception has occurred. */ public static List<File> getDirectoriesFromResources(String path, final boolean isPackage) throws IOException { if (isPackage) { path = path.replace('.', '/'); } final List<URL> resources = ClassExtensions.getResources(path); final List<File> dirs = new ArrayList<>(); for (final URL resource : resources) { dirs.add(new File(URLDecoder.decode(resource.getFile(), "UTF-8"))); } return dirs; } /** * If the given class is in a JAR, WAR or EAR file than the manifest url as String is returned. * * @param clazz * The class. * @return the manifest url as String if the given class is in a JAR, WAR or EAR file. */ public static String getManifestUrl(final Class<?> clazz) { String manifestUrl = null; final String path = ClassExtensions.getPath(clazz); final URL classUrl = ClassExtensions.getResource(path); if (classUrl != null) { final String classUrlString = classUrl.toString(); if ((classUrlString.startsWith("jar:") && (classUrlString.indexOf(path) > 0)) || (classUrlString.startsWith("war:") && (classUrlString.indexOf(path) > 0)) || (classUrlString.startsWith("ear:") && (classUrlString.indexOf(path) > 0))) { manifestUrl = classUrlString.replace(path, "/META-INF/MANIFEST.MF"); } } return manifestUrl; } /** * Returns the name of the given class or null if the given class is null. * * @param clazz * The class. * * @return The name of the given class. */ public static String getName(final Class<?> clazz) { return getName(clazz, false); } /** * Returns the name of the given class or null if the given class is null. If the given flag * 'simple' is true the simple name (without the package) will be returned. * * @param clazz * The class * @param simple * The flag if the simple name should be returned. * * @return The name of the given class or if the given flag 'simple' is true the simple name * (without the package) will be returned. */ public static String getName(Class<?> clazz, final boolean simple) { String name = null; if (clazz != null) { while (clazz.isAnonymousClass()) { clazz = clazz.getSuperclass(); } if (simple) { name = clazz.getSimpleName(); } else { name = clazz.getName(); } } return name; } /** * Gets the path from the given class. For instance /java/lang/Object.class if the given class * is from {@code Object} * * @param clazz * The class. * @return the path from the given class. */ public static String getPath(final Class<?> clazz) { final String path = "/" + clazz.getName().replaceAll("\\.", "/") + FileExtension.CLASS.getExtension(); return path; } /** * Finds the absolute path from the object. * * @param obj * The object. * @return The absolute path from the object. */ public static String getPathFromObject(final Object obj) { if (obj == null) { return null; } final String pathFromObject = obj.getClass() .getResource(ClassExtensions.getClassnameWithSuffix(obj)).getPath(); return pathFromObject; } /** * Gives the url from the path back. * * @param clazz * The class-object. * @param path * The path. * @return 's the url from the path. */ public static URL getResource(final Class<?> clazz, final String path) { URL url = clazz.getResource(path); if (null == url) { url = ClassExtensions.getClassLoader().getResource(path); } return url; } /** * Gives the URL from the resource. Wrapes the Class.getResource(String)-method. * * @param name * The name from the resource. * @return The resource or null if the resource does not exists. */ public static URL getResource(final String name) { String path = name; if (name.startsWith("/")) { path = name.substring(1, name.length()); } final URL url = ClassExtensions.getClassLoader().getResource(path); return url; } /** * Gives the URL from the resource. Wrapes the Class.getResource(String)-method. * * @param <T> * the generic type * @param name * The name from the resource. * @param obj * The Object. * @return The resource or null if the resource does not exists. */ public static <T> URL getResource(final String name, final T obj) { final Class<?> clazz = obj.getClass(); URL url = clazz.getResource(name); if (url == null) { url = getResource(clazz, name); } return url; } /** * Gives the resource as a file Object. * * @param name * The name from the file. * @return The file or null if the file does not exists. * @throws URISyntaxException * occurs by creation of the file with an uri. */ public static File getResourceAsFile(final String name) throws URISyntaxException { File file = null; URL url = getResource(name); if (null == url) { url = ClassExtensions.getClassLoader().getResource(name); if (null != url) { file = new File(url.toURI()); } } else { file = new File(url.toURI()); } return file; } /** * Gives the resource as a file Object. * * @param name * The name from the file. * @param obj * The Object. * @return The file or null if the file does not exists. * @throws URISyntaxException * occurs by creation of the file with an uri. */ public static File getResourceAsFile(final String name, final Object obj) throws URISyntaxException { File file = null; URL url = getResource(name, obj); if (null == url) { url = ClassExtensions.getClassLoader(obj).getResource(name); if (null != url) { file = new File(url.toURI()); } } else { file = new File(url.toURI()); } return file; } /** * This method call the getResourceAsStream from the ClassLoader. You can use this method to * read files from jar-files. * * @param clazz * the clazz * @param uri * The uri as String. * @return The InputStream from the uri. */ public static InputStream getResourceAsStream(final Class<?> clazz, final String uri) { InputStream is = clazz.getResourceAsStream(uri); if (null == is) { is = ClassExtensions.getClassLoader().getResourceAsStream(uri); } return is; } /** * Gives the Inputstream from the resource. Wrapes the Class.getResourceAsStream(String)-method. * * @param name * The name from the resource. * @return The resource or null if the resource does not exists. */ public static InputStream getResourceAsStream(final String name) { final ClassLoader loader = ClassExtensions.getClassLoader(); final InputStream inputStream = loader.getResourceAsStream(name); return inputStream; } /** * Gives the Inputstream from the resource. Wrapes the Class.getResourceAsStream(String)-method. * * @param name * The name from the resource. * @param obj * The Object. * @return The resource or null if the resource does not exists. */ public static InputStream getResourceAsStream(final String name, final Object obj) { InputStream inputStream = obj.getClass().getResourceAsStream(name); if (null == inputStream) { final ClassLoader loader = ClassExtensions.getClassLoader(obj); inputStream = loader.getResourceAsStream(name); } return inputStream; } /** * Gets a list with urls from the given path for all resources. * * @param path * The base path. * @return The resources. * @throws IOException * Signals that an I/O exception has occurred. */ public static List<URL> getResources(final String path) throws IOException { final ClassLoader classLoader = ClassExtensions.getClassLoader(); final List<URL> list = Collections.list(classLoader.getResources(path)); return list; } /** * Returns the simple name of the given class or null if the given class is null. * * @param clazz * The class. * * @return The simple name of the given class. */ public static String getSimpleName(final Class<?> clazz) { return getName(clazz, true); } /** * Returns the URL from the given class. * * @param clazz * The class. * @return the URL from the given class. */ public static URL getURL(final Class<?> clazz) { return ClassExtensions.getResource(ClassExtensions.getPath(clazz)); } /** * Compares the two given ClassLoader objects and returns true if compare is a derivate of * source. * * @param source * the source * @param compare * the compare * @return true, if compare is a derivate of source. */ public static boolean isDerivate(final ClassLoader source, ClassLoader compare) { if (source == compare) { return true; } if (compare == null) { return false; } if (source == null) { return true; } while (null != compare) { compare = compare.getParent(); if (source == compare) { return true; } } return false; } /** * Normalizes the given full qualified class name. This can be an entry from a jar file for * instance. * * @param qualifiedClassname * the full qualified class name to normalize. * @return The normalized class name. */ public static String normalizeQualifiedClassName(final String qualifiedClassname) { return normalizeSimpleClassName(qualifiedClassname).replaceAll("/", "."); } /** * Normalizes the given simple class name. * * @param className * the class name to normalize. * @return The normalized class name. */ public static String normalizeSimpleClassName(final String className) { String result = className; if (className.endsWith(FileExtension.CLASS.getExtension())) { result = className.replaceLast(FileExtension.CLASS.getExtension(), ""); } final int lastIndexOf$ = result.lastIndexOf("$"); if (lastIndexOf$ != -1) { final String prefix = result.substring(0, lastIndexOf$); final String compilerClassName = result.substring(lastIndexOf$ + 1, result.length()); if (StringUtils.isNumeric(compilerClassName)) { return prefix; } } return result; } /** * Scan for classes in the given directory. * * @param directory * the directory * @param packagePath * the package path * @return the list * @throws ClassNotFoundException * the class not found exception */ public static Set<Class<?>> scanClassesFromPackage(final File directory, final String packagePath) throws ClassNotFoundException { return scanClassesFromPackage(directory, packagePath, false); } /** * Scan recursive for classes in the given directory. * * @param directory * the directory * @param packagePath * the package path * @param recursive * the recursive * @return the list * @throws ClassNotFoundException * is thrown if a class in the given path cannot be located. */ public static Set<Class<?>> scanClassesFromPackage(final File directory, final String packagePath, final boolean recursive) throws ClassNotFoundException { final Set<Class<?>> foundClasses = new LinkedHashSet<>(); if (!directory.exists()) { return foundClasses; } // define the include filefilter for class files... final FileFilter includeFileFilter = new ClassFileFilter(); final File[] files = directory.listFiles(includeFileFilter); for (final File file : files) { String qualifiedClassname = null; if (file.isDirectory() && recursive) { qualifiedClassname = packagePath + "." + file.getName(); foundClasses.addAll(scanClassesFromPackage(file, qualifiedClassname, recursive)); } else { if (!file.isDirectory()) { final String filename = FilenameUtils.getFilenameWithoutExtension(file); qualifiedClassname = packagePath + '.' + filename; foundClasses.add(forName(qualifiedClassname)); } } } return foundClasses; } /** * Scan class names from the given package name. * * @param packageName * the package name * @return the Set with all class found in the given package name. * @throws IOException * Signals that an I/O exception has occurred. * @throws ClassNotFoundException * is thrown if a class in the given path cannot be located. */ public static Set<Class<?>> scanClassNames(final String packageName) throws IOException, ClassNotFoundException { return scanClassNames(packageName, false); } /** * Scan class names from the given package name. * * @param packageName * the package name * @param recursive * the recursive flag * @return the Set with all class found in the given package name. * @throws IOException * Signals that an I/O exception has occurred. * @throws ClassNotFoundException * is thrown if a class in the given path cannot be located. */ public static Set<Class<?>> scanClassNames(final String packageName, final boolean recursive) throws IOException, ClassNotFoundException { final Set<Class<?>> foundClasses = new LinkedHashSet<>(); final Set<String> qualifiedClassnames = PackageUtils.scanClassNames(packageName, recursive, true); for (final String qualifiedClassname : qualifiedClassnames) { foundClasses.add(forName(qualifiedClassname)); } return foundClasses; } }