package net.ion.craken.expression;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
public abstract class ValueObject {
private volatile List<Object> fieldValues = null;
private List<Object> valueList() {
if (fieldValues == null) {
fieldValues = Collections.unmodifiableList(toValueList(this, getValueFields(getClass())));
}
return fieldValues;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
return valueList().equals(((ValueObject) obj).valueList());
}
@Override
public int hashCode() {
return valueList().hashCode();
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder(getClass().getSimpleName());
buf.append(" {");
Field[] fields = getValueFields(getClass());
int i = 0;
for (Object value : valueList()) {
if (i > 0) {
buf.append(", ");
}
buf.append(fields[i++].getName()).append('=').append(value);
}
buf.append('}');
return buf.toString();
}
private static final Comparator<Field> NAME_ORDER = new Comparator<Field>() {
public int compare(Field field1, Field field2) {
return field1.getName().compareTo(field2.getName());
}
};
private static final ConcurrentMap<Class<?>, Field[]> valueFieldMap = new ConcurrentHashMap<Class<?>, Field[]>();
private static Field[] getValueFields(Class<?> type) {
Field[] fields = valueFieldMap.get(type);
if (fields == null) {
fields = introspectValueFields(type);
valueFieldMap.put(type, fields);
}
return fields;
}
private static List<Object> toValueList(Object obj, Field[] fields) {
List<Object> list = new ArrayList<Object>();
for (Field field : fields) {
try {
list.add(field.get(obj));
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
return list;
}
private static Field[] introspectValueFields(Class<?> type) {
if (type == ValueObject.class) {
return NO_FIELD;
}
List<Field> fieldList = new ArrayList<Field>();
fieldList.addAll(Arrays.asList(introspectValueFields(type.getSuperclass())));
List<Field> myFields = tail(fieldList);
for (Field field : type.getDeclaredFields()) {
if (!Modifier.isStatic(field.getModifiers()) && Modifier.isFinal(field.getModifiers())) {
myFields.add(field);
}
}
Collections.sort(myFields, NAME_ORDER);
Field[] fields = fieldList.toArray(new Field[fieldList.size()]);
AccessibleObject.setAccessible(fields, true);
return fields;
}
private static <T> List<T> tail(List<T> list) {
int size = list.size();
return list.subList(size, size);
}
private static final Field[] NO_FIELD = new Field[0];
}