package org.jadira.reflection.access;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import org.jadira.reflection.access.api.ClassAccess;
import org.jadira.reflection.access.api.FieldAccess;
import org.jadira.reflection.access.api.MethodAccess;
import org.jadira.reflection.access.model.ClassModel;
import org.jadira.reflection.core.misc.ClassUtils;
public abstract class AbstractClassAccess<C> implements ClassAccess<C> {
/**
* The class to be accessed
*/
private Class<C> clazz;
/**
* An ordered array giving the names of the fields in the class to accessed
*/
protected final String[] fieldNames;
/**
* An ordered array giving the Fields in the class to accessed
*/
private final Field[] fields;
private final FieldAccess<C>[] fieldAccess;
/**
* An ordered array giving the names of the methods in the class to accessed
*/
private final String[] methodNames;
/**
* An ordered array giving the Methods in the class to accessed
*/
private final Method[] methods;
private final MethodAccess<C>[] methodAccess;
private final ClassModel<C> classModel;
private final ClassAccess<? super C> superClassAccess;
private final boolean providesHashCode;
private final boolean providesEquals;
/**
* Constructor, intended for use by generated subclasses
* @param clazz The Class to be accessed
*/
@SuppressWarnings("unchecked")
protected AbstractClassAccess(Class<C> clazz) {
this.clazz = clazz;
Field[] myFields = ClassUtils.collectDeclaredInstanceFields(clazz);
String[] unsortedFieldNames = new String[myFields.length];
for (int i = 0; i < unsortedFieldNames.length; i++) {
unsortedFieldNames[i] = myFields[i].getName();
}
fieldNames = Arrays.copyOf(unsortedFieldNames, unsortedFieldNames.length);
Arrays.sort(fieldNames);
final FieldAccess<C>[] myFieldAccess = (FieldAccess<C>[]) new FieldAccess[myFields.length];
fields = new Field[myFields.length];
for (int i = 0; i < fields.length; i++) {
String fieldName = unsortedFieldNames[i];
for (int tIdx = 0; tIdx < unsortedFieldNames.length; tIdx++) {
if (fieldName.equals(fieldNames[tIdx])) {
myFieldAccess[tIdx] = constructFieldAccess(myFields[i]);
fields[tIdx] = myFields[i];
break;
}
}
}
fieldAccess = myFieldAccess;
Method[] myMethods = ClassUtils.collectDeclaredMethods(clazz);
String[] unsortedMethodNames = new String[myMethods.length];
for (int i=0; i < unsortedMethodNames.length; i++) {
unsortedMethodNames[i] = myMethods[i].getName();
}
methodNames = Arrays.copyOf(unsortedMethodNames, unsortedMethodNames.length);
Arrays.sort(methodNames);
final MethodAccess<C>[] myMethodAccess = (MethodAccess<C>[])new MethodAccess[myMethods.length];
methods = new Method[myMethods.length];
for (int i=0; i < methods.length; i++) {
String methodName = unsortedMethodNames[i];
for (int tIdx = 0; tIdx < unsortedMethodNames.length; tIdx++) {
if (methodName.equals(methodNames[tIdx])) {
myMethodAccess[tIdx] = constructMethodAccess(myMethods[i]);
methods[tIdx] = myMethods[i];
break;
}
}
}
methodAccess = myMethodAccess;
final Class<?> superClass = clazz.getSuperclass();
if (superClass != null) {
this.superClassAccess = constructClassAccess(clazz.getSuperclass());
} else {
this.superClassAccess = null;
}
this.classModel = ClassModel.get(this);
Class<?> hashcodeClass;
try {
hashcodeClass = clazz.getMethod("hashCode").getDeclaringClass();
} catch (NoSuchMethodException e) {
throw new IllegalStateException("hashCode() method could not be found: " + e.getMessage(), e);
} catch (SecurityException e) {
throw new IllegalStateException("hashCode() method could not be accessed: " + e.getMessage(), e);
}
providesHashCode = hashcodeClass.equals(clazz);
Class<?> equalsClass;
try {
equalsClass = clazz.getMethod("equals", Object.class).getDeclaringClass();
} catch (NoSuchMethodException e) {
throw new IllegalStateException("equals() method could not be found: " + e.getMessage(), e);
} catch (SecurityException e) {
throw new IllegalStateException("equals() method could not be accessed: " + e.getMessage(), e);
}
providesEquals = equalsClass.equals(clazz);
}
@Override
public Class<C> getType() {
return clazz;
}
@Override
public FieldAccess<C>[] getDeclaredFieldAccessors() {
return fieldAccess;
}
@Override
public FieldAccess<C> getDeclaredFieldAccess(Field f) {
int idx = Arrays.binarySearch(fieldNames, f.getName());
return fieldAccess[idx];
}
@Override
public MethodAccess<C>[] getDeclaredMethodAccessors() {
return methodAccess;
}
@Override
public MethodAccess<C> getDeclaredMethodAccess(Method m) {
int idx = Arrays.binarySearch(methodNames, m.getName());
if (methodAccess[idx].method().equals(m)) {
return methodAccess[idx];
}
int backTrack = idx;
while (true) {
if (methodAccess[backTrack].method().equals(m)) {
return methodAccess[idx];
}
if (!(methodAccess[backTrack].method().getName()
.equals(m.getName()))) {
break;
}
backTrack = backTrack - 1;
}
while (true) {
idx = idx + 1;
if (methodAccess[idx].method().equals(m)) {
return methodAccess[idx];
}
if (!(methodAccess[idx].method().getName().equals(m.getName()))) {
break;
}
}
return null;
}
@Override
public ClassModel<C> getClassModel() {
return classModel;
}
protected String[] getDeclaredFieldNames() {
return fieldNames;
}
protected Field[] getDeclaredFields() {
return fields;
}
protected String[] getDeclaredMethodNames() {
return methodNames;
}
protected Method[] getDeclaredMethods() {
return methods;
}
@Override
public ClassAccess<? super C> getSuperClassAccess() {
return superClassAccess;
}
@Override
public boolean providesHashCode() {
return providesHashCode;
}
@Override
public boolean providesEquals() {
return providesEquals;
}
protected abstract FieldAccess<C> constructFieldAccess(Field field);
protected abstract MethodAccess<C> constructMethodAccess(Method method);
protected abstract <X> ClassAccess<X> constructClassAccess(Class<X> clazz);
}