/** * This file is part of the JCROM project. * Copyright (C) 2008-2014 - All rights reserved. * Authors: Olafur Gauti Gudmundsson, Nicolas Dos Santos * * 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. */ package org.jcrom.util; import javafx.beans.property.*; import org.jcrom.annotations.JcrNode; import java.io.*; import java.lang.reflect.*; import java.net.URL; import java.sql.Timestamp; import java.util.*; import java.util.jar.JarEntry; import java.util.jar.JarInputStream; /** * Various reflection utility methods, used mainly in the Mapper. * * @author Olafur Gauti Gudmundsson * @author Nicolas Dos Santos */ public final class ReflectionUtils { private ReflectionUtils() { } /** * Get an array of all fields declared in the supplied class, * and all its superclasses (except java.lang.Object). * * @param type the class for which we want to retrieve the Fields * @param returnFinalFields specifies whether to return final fields * @return an array of all declared and inherited fields */ public static Field[] getDeclaredAndInheritedFields(Class<?> type, boolean returnFinalFields) { List<Field> allFields = new ArrayList<Field>(); allFields.addAll(getValidFields(type.getDeclaredFields(), returnFinalFields)); Class<?> parent = type.getSuperclass(); while (parent != null && parent != Object.class) { allFields.addAll(getValidFields(parent.getDeclaredFields(), returnFinalFields)); parent = parent.getSuperclass(); } return allFields.toArray(new Field[allFields.size()]); } public static List<Field> getValidFields(Field[] fields, boolean returnFinalFields) { List<Field> validFields = new ArrayList<Field>(); // we ignore static and final fields for (Field field : fields) { if (!Modifier.isStatic(field.getModifiers()) && (returnFinalFields || !Modifier.isFinal(field.getModifiers()))) { validFields.add(field); } } return validFields; } /** * Retriveve a method from a given class or one of its superclass. * * @param type the class for which we want to retrieve the Method * @param name the name of the method * @param parametersType the parameters type of the method * @return */ public static Method getMethod(Class<?> type, String name, Class<?>... parametersType) throws NoSuchMethodException { try { return type.getMethod(name, parametersType); } catch (NoSuchMethodException e) { if (!Object.class.equals(type.getSuperclass())) { return getMethod(type.getSuperclass(), name, parametersType); } throw new NoSuchMethodException(); } } /** * Check if a class implements a specific interface. * * @param type the class we want to check * @param interfaceClass the interface class we want to check against * @return true if type implements interfaceClass, else false */ public static boolean implementsInterface(Class<?> type, Class<?> interfaceClass) { if (type.isInterface()) { return type == interfaceClass; } for (Class<?> ifc : type.getInterfaces()) { if (ifc == interfaceClass) { return true; } } return false; } /** * Check if a class extends a specific class. * * @param type the class we want to check * @param superClass the super class we want to check against * @return true if type implements superClass, else false */ public static boolean extendsClass(Class<?> type, Class<?> superClass) { if (type == superClass) { return true; } Class<?> c = type.getSuperclass(); while (c != null && c != Object.class) { if (c == superClass) { return true; } c = c.getSuperclass(); } return false; } /** * Check if the class supplied represents a valid JCR property type. * * @param type the class we want to check * @return true if the class represents a valid JCR property type */ public static boolean isPropertyType(Class<?> type) { return isValidMapValueType(type) || type == InputStream.class || isArrayOfType(type, byte.class); } public static boolean isValidMapValueType(Class<?> type) { if (type == ObjectProperty.class) { return true; } return type == String.class || type == StringProperty.class || isArrayOfType(type, String.class) || type == Date.class || isArrayOfType(type, Date.class) || type == Calendar.class || isArrayOfType(type, Calendar.class) || type == Timestamp.class || isArrayOfType(type, Timestamp.class) || type == Integer.class || type == IntegerProperty.class || isArrayOfType(type, Integer.class) || type == int.class || isArrayOfType(type, int.class) || type == Long.class || type == LongProperty.class || isArrayOfType(type, Long.class) || type == long.class || isArrayOfType(type, long.class) || type == Double.class || type == DoubleProperty.class || isArrayOfType(type, Double.class) || type == double.class || isArrayOfType(type, double.class) || type == Boolean.class || type == BooleanProperty.class || isArrayOfType(type, Boolean.class) || type == boolean.class || isArrayOfType(type, boolean.class) || type == Locale.class || isArrayOfType(type, Locale.class) || type.isEnum() || (type.isArray() && type.getComponentType().isEnum()); } private static boolean isArrayOfType(Class<?> c, Class<?> type) { return c.isArray() && c.getComponentType() == type; } public static boolean isDateType(Class<?> type) { return type == Date.class || type == Calendar.class || type == Timestamp.class; } /** * Get the (first) class that parameterizes the Field supplied. * * @param field the field * @return the class that parameterizes the field, or null if field is * not parameterized */ public static Class<?> getParameterizedClass(Field field) { return getParameterizedClass(field, 0); } /** * Get the class that parameterizes the Field supplied, at the index * supplied (field can be parameterized with multiple param classes). * * @param field the field * @param index the index of the parameterizing class * @return the class that parameterizes the field, or null if field is * not parameterized */ public static Class<?> getParameterizedClass(Field field, int index) { if (field.getGenericType() instanceof ParameterizedType) { ParameterizedType ptype = (ParameterizedType) field.getGenericType(); Type paramType = ptype.getActualTypeArguments()[index]; if (paramType instanceof GenericArrayType) { Class<?> arrayType = (Class<?>) ((GenericArrayType) paramType).getGenericComponentType(); return Array.newInstance(arrayType, 0).getClass(); } else { if (paramType instanceof ParameterizedType) { ParameterizedType paramPType = (ParameterizedType) paramType; return (Class<?>) paramPType.getRawType(); } else { return (Class<?>) paramType; } } } return null; } public static Class<?> getTypeArgumentOfParameterizedClass(Field field, int index, int typeIndex) { if (field.getGenericType() instanceof ParameterizedType) { ParameterizedType ptype = (ParameterizedType) field.getGenericType(); Type paramType = ptype.getActualTypeArguments()[index]; if (!(paramType instanceof GenericArrayType)) { if (paramType instanceof ParameterizedType) { ParameterizedType paramPType = (ParameterizedType) paramType; Type paramParamType = paramPType.getActualTypeArguments()[typeIndex]; if (!(paramParamType instanceof ParameterizedType)) { return (Class<?>) paramParamType; } } } } return null; } public static Class<?> getParameterizedClass(Class<?> c) { return getParameterizedClass(c, 0); } public static Class<?> getParameterizedClass(Class<?> c, int index) { TypeVariable<?>[] typeVars = c.getTypeParameters(); if (typeVars.length > 0) { return (Class<?>) typeVars[index].getBounds()[0]; } else { return null; } } /** * Tries to retrieve the generic parameter of an ObjectProperty at runtime. */ public static Class<?> getObjectPropertyGeneric(Object source, Field field) { Type type = field.getGenericType(); if (type instanceof ParameterizedType) { return getGenericClass(source, (ParameterizedType) type); } else { return field.getType(); } } /** * Try to extract the generic type of the given ParameterizedType used in the given source object. * * @param source * @param type * @return */ private static Class getGenericClass(Object source, ParameterizedType type) { Type type1 = type.getActualTypeArguments()[0]; if (type1 instanceof ParameterizedType) { return (Class) ((ParameterizedType) type1).getRawType(); } else if (type1 instanceof TypeVariable) { // Type is generic, try to get its actual type from the super class // e.g.: ObjectProperty<T> where T extends U if (source.getClass().getGenericSuperclass() instanceof ParameterizedType) { Type parameterizedType = ((ParameterizedType) source.getClass().getGenericSuperclass()).getActualTypeArguments()[0]; if (parameterizedType instanceof ParameterizedType) { // it means that the parent class is also generic return (Class) ((ParameterizedType) parameterizedType).getRawType(); } else { return (Class) parameterizedType; } } else { // The actual type is not declared, use the upper bound of the type e.g. U return (Class) ((TypeVariable) type1).getBounds()[0]; } } else { return (Class) type1; } } /** * Check if a field is parameterized with a specific class. * * @param field the field * @param c the class to check against * @return true if the field is parameterized and c is the class that * parameterizes the field, or is an interface that the parameterized class * implements, else false */ public static boolean isFieldParameterizedWithClass(Field field, Class<?> c) { if (field.getGenericType() instanceof ParameterizedType) { ParameterizedType ptype = (ParameterizedType) field.getGenericType(); for (Type type : ptype.getActualTypeArguments()) { if (type == c) { return true; } if (c.isInterface() && implementsInterface((Class<?>) type, c)) { return true; } } } return false; } /** * Check if the field supplied is parameterized with a valid JCR * property type. * * @param field the field * @return true if the field is parameterized with a valid JCR property * type, else false */ public static boolean isFieldParameterizedWithPropertyType(Field field) { if (field.getGenericType() instanceof ParameterizedType) { ParameterizedType ptype = (ParameterizedType) field.getGenericType(); for (Type type : ptype.getActualTypeArguments()) { if (isPropertyType((Class<?>) type)) { return true; } } } return false; } public static JcrNode getJcrNodeAnnotation(Class<?> c) { if (c.isAnnotationPresent(JcrNode.class)) { return c.getAnnotation(JcrNode.class); } else { // need to check all superclasses Class<?> parent = c.getSuperclass(); while (parent != null && parent != Object.class) { if (parent.isAnnotationPresent(JcrNode.class)) { return parent.getAnnotation(JcrNode.class); } // ...and interfaces that the superclass implements for (Class<?> interfaceClass : parent.getInterfaces()) { if (interfaceClass.isAnnotationPresent(JcrNode.class)) { return interfaceClass.getAnnotation(JcrNode.class); } } parent = parent.getSuperclass(); } // ...and all implemented interfaces for (Class<?> interfaceClass : c.getInterfaces()) { if (interfaceClass.isAnnotationPresent(JcrNode.class)) { return interfaceClass.getAnnotation(JcrNode.class); } } } // no annotation found, use the defaults return null; } private static String stripFilenameExtension(String filename) { if (filename.indexOf('.') != -1) { return filename.substring(0, filename.lastIndexOf('.')); } else { return filename; } } public static Set<Class<?>> getFromDirectory(File directory, String packageName) throws ClassNotFoundException { Set<Class<?>> classes = new HashSet<Class<?>>(); if (directory.exists()) { for (String file : directory.list()) { if (file.endsWith(".class")) { String name = packageName + '.' + stripFilenameExtension(file); Class<?> clazz = Class.forName(name); classes.add(clazz); } } } return classes; } public static Set<Class<?>> getFromJARFile(String jar, String packageName) throws IOException, FileNotFoundException, ClassNotFoundException { Set<Class<?>> classes = new HashSet<Class<?>>(); JarInputStream jarFile = new JarInputStream(new FileInputStream(jar)); JarEntry jarEntry; do { jarEntry = jarFile.getNextJarEntry(); if (jarEntry != null) { String className = jarEntry.getName(); if (className.endsWith(".class")) { className = stripFilenameExtension(className); if (className.startsWith(packageName)) { classes.add(Class.forName(className.replace('/', '.'))); } } } } while (jarEntry != null); return classes; } public static Set<Class<?>> getClasses(String packageName) throws IOException, ClassNotFoundException { ClassLoader loader = Thread.currentThread().getContextClassLoader(); return getClasses(loader, packageName); } public static Set<Class<?>> getClasses(ClassLoader loader, String packageName) throws IOException, ClassNotFoundException { Set<Class<?>> classes = new HashSet<Class<?>>(); String path = packageName.replace('.', '/'); Enumeration<URL> resources = loader.getResources(path); if (resources != null) { while (resources.hasMoreElements()) { String filePath = resources.nextElement().getFile(); // WINDOWS HACK if (filePath.indexOf("%20") > 0) { filePath = filePath.replaceAll("%20", " "); } if (filePath != null) { if ((filePath.indexOf("!") > 0) & (filePath.indexOf(".jar") > 0)) { String jarPath = filePath.substring(0, filePath.indexOf("!")).substring(filePath.indexOf(":") + 1); // WINDOWS HACK if (jarPath.indexOf(":") >= 0) { jarPath = jarPath.substring(1); } classes.addAll(getFromJARFile(jarPath, path)); } else { classes.addAll(getFromDirectory(new File(filePath), packageName)); } } } } return classes; } }