org.codefilarete.tool.reflect.MemberPrinter Maven / Gradle / Ivy
package org.codefilarete.tool.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;
import java.util.StringTokenizer;
import org.codefilarete.tool.StringAppender;
import org.codefilarete.tool.collection.Arrays;
import org.codefilarete.tool.exception.NotImplementedException;
import org.codefilarete.tool.Nullable;
/**
* A class for custom Reflection API print. Mainly for compact package presentation.
*
* @author Guillaume Mary
*/
public class MemberPrinter {
/** A printer that shows the whole package name (full path) */
public static final MemberPrinter FULL_PACKAGE_PRINTER = new MemberPrinter(Package::getName);
/** A printer for compact package name presentation (first letter of each path) */
public static final MemberPrinter FLATTEN_PACKAGE_PRINTER = new MemberPrinter(MemberPrinter::flattenPackage);
/** A printer that won't print package name for "well known" package : java.util and java.lang */
public static final MemberPrinter WELL_KNOWN_FLATTEN_PACKAGE_PRINTER = new MemberPrinter(new PackagePrinter() {
private final Set WELL_KNOW_PACKAGES = Collections.unmodifiableSet(Arrays.asSet(
Collection.class.getPackage(),
String.class.getPackage()));
@Override
public String toString(Package aPackage) {
if (WELL_KNOW_PACKAGES.contains(aPackage)) {
// well-known packages are not printed
return "";
} else {
return FLATTEN_PACKAGE_PRINTER.toString(aPackage);
}
}
});
/**
* Gives a compact view of a package name by keeping only first character of each path, separated by dots.
*
* @param aPackage not null (not compatible with top level classes / primitive types)
* @return a String of the first character of package names
*/
private static String flattenPackage(Package aPackage) {
StringAppender result = new StringAppender();
StringTokenizer tokenizer = new StringTokenizer(aPackage.getName(), ".", false);
while (tokenizer.hasMoreTokens()) {
String packageName = tokenizer.nextToken();
result.cat(packageName.charAt(0), ".");
}
result.cutTail(1);
return result.toString();
}
private final PackagePrinter packagePrinter;
public MemberPrinter(PackagePrinter packagePrinter) {
this.packagePrinter = packagePrinter;
}
public String toString(Package aPackage) {
// prevent top level class case
return aPackage == null ? "" : packagePrinter.toString(aPackage);
}
public String toString(Class aClass) {
Package classPackage = aClass.isArray() ? aClass.getComponentType().getPackage() : aClass.getPackage();
String packageName = toString(classPackage);
if (packageName.isEmpty()) {
return aClass.getSimpleName();
} else {
return packageName + "." + Nullable.nullable(aClass.getEnclosingClass()).map(c -> c.getSimpleName() + "$").getOr("") + aClass.getSimpleName();
}
}
public String toString(Constructor constructor) {
return new ClassAppender().cat(constructor.getDeclaringClass())
.cat("(").ccat(constructor.getParameterTypes(), ", ").cat(")").toString();
}
public String toString(Method method) {
return new ClassAppender().cat(method.getDeclaringClass(), ".", method.getName())
.cat("(").ccat(method.getParameterTypes(), ", ").cat(")").toString();
}
public String toString(Executable executable) {
if (executable instanceof Constructor) {
return toString((Constructor) executable);
} else if (executable instanceof Method) {
return toString((Method) executable);
} else {
throw new NotImplementedException(Nullable.nullable(executable).map(Object::getClass).map(this::toString).getOr("null instance") + " is not supported");
}
}
public String toString(Field field) {
return new ClassAppender().cat(field.getDeclaringClass(), ".", field.getName()).toString();
}
@FunctionalInterface
public interface PackagePrinter {
/**
* Gives a representation of a package
* @param aPackage not null
* @return not null, an empty String in worst case
*/
String toString(Package aPackage);
}
private class ClassAppender extends StringAppender {
@Override
public StringAppender cat(Object o) {
if (o instanceof Class) {
return super.cat(MemberPrinter.this.toString((Class) o));
} else {
return super.cat(o);
}
}
}
}