Java tutorial
/******************************************************************************* * Copyright (C) 2017, 2018 wysohn * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. *******************************************************************************/ package io.github.wysohn.triggerreactor.tools; import java.io.File; import java.io.IOException; import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; import java.util.Arrays; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.zip.ZipEntry; import java.util.zip.ZipException; import java.util.zip.ZipFile; import org.apache.commons.lang3.ClassUtils; public class ReflectionUtil { public static void setField(Object obj, String fieldName, Object value) throws NoSuchFieldException, IllegalArgumentException { Class<?> clazz = obj.getClass(); Field field = clazz.getDeclaredField(fieldName); field.setAccessible(true); try { field.set(obj, value); } catch (IllegalAccessException e) { e.printStackTrace(); } } public static void setFinalField(Object obj, String fieldName, Object value) throws NoSuchFieldException { setFinalField(obj.getClass(), obj, fieldName, value); } public static void setFinalField(Class<?> clazz, Object obj, String fieldName, Object value) throws NoSuchFieldException { Field field = clazz.getDeclaredField(fieldName); setFinalField(obj, field, value); } /** * https://stackoverflow.com/questions/3301635/change-private-static-final-field-using-java-reflection * @param field * @param newValue * @throws SecurityException * @throws NoSuchFieldException * @throws Exception */ private static void setFinalField(Object target, Field field, Object newValue) throws NoSuchFieldException { field.setAccessible(true); Field modifiersField = null; try { modifiersField = Field.class.getDeclaredField("modifiers"); } catch (SecurityException e1) { e1.printStackTrace(); } modifiersField.setAccessible(true); try { modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); } catch (IllegalArgumentException | IllegalAccessException e) { e.printStackTrace(); } try { field.set(target, newValue); } catch (IllegalAccessException e) { e.printStackTrace(); } } public static Object getField(Object obj, String fieldName) throws NoSuchFieldException, IllegalArgumentException { Class<?> clazz = obj.getClass(); Field field = clazz.getDeclaredField(fieldName); field.setAccessible(true); try { return field.get(obj); } catch (IllegalAccessException e) { e.printStackTrace(); } return null; } public static Object getField(Class<?> clazz, Object obj, String fieldName) throws NoSuchFieldException, IllegalArgumentException { Field field = clazz.getDeclaredField(fieldName); field.setAccessible(true); try { return field.get(obj); } catch (IllegalAccessException e) { e.printStackTrace(); } return null; } @SuppressWarnings({ "unchecked", "unchecked" }) public static Object invokeMethod(Class<?> clazz, Object obj, String methodName, Object... args) throws NoSuchMethodException, IllegalArgumentException, InvocationTargetException, IllegalAccessException { try { List<Method> validMethods = new ArrayList<>(); for (Method method : clazz.getMethods()) { Class<?>[] parameterTypes = null; if (!method.getName().equals(methodName)) { continue; } parameterTypes = method.getParameterTypes(); if (method.isVarArgs()) { if (method.isVarArgs() && (parameterTypes.length - args.length >= 2)) { parameterTypes = null; continue; } } else { if (parameterTypes.length != args.length) { parameterTypes = null; continue; } } if (method.isVarArgs()) { boolean matches = false; // check non vararg part for (int i = 0; i < parameterTypes.length - 1; i++) { matches = checkMatch(parameterTypes[i], args[i]); if (!matches) break; } // check rest for (int i = parameterTypes.length - 1; i < args.length; i++) { Class<?> arrayType = parameterTypes[parameterTypes.length - 1].getComponentType(); matches = checkMatch(arrayType, args[i]); if (!matches) break; } if (matches) { validMethods.add(method); } } else { boolean matches = true; for (int i = 0; i < parameterTypes.length; i++) { matches = checkMatch(parameterTypes[i], args[i]); if (!matches) break; } if (matches) { validMethods.add(method); } } } if (!validMethods.isEmpty()) { Method method = validMethods.get(0); for (int i = 1; i < validMethods.size(); i++) { Method targetMethod = validMethods.get(i); Class<?>[] currentParams = method.getParameterTypes(); Class<?>[] targetParams = targetMethod.getParameterTypes(); if (method.isVarArgs() && targetMethod.isVarArgs()) { for (int j = 0; j < currentParams.length; j++) { if (currentParams[j].isAssignableFrom(targetParams[j])) { method = targetMethod; break; } } } else if (method.isVarArgs()) { //usually, non-vararg is more specific method. So we use that method = targetMethod; } else if (targetMethod.isVarArgs()) { //do nothing } else { for (int j = 0; j < currentParams.length; j++) { if (targetParams[j].isEnum()) { // enum will be handled later method = targetMethod; break; } else if (ClassUtils.isAssignable(targetParams[j], currentParams[j], true)) { //narrow down to find the most specific method method = targetMethod; break; } } } } method.setAccessible(true); for (int i = 0; i < args.length; i++) { Class<?>[] parameterTypes = method.getParameterTypes(); if (args[i] instanceof String && i < parameterTypes.length && parameterTypes[i].isEnum()) { try { args[i] = Enum.valueOf((Class<? extends Enum>) parameterTypes[i], (String) args[i]); } catch (IllegalArgumentException ex1) { // Some overloaded methods already has // String to Enum conversion // So just lets see if one exists Class<?>[] types = new Class<?>[args.length]; for (int k = 0; k < args.length; k++) types[k] = args[k].getClass(); try { Method alternative = clazz.getMethod(methodName, types); return alternative.invoke(obj, args); } catch (NoSuchMethodException ex2) { throw new RuntimeException( "Tried to convert value [" + args[i] + "] to Enum [" + parameterTypes[i] + "] or find appropriate method but found nothing. Make sure" + " that the value [" + args[i] + "] matches exactly with one of the Enums in [" + parameterTypes[i] + "] or the method you are looking exists."); } } } } if (method.isVarArgs()) { Class<?>[] parameterTypes = method.getParameterTypes(); Object varargs = Array.newInstance(parameterTypes[parameterTypes.length - 1].getComponentType(), args.length - parameterTypes.length + 1); for (int k = 0; k < Array.getLength(varargs); k++) { Array.set(varargs, k, args[parameterTypes.length - 1 + k]); } Object[] newArgs = new Object[parameterTypes.length]; for (int k = 0; k < newArgs.length - 1; k++) { newArgs[k] = args[k]; } newArgs[newArgs.length - 1] = varargs; args = newArgs; } return method.invoke(obj, args); } if (args.length > 0) { StringBuilder builder = new StringBuilder(String.valueOf(args[0].getClass().getSimpleName())); for (int i = 1; i < args.length; i++) { builder.append(", " + args[i].getClass().getSimpleName()); } throw new NoSuchMethodException(methodName + "(" + builder.toString() + ")"); } else { throw new NoSuchMethodException(methodName + "()"); } } catch (NullPointerException e) { StringBuilder builder = new StringBuilder(String.valueOf(args[0])); for (int i = 1; i < args.length; i++) builder.append("," + String.valueOf(args[i])); throw new NullPointerException("Call " + methodName + "(" + builder.toString() + ")"); } } private static boolean checkMatch(Class<?> parameterType, Object arg) { // skip enum if argument was String. We will try valueOf() later if (!(arg instanceof String && parameterType.isEnum()) && !ClassUtils.isAssignable(arg == null ? null : arg.getClass(), parameterType, true)) { return false; } return true; } private static boolean compareClass(Class<?> clazz1, Class<?> clazz2) { if (ClassUtils.isPrimitiveWrapper(clazz1)) clazz1 = ClassUtils.wrapperToPrimitive(clazz1); if (ClassUtils.isPrimitiveWrapper(clazz2)) clazz2 = ClassUtils.wrapperToPrimitive(clazz2); if (clazz1 != null) { return clazz1.equals(clazz2); } else if (clazz2 != null) { return clazz2.equals(clazz1); } else { return true; } } public static Object invokeMethod(Object obj, String methodName, Object... args) throws NoSuchMethodException, IllegalArgumentException, InvocationTargetException, IllegalAccessException { Class<?> clazz = obj.getClass(); return invokeMethod(clazz, obj, methodName, args); } /** * extract all possible field values. Primitive types will be 'copied,' and * reference types can be referenced. * * @param e * @return */ public static Map<String, Object> extractVariables(Object e) { Map<String, Object> map = new HashMap<String, Object>(); Class<?> clazz = e.getClass(); for (Field field : getAllFields(new ArrayList<Field>(), clazz)) { field.setAccessible(true); try { map.put(field.getName(), field.get(e)); } catch (IllegalArgumentException | IllegalAccessException e1) { e1.printStackTrace(); } } return map; } public static Map<String, Object> extractVariablesWithEnumAsString(Object e) { Map<String, Object> map = new HashMap<String, Object>(); Class<?> clazz = e.getClass(); for (Field field : getAllFields(new ArrayList<Field>(), clazz)) { field.setAccessible(true); try { if (field.getClass().isEnum()) { Enum<?> enumVal = (Enum<?>) field.get(e); map.put(field.getName(), enumVal.name()); } else { map.put(field.getName(), field.get(e)); } } catch (IllegalArgumentException | IllegalAccessException e1) { e1.printStackTrace(); } } return map; } /** * http://stackoverflow.com/questions/1042798/retrieving-the-inherited-attribute-names-values-using-java-reflection * @param fields * @param c * @return */ public static List<Field> getAllFields(List<Field> fields, Class<?> c) { fields.addAll(Arrays.asList(c.getDeclaredFields())); if (c.getSuperclass() != null) { fields = getAllFields(fields, c.getSuperclass()); } return fields; } public static List<Field> getAllPublicFields(List<Field> fields, Class<?> c) { fields.addAll(Arrays.asList(c.getFields())); if (c.getSuperclass() != null) { fields = getAllPublicFields(fields, c.getSuperclass()); } return fields; } public static List<Method> getAllPublicMethods(List<Method> methods, Class<?> c) { methods.addAll(Arrays.asList(c.getMethods())); if (c.getSuperclass() != null) { methods = getAllPublicMethods(methods, c.getSuperclass()); } return methods; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //https://stackoverflow.com/questions/520328/can-you-find-all-classes-in-a-package-using-reflection public static List<String> getAllClasses(ClassLoader cl, String packageName) { packageName = packageName.replace('.', '/'); List<String> classes = new ArrayList<>(); URL[] urls = ((URLClassLoader) cl).getURLs(); for (URL url : urls) { //System.out.println(url.getFile()); File jar = new File(url.getFile()); if (jar.isDirectory()) { File subdir = new File(jar, packageName); if (!subdir.exists()) continue; File[] files = subdir.listFiles(); for (File file : files) { if (!file.isFile()) continue; if (file.getName().endsWith(".class")) classes.add(file.getName().substring(0, file.getName().length() - 6).replace('/', '.')); } } else { // try to open as ZIP try { ZipFile zip = new ZipFile(jar); for (Enumeration<? extends ZipEntry> entries = zip.entries(); entries.hasMoreElements();) { ZipEntry entry = entries.nextElement(); String name = entry.getName(); if (!name.startsWith(packageName)) continue; if (name.endsWith(".class") && name.indexOf('$') < 0) classes.add(name.substring(0, name.length() - 6).replace('/', '.')); } zip.close(); } catch (ZipException e) { //System.out.println("Not a ZIP: " + e.getMessage()); } catch (IOException e) { System.err.println(e.getMessage()); } } } return classes; } public static Object constructNew(Class<?> clazz, Object[] args) throws NoSuchMethodException, InstantiationException, IllegalArgumentException, IllegalAccessException { if (args.length < 1) { return clazz.newInstance(); } else { Class<?>[] paramTypes = new Class[args.length]; for (int i = 0; i < args.length; i++) { paramTypes[i] = args[i].getClass(); } Constructor<?> con = clazz.getConstructor(paramTypes); try { return con.newInstance(args); } catch (InvocationTargetException e) { e.printStackTrace(); } return null; } } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /* public static void main(String[] ar) throws NoSuchMethodException, IllegalArgumentException, InvocationTargetException, IllegalAccessException{ System.out.println(invokeMethod(Test.class, (Object) null, "someMethod1", 1,2,"hey")); } public static class Test{ public static Object someMethod1(Object... val) { return "Object"; } public static Object someMethod1(Integer... val) { return "Integer"; } public static Object someMethod1(Integer val) { return "test"; } }*/ }