/*
* Made by Wannes 'W' De Smet
* (c) 2011 Wannes De Smet
* All rights reserved.
*
*/
package net.wgr.core;
import org.apache.log4j.Logger;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.URISyntaxException;
import java.net.URL;
import java.security.CodeSource;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
/**
* @author wannes
* @created Jul 8, 2011
*/
public class ReflectionUtils {
private ReflectionUtils() {
}
public static Field[] getAllFields(Class clazz) {
List<Field> fields = new ArrayList<>();
fields.addAll(Arrays.asList(clazz.getDeclaredFields()));
if (clazz.getSuperclass() != null) {
fields.addAll(Arrays.asList(getAllFields(clazz.getSuperclass())));
}
return fields.toArray(new Field[]{});
}
public static Method[] getAllMethods(Class clazz) {
ArrayList<Method> methods = new ArrayList<>();
if (clazz != null) {
methods.addAll(Arrays.asList(clazz.getDeclaredMethods()));
}
Class parent = clazz.getSuperclass();
while (parent != null) {
methods.addAll(Arrays.asList(parent.getDeclaredMethods()));
parent = parent.getSuperclass();
}
return methods.toArray(new Method[0]);
}
/**
* Scans all classes accessible from the context class loader which belong
* to the given package.
*
* @param packageName The base package
* @return The classes
* @throws ClassNotFoundException
* @throws IOException
*/
public static List<Class> getClasses(String packageName, Class sourceContext) throws ClassNotFoundException, IOException {
CodeSource src = sourceContext.getProtectionDomain().getCodeSource();
List<Class> classes = new ArrayList<>();
if (src != null) {
URL jar = src.getLocation();
File f = null;
try {
f = new File(jar.toURI());
} catch (URISyntaxException ex) {
Logger.getLogger(ReflectionUtils.class).error("Illegal source path given by CodeSource", ex);
return classes;
}
if (f.isDirectory()) {
File target = new File(f, packageName.replace('.', '/'));
classes = findClasses(target, packageName);
} else {
ZipInputStream zip = new ZipInputStream(jar.openStream());
ZipEntry ze = null;
String path = packageName.replace('.', '/');
while ((ze = zip.getNextEntry()) != null) {
if (ze.getName().startsWith(path) && ze.getName().endsWith(".class")) {
// Remove leading slash
int start = path.length() + 1;
// Remove trailing .class extension
int end = ze.getName().length() - 6;
if (start > end) {
continue;
}
String className = ze.getName().substring(start, end);
if (!className.isEmpty() && !className.contains("$") && !className.contains("/")) {
classes.add(Class.forName(packageName + '.' + className));
}
}
}
}
}
return classes;
}
/**
* Recursive method used to find all classes in a given directory.
*
* @param directory The base directory
* @param packageName The package name for classes found inside the base
* directory
* @return The classes
* @throws ClassNotFoundException
* @source http://snippets.dzone.com/posts/show/4831
*/
private static List<Class> findClasses(File directory, String packageName) throws ClassNotFoundException {
List<Class> classes = new ArrayList<>();
if (!directory.exists()) {
return classes;
}
File[] files = directory.listFiles();
for (File file : files) {
String fileName = file.getName();
if (file.isDirectory()) {
assert !fileName.contains(".");
// Do not recurse ATM
//classes.addAll(findClasses(file, packageName + "." + fileName));
} else if (fileName.endsWith(".class") && !fileName.contains("$")) {
Class clazz;
try {
clazz = Class.forName(packageName + '.' + fileName.substring(0, fileName.length() - 6));
} catch (ExceptionInInitializerError e) {
// happen, for example, in classes, which depend on
// Spring to inject some beans, and which fail,
// if dependency is not fulfilled
clazz = Class.forName(packageName + '.' + fileName.substring(0, fileName.length() - 6),
false, Thread.currentThread().getContextClassLoader());
}
classes.add(clazz);
}
}
return classes;
}
/**
* Returns difference between instances
*
* @param <T> instance type
* @param original
* @param changed
* @return <fieldName, value>
*/
public static <T> Map<String, Object> diff(T original, T changed) {
if (original == null && changed == null) {
throw new IllegalArgumentException("Original and changed object were null");
}
boolean addAll = false;
if (original == null) {
addAll = true;
original = changed;
}
if (changed == null) {
addAll = true;
changed = original;
}
HashMap<String, Object> result = new HashMap<>();
for (Field f : getAllFields(original.getClass())) {
if (Modifier.isTransient(f.getModifiers())) {
continue;
}
f.setAccessible(true);
try {
Object origVal = f.get(original);
Object newVal = f.get(changed);
if (addAll || (origVal == null && newVal != null) || (origVal != null && !origVal.equals(newVal))) {
result.put(f.getName(), newVal);
}
} catch (IllegalArgumentException | IllegalAccessException ex) {
Logger.getLogger(ReflectionUtils.class).error("Failed to reflect properly", ex);
}
}
return result;
}
}