package com.dianping.pigeon.remoting.common.util;
import java.io.IOException;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import com.dianping.pigeon.remoting.common.util.StringizerUtils.Reflects.IMemberFilter;
public class StringizerUtils {
public static JsonStringizer forJson() {
return JsonStringizer.DEFAULT;
}
public static enum JsonStringizer {
DEFAULT(false),
COMPACT(true);
private boolean m_compact;
private JsonStringizer(boolean compact) {
m_compact = compact;
}
public JsonStringizer compact() {
return COMPACT;
}
public String from(Object obj) {
return from(obj, 0, 0);
}
public String from(Object obj, int maxLength, int maxItemLength) {
StringBuilder sb = new StringBuilder(1024);
LengthLimiter limiter = new LengthLimiter(sb, maxLength,
maxItemLength);
Set<Object> done = new HashSet<Object>();
try {
fromObject(done, limiter, obj);
} catch (RuntimeException e) {
// expected
sb.append("...");
}
return sb.toString();
}
private void fromArray(Set<Object> done, LengthLimiter sb, Object obj) {
int len = Array.getLength(obj);
sb.append('[');
for (int i = 0; i < len; i++) {
if (i > 0) {
sb.append(',');
if (!m_compact) {
sb.append(' ');
}
}
Object element = Array.get(obj, i);
fromObject(done, sb, element);
}
sb.append(']');
}
@SuppressWarnings("unchecked")
private void fromCollection(Set<Object> done, LengthLimiter sb,
Object obj) {
boolean first = true;
sb.append('[');
for (Object item : ((Collection<Object>) obj)) {
if (first) {
first = false;
} else {
sb.append(',');
if (!m_compact) {
sb.append(' ');
}
}
fromObject(done, sb, item);
}
sb.append(']');
}
@SuppressWarnings("unchecked")
private void fromMap(Set<Object> done, LengthLimiter sb, Object obj) {
boolean first = true;
sb.append('{');
for (Map.Entry<Object, Object> e : ((Map<Object, Object>) obj)
.entrySet()) {
Object key = e.getKey();
Object value = e.getValue();
if (value == null) {
continue;
}
if (first) {
first = false;
} else {
sb.append(',');
if (!m_compact) {
sb.append(' ');
}
}
sb.append('"').append(key).append("\":");
if (!m_compact) {
sb.append(' ');
}
fromObject(done, sb, value);
}
sb.append('}');
}
private void fromObject(Set<Object> done, LengthLimiter sb, Object obj) {
if (obj == null) {
return;
}
Class<?> type = obj.getClass();
if (type == String.class) {
sb.append('"').append(obj.toString(), true).append('"');
} else if (type.isPrimitive()
|| Number.class.isAssignableFrom(type) || type.isEnum()) {
sb.append(obj.toString(), true);
} else if (type == Boolean.class) {
sb.append(obj.toString(), true);
} else if (type == Date.class) {
sb.append('"')
.append(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
.format(obj),
true).append('"');
} else if (type == Class.class) {
sb.append('"').append(obj, true).append('"');
} else if (done.contains(obj)) {
sb.append("{}");
return;
} else {
done.add(obj);
if (type.isArray()) {
fromArray(done, sb, obj);
} else if (Collection.class.isAssignableFrom(type)) {
fromCollection(done, sb, obj);
} else if (Map.class.isAssignableFrom(type)) {
fromMap(done, sb, obj);
} else {
fromPojo(done, sb, obj);
}
}
}
private void fromPojo(Set<Object> done, LengthLimiter sb, Object obj) {
Class<? extends Object> type = obj.getClass();
if (hasToString(type)) {
fromObject(done, sb, obj.toString());
return;
}
List<Method> getters = Reflects.forMethod().getMethods(type,
new IMemberFilter<Method>() {
@Override
public boolean filter(Method method) {
return Reflects.forMethod().isGetter(method);
}
});
Collections.sort(getters, new Comparator<Method>() {
@Override
public int compare(Method m1, Method m2) {
return m1.getName().compareTo(m2.getName());
}
});
if (getters.isEmpty()) {
// use java toString() since we can't handle it
sb.append(obj.toString());
} else {
boolean first = true;
sb.append('{');
for (Method getter : getters) {
String key = Reflects.forMethod().getGetterName(getter);
Object value;
try {
if (!getter.isAccessible()) {
getter.setAccessible(true);
}
value = getter.invoke(obj);
} catch (Exception e) {
// ignore it
value = null;
}
if (value == null) {
continue;
}
if (first) {
first = false;
} else {
sb.append(',');
if (!m_compact) {
sb.append(' ');
}
}
sb.append('"').append(key).append("\":");
if (!m_compact) {
sb.append(' ');
}
fromObject(done, sb, value);
}
sb.append('}');
}
}
public boolean hasToString(Class<?> type) {
try {
Method method = type.getMethod("toString");
if (method.getDeclaringClass() != Object.class) {
return true;
}
} catch (Exception e) {
// ignore it
}
return false;
}
}
static class LengthLimiter {
private int m_maxLength;
private int m_maxItemLength;
private int m_halfMaxItemLength;
private StringBuilder m_sb;
public LengthLimiter(StringBuilder sb, int maxLength, int maxItemLength) {
m_sb = sb;
m_maxLength = maxLength - 3;
m_maxItemLength = maxItemLength;
m_halfMaxItemLength = maxItemLength / 2 - 1;
}
public LengthLimiter append(char ch) {
m_sb.append(ch);
return this;
}
public LengthLimiter append(Object value) {
append(value, false);
return this;
}
public LengthLimiter append(Object value, boolean itemLimit) {
int len = m_sb.length();
String str = getString(value, itemLimit);
if (m_maxLength > 0 && len + str.length() > m_maxLength) {
throw new RuntimeException("Length limited.");
} else {
m_sb.append(str);
return this;
}
}
private String getString(Object value, boolean itemLimit) {
String str = String.valueOf(value);
if (itemLimit && m_maxItemLength > 0) {
int len = str.length();
if (len > m_maxItemLength) {
return str.substring(0, m_halfMaxItemLength) + "..."
+ str.substring(len - m_halfMaxItemLength, len);
}
}
return str;
}
}
public static class Reflects {
private Reflects() {
}
public static ClassReflector forClass() {
return ClassReflector.INSTANCE;
}
public static ConstructorReflector forConstructor() {
return ConstructorReflector.INSTANCE;
}
public static FieldReflector forField() {
return FieldReflector.INSTANCE;
}
public static MethodReflector forMethod() {
return MethodReflector.INSTANCE;
}
public static ModifierReflector forModifier() {
return ModifierReflector.INSTANCE;
}
public static ResourceReflector forResource() {
return ResourceReflector.INSTANCE;
}
public static enum ClassReflector {
INSTANCE;
/**
* for class name like "a.b.C" or "a.b.C$D$E"
*
* @param className
* @return class from current context class loader
*/
public Class<?> getClass(String className) {
return getClass(className, null);
}
/**
* for class name like "a.b.C" or "a.b.C$D$E"
*
* @param className
* @param classloader
* @return class from current context class loader
*/
public Class<?> getClass(String className, ClassLoader classloader) {
Class<?> clazz = null;
if (classloader != null) {
try {
clazz = classloader.loadClass(className);
} catch (ClassNotFoundException e) {
// ignore it
}
} else {
// step1: try to load from caller class loader
try {
clazz = Class.forName(className);
} catch (ClassNotFoundException e) {
// step2: try to load from thread context class loader
if (clazz == null) {
clazz = getClass(className, Thread.currentThread()
.getContextClassLoader());
}
// step3: try to load from current-class class loader
if (clazz == null) {
clazz = getClass(className,
Reflects.class.getClassLoader());
}
}
}
return clazz;
}
/**
* for class name like "a.b.C" or "a.b.C.D.E"
*
* @param className
* @return class from current context class loader
*/
public Class<?> getClass2(String className) {
return getClass2(className, null);
}
/**
* for class name like "a.b.C" or "a.b.C.D.E"
*
* @param className
* @return class from current context class loader
*/
public Class<?> getClass2(String className, ClassLoader classloader) {
Class<?> clazz = null;
String name = className;
while (true) {
clazz = getClass(name, classloader);
if (clazz != null) {
break;
}
// try with inner class name
int pos = name.lastIndexOf('.');
if (pos < 0) {
break;
}
name = name.substring(0, pos) + '$'
+ name.substring(pos + 1);
}
return clazz;
}
public Class<?> getNestedClass(Class<?> clazz, String simpleName) {
if (clazz != null) {
Class<?>[] subClasses = clazz.getDeclaredClasses();
if (subClasses != null) {
for (Class<?> subClass : subClasses) {
if (subClass.getSimpleName().equals(simpleName)) {
return subClass;
}
}
}
}
return null;
}
public Class<?> getNestedClass(String className, String simpleName) {
return getNestedClass(getClass(className), simpleName);
}
public Class<?> getNestedClass(String className, String simpleName,
ClassLoader classloader) {
return getNestedClass(getClass(className, classloader),
simpleName);
}
}
public static enum ConstructorReflector {
INSTANCE;
public Object createInstance(Class<?> clazz,
Object... typesAndParameters) {
try {
TypeArguments typeArgs = new TypeArguments(
typesAndParameters);
Constructor<?> constructor = clazz.getConstructor(typeArgs
.getTypes());
return constructor.newInstance(typeArgs.getArguments());
} catch (Exception e) {
// ignore it
}
return null;
}
}
public static enum FieldFilter implements IMemberFilter<Field> {
PUBLIC {
public boolean filter(Field field) {
return ModifierReflector.INSTANCE.isPublic(field);
}
},
STATIC {
public boolean filter(Field field) {
return ModifierReflector.INSTANCE.isStatic(field);
}
},
PUBLIC_STATIC {
public boolean filter(Field field) {
return ModifierReflector.INSTANCE.isPublic(field)
&& ModifierReflector.INSTANCE.isStatic(field);
}
};
}
public static enum FieldReflector {
INSTANCE;
public List<Field> getAllDeclaredFields(Class<?> clazz,
IMemberFilter<Field> filter) {
List<Field> list = new ArrayList<Field>();
Class<?> current = clazz;
while (current != null && current != Object.class) {
Field[] fields = current.getDeclaredFields();
for (Field field : fields) {
if (filter == null || filter.filter(field)) {
list.add(field);
}
}
current = current.getSuperclass();
}
return list;
}
public Field getDeclaredField(Class<?> clazz, String fieldName) {
if (clazz != null) {
try {
Field field = clazz.getDeclaredField(fieldName);
return field;
} catch (Exception e) {
// ignore
}
}
return null;
}
public List<Field> getDeclaredFields(Class<?> clazz,
IMemberFilter<Field> filter) {
List<Field> list = new ArrayList<Field>();
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
if (filter == null || filter.filter(field)) {
list.add(field);
}
}
return list;
}
@SuppressWarnings("unchecked")
public <T> T getDeclaredFieldValue(Class<?> clazz,
String fieldName, Object instance) {
Field field = getDeclaredField(clazz, fieldName);
if (field != null) {
try {
if (!field.isAccessible()) {
field.setAccessible(true);
}
return (T) field.get(instance);
} catch (Exception e) {
// ignore
}
}
return null;
}
public boolean setDeclaredFieldValue(Class<?> clazz,
String fieldName, Object instance, Object value) {
Field field = getDeclaredField(clazz, fieldName);
if (field != null) {
try {
if (!field.isAccessible()) {
field.setAccessible(true);
}
field.set(instance, value);
return true;
} catch (Exception e) {
// ignore
}
}
return false;
}
public List<Field> getFields(Class<?> clazz,
IMemberFilter<Field> filter) {
List<Field> list = new ArrayList<Field>();
Field[] fields = clazz.getFields();
for (Field field : fields) {
if (filter == null || filter.filter(field)) {
list.add(field);
}
}
return list;
}
@SuppressWarnings("unchecked")
public <T> T getFieldValue(Object instance, String fieldName) {
if (instance != null) {
try {
Field field = instance.getClass().getField(fieldName);
return (T) field.get(instance);
} catch (Exception e) {
// ignore
}
}
return null;
}
@SuppressWarnings("unchecked")
public <T> T getStaticFieldValue(Class<?> clazz, String fieldName) {
if (clazz != null) {
try {
Field field = clazz.getField(fieldName);
return (T) field.get(null);
} catch (Exception e) {
// ignore
}
}
return null;
}
@SuppressWarnings("unchecked")
public <T> T getStaticFieldValue(String className, String fieldName) {
try {
Class<?> clazz = forClass().getClass(className);
if (clazz != null) {
return (T) getStaticFieldValue(clazz, fieldName);
}
} catch (Exception e) {
// ignore it
}
return null;
}
@SuppressWarnings("unchecked")
public <T> T getDeclaredFieldValue(Object instance,
String... fields) {
Object value = instance;
for (String field : fields) {
value = getDeclaredFieldValue(value.getClass(), field,
value);
if (value == null) {
break;
}
}
return (T) value;
}
}
public static interface IMemberFilter<T extends Member> {
public boolean filter(T member);
}
public static enum MethodFilter implements IMemberFilter<Method> {
PUBLIC {
public boolean filter(Method method) {
return ModifierReflector.INSTANCE.isPublic(method);
}
},
STATIC {
public boolean filter(Method method) {
return ModifierReflector.INSTANCE.isStatic(method);
}
},
PUBLIC_STATIC {
public boolean filter(Method method) {
return ModifierReflector.INSTANCE.isPublic(method)
&& ModifierReflector.INSTANCE.isStatic(method);
}
};
}
public static enum MethodReflector {
INSTANCE;
public Method getDeclaredMethod(Class<?> clazz, String methodName,
Class<?>... parameterTypes) {
try {
return clazz.getDeclaredMethod(methodName, parameterTypes);
} catch (Exception e) {
// ignore it
}
return null;
}
public List<Method> getDeclaredMethods(Class<?> clazz,
IMemberFilter<Method> filter) {
List<Method> list = new ArrayList<Method>();
try {
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
if (filter == null || filter.filter(method)) {
list.add(method);
}
}
} catch (Exception e) {
// ignore it
}
return list;
}
public String getGetMethodName(String propertyName) {
int len = propertyName == null ? 0 : propertyName.length();
if (len == 0) {
throw new IllegalArgumentException(String.format(
"Invalid property name: %s!", propertyName));
}
StringBuilder sb = new StringBuilder(len + 3);
sb.append("get");
sb.append(Character.toUpperCase(propertyName.charAt(0)));
sb.append(propertyName.substring(1));
return sb.toString();
}
public String getGetterName(Method method) {
String name = method.getName();
int length = name.length();
if (length > 3 && name.startsWith("get")) {
return Character.toLowerCase(name.charAt(3))
+ name.substring(4);
} else if (length > 2 && name.startsWith("is")) {
return Character.toLowerCase(name.charAt(2))
+ name.substring(3);
} else {
return name;
}
}
public Method getMethod(Class<?> clazz, String methodName,
Class<?>... parameterTypes) {
try {
return clazz.getMethod(methodName, parameterTypes);
} catch (Exception e) {
// ignore it
}
return null;
}
public List<Method> getMethods(Class<?> clazz,
IMemberFilter<Method> filter) {
List<Method> list = new ArrayList<Method>();
try {
Method[] methods = clazz.getMethods();
for (Method method : methods) {
if (filter == null || filter.filter(method)) {
list.add(method);
}
}
} catch (Exception e) {
// ignore it
}
return list;
}
@SuppressWarnings("unchecked")
public <T> T getPropertyValue(Object instance, String propertyName) {
String methodName = getGetMethodName(propertyName);
Object value = invokeMethod(instance, methodName);
return (T) value;
}
@SuppressWarnings("unchecked")
public <T> T invokeDeclaredMethod(Object instance,
String methodName, Object... typesAndParameters) {
if (instance == null) {
return null;
}
TypeArguments typeArgs = new TypeArguments(typesAndParameters);
Method method = getDeclaredMethod(instance.getClass(),
methodName, typeArgs.getTypes());
if (method != null) {
try {
method.setAccessible(true);
return (T) method.invoke(instance,
typeArgs.getArguments());
} catch (Exception e) {
// ignore it
}
}
return null;
}
@SuppressWarnings("unchecked")
public <T> T invokeMethod(Object instance, String methodName,
Object... typesAndParameters) {
if (instance == null) {
return null;
}
TypeArguments typeArgs = new TypeArguments(typesAndParameters);
Method method = getMethod(instance.getClass(), methodName,
typeArgs.getTypes());
if (method != null) {
try {
return (T) method.invoke(instance,
typeArgs.getArguments());
} catch (Exception e) {
// ignore it
}
}
return null;
}
@SuppressWarnings("unchecked")
public <T> T invokeStaticMethod(Class<?> clazz, String methodName,
Object... typesAndParameters) {
if (clazz == null) {
return null;
}
TypeArguments typeArgs = new TypeArguments(typesAndParameters);
Method method = getMethod(clazz, methodName,
typeArgs.getTypes());
if (method != null) {
try {
return (T) method.invoke(null);
} catch (Exception e) {
// ignore it
}
}
return null;
}
public boolean isGetter(Method method) {
if (method.getParameterTypes().length > 0) {
return false;
}
int modifier = method.getModifiers();
if (!Modifier.isPublic(modifier) || Modifier.isStatic(modifier)) {
return false;
}
String name = method.getName();
if (name.startsWith("get") && !name.equals("getClass")) {
return true;
} else if (name.startsWith("is")
&& method.getReturnType() == Boolean.TYPE) {
return true;
} else {
return false;
}
}
}
public static enum ModifierReflector {
INSTANCE;
public boolean isAbstract(Class<?> clazz) {
return Modifier.isAbstract(clazz.getModifiers());
}
public boolean isAbstract(Member member) {
return Modifier.isAbstract(member.getModifiers());
}
public boolean isPublic(Class<?> clazz) {
return Modifier.isPublic(clazz.getModifiers());
}
public boolean isPublic(Member member) {
return Modifier.isPublic(member.getModifiers());
}
public boolean isStatic(Class<?> clazz) {
return Modifier.isStatic(clazz.getModifiers());
}
public boolean isStatic(Member member) {
return Modifier.isStatic(member.getModifiers());
}
}
public static enum ResourceReflector {
INSTANCE;
public Properties getResource(Class<?> anchorClass, String resName) {
// step1: try to load from current class loader
Properties prop = getResource(getClass().getClassLoader(),
anchorClass, resName);
// step2: try to load from thread context class loader
if (prop == null) {
ClassLoader classloader = Thread.currentThread()
.getContextClassLoader();
prop = getResource(classloader, anchorClass, resName);
}
return prop;
}
public Properties getResource(ClassLoader classloader,
Class<?> anchorClass, String resName) {
resName = getResourceName(anchorClass, resName);
URL url = classloader.getResource(resName);
if (url != null) {
try {
Properties prop = new Properties();
prop.load(url.openStream());
return prop;
} catch (IOException e) {
// ignore it
}
}
return null;
}
public Properties getResource(String resName) {
return getResource(Reflects.class, resName);
}
private String getResourceName(Class<?> clazz, String resName) {
// Turn package name into a directory path
if (resName.length() > 0 && resName.charAt(0) == '/')
return resName.substring(1);
String qualifiedClassName = clazz != null ? clazz.getName()
: getClass().getName();
int classIndex = qualifiedClassName.lastIndexOf('.');
if (classIndex == -1)
return resName; // from a default package
return qualifiedClassName.substring(0, classIndex + 1).replace(
'.', '/')
+ resName;
}
}
static class TypeArguments {
private Class<?>[] m_types;
private Object[] m_arguments;
public TypeArguments(Object... typesAndParameters) {
int length = typesAndParameters.length;
if (length % 2 != 0) {
throw new IllegalArgumentException(String.format(
"Constrcutor argument types and data should be even"
+ ", but was odd: %s.", length));
}
int half = length / 2;
Class<?>[] types = new Class<?>[half];
Object[] arguments = new Object[half];
for (int i = 0; i < half; i++) {
types[i] = (Class<?>) typesAndParameters[2 * i];
arguments[i] = typesAndParameters[2 * i + 1];
}
m_types = types;
m_arguments = arguments;
}
public Object[] getArguments() {
return m_arguments;
}
public Class<?>[] getTypes() {
return m_types;
}
}
}
}