001package org.jadira.reflection.access; 002 003import java.lang.reflect.Field; 004import java.lang.reflect.Method; 005import java.util.Arrays; 006 007import org.jadira.reflection.access.api.ClassAccess; 008import org.jadira.reflection.access.api.FieldAccess; 009import org.jadira.reflection.access.api.MethodAccess; 010import org.jadira.reflection.access.model.ClassModel; 011import org.jadira.reflection.core.misc.ClassUtils; 012 013public abstract class AbstractClassAccess<C> implements ClassAccess<C> { 014 015 /** 016 * The class to be accessed 017 */ 018 private Class<C> clazz; 019 020 /** 021 * An ordered array giving the names of the fields in the class to accessed 022 */ 023 protected final String[] fieldNames; 024 025 /** 026 * An ordered array giving the Fields in the class to accessed 027 */ 028 private final Field[] fields; 029 030 private final FieldAccess<C>[] fieldAccess; 031 032 /** 033 * An ordered array giving the names of the methods in the class to accessed 034 */ 035 private final String[] methodNames; 036 037 /** 038 * An ordered array giving the Methods in the class to accessed 039 */ 040 private final Method[] methods; 041 042 private final MethodAccess<C>[] methodAccess; 043 044 private final ClassModel<C> classModel; 045 046 private final ClassAccess<? super C> superClassAccess; 047 048 private final boolean providesHashCode; 049 private final boolean providesEquals; 050 051 /** 052 * Constructor, intended for use by generated subclasses 053 * @param clazz The Class to be accessed 054 */ 055 @SuppressWarnings("unchecked") 056 protected AbstractClassAccess(Class<C> clazz) { 057 058 this.clazz = clazz; 059 060 Field[] myFields = ClassUtils.collectDeclaredInstanceFields(clazz); 061 062 String[] unsortedFieldNames = new String[myFields.length]; 063 for (int i = 0; i < unsortedFieldNames.length; i++) { 064 unsortedFieldNames[i] = myFields[i].getName(); 065 } 066 fieldNames = Arrays.copyOf(unsortedFieldNames, unsortedFieldNames.length); 067 Arrays.sort(fieldNames); 068 069 final FieldAccess<C>[] myFieldAccess = (FieldAccess<C>[]) new FieldAccess[myFields.length]; 070 fields = new Field[myFields.length]; 071 072 for (int i = 0; i < fields.length; i++) { 073 074 String fieldName = unsortedFieldNames[i]; 075 for (int tIdx = 0; tIdx < unsortedFieldNames.length; tIdx++) { 076 if (fieldName.equals(fieldNames[tIdx])) { 077 myFieldAccess[tIdx] = constructFieldAccess(myFields[i]); 078 fields[tIdx] = myFields[i]; 079 break; 080 } 081 } 082 083 } 084 fieldAccess = myFieldAccess; 085 086 Method[] myMethods = ClassUtils.collectDeclaredMethods(clazz); 087 088 String[] unsortedMethodNames = new String[myMethods.length]; 089 for (int i=0; i < unsortedMethodNames.length; i++) { 090 unsortedMethodNames[i] = myMethods[i].getName(); 091 } 092 methodNames = Arrays.copyOf(unsortedMethodNames, unsortedMethodNames.length); 093 Arrays.sort(methodNames); 094 095 final MethodAccess<C>[] myMethodAccess = (MethodAccess<C>[])new MethodAccess[myMethods.length]; 096 methods = new Method[myMethods.length]; 097 098 for (int i=0; i < methods.length; i++) { 099 100 String methodName = unsortedMethodNames[i]; 101 for (int tIdx = 0; tIdx < unsortedMethodNames.length; tIdx++) { 102 if (methodName.equals(methodNames[tIdx])) { 103 myMethodAccess[tIdx] = constructMethodAccess(myMethods[i]); 104 methods[tIdx] = myMethods[i]; 105 break; 106 } 107 } 108 } 109 methodAccess = myMethodAccess; 110 111 final Class<?> superClass = clazz.getSuperclass(); 112 if (superClass != null) { 113 this.superClassAccess = constructClassAccess(clazz.getSuperclass()); 114 } else { 115 this.superClassAccess = null; 116 } 117 118 this.classModel = ClassModel.get(this); 119 120 Class<?> hashcodeClass; 121 try { 122 hashcodeClass = clazz.getMethod("hashCode").getDeclaringClass(); 123 } catch (NoSuchMethodException e) { 124 throw new IllegalStateException("hashCode() method could not be found: " + e.getMessage(), e); 125 } catch (SecurityException e) { 126 throw new IllegalStateException("hashCode() method could not be accessed: " + e.getMessage(), e); 127 } 128 129 providesHashCode = hashcodeClass.equals(clazz); 130 131 Class<?> equalsClass; 132 try { 133 equalsClass = clazz.getMethod("equals", Object.class).getDeclaringClass(); 134 } catch (NoSuchMethodException e) { 135 throw new IllegalStateException("equals() method could not be found: " + e.getMessage(), e); 136 } catch (SecurityException e) { 137 throw new IllegalStateException("equals() method could not be accessed: " + e.getMessage(), e); 138 } 139 140 providesEquals = equalsClass.equals(clazz); 141 } 142 143 @Override 144 public Class<C> getType() { 145 return clazz; 146 } 147 148 @Override 149 public FieldAccess<C>[] getDeclaredFieldAccessors() { 150 return fieldAccess; 151 } 152 153 @Override 154 public FieldAccess<C> getDeclaredFieldAccess(Field f) { 155 int idx = Arrays.binarySearch(fieldNames, f.getName()); 156 return fieldAccess[idx]; 157 } 158 159 160 @Override 161 public MethodAccess<C>[] getDeclaredMethodAccessors() { 162 return methodAccess; 163 } 164 165 @Override 166 public MethodAccess<C> getDeclaredMethodAccess(Method m) { 167 int idx = Arrays.binarySearch(methodNames, m.getName()); 168 if (methodAccess[idx].method().equals(m)) { 169 return methodAccess[idx]; 170 } 171 int backTrack = idx; 172 while (true) { 173 if (methodAccess[backTrack].method().equals(m)) { 174 return methodAccess[idx]; 175 } 176 if (!(methodAccess[backTrack].method().getName() 177 .equals(m.getName()))) { 178 break; 179 } 180 backTrack = backTrack - 1; 181 } 182 while (true) { 183 idx = idx + 1; 184 if (methodAccess[idx].method().equals(m)) { 185 return methodAccess[idx]; 186 } 187 if (!(methodAccess[idx].method().getName().equals(m.getName()))) { 188 break; 189 } 190 } 191 return null; 192 } 193 194 @Override 195 public ClassModel<C> getClassModel() { 196 return classModel; 197 } 198 199 protected String[] getDeclaredFieldNames() { 200 return fieldNames; 201 } 202 203 protected Field[] getDeclaredFields() { 204 return fields; 205 } 206 207 protected String[] getDeclaredMethodNames() { 208 return methodNames; 209 } 210 211 protected Method[] getDeclaredMethods() { 212 return methods; 213 } 214 215 @Override 216 public ClassAccess<? super C> getSuperClassAccess() { 217 return superClassAccess; 218 } 219 220 221 @Override 222 public boolean providesHashCode() { 223 return providesHashCode; 224 } 225 226 @Override 227 public boolean providesEquals() { 228 return providesEquals; 229 } 230 231 protected abstract FieldAccess<C> constructFieldAccess(Field field); 232 233 protected abstract MethodAccess<C> constructMethodAccess(Method method); 234 235 protected abstract <X> ClassAccess<X> constructClassAccess(Class<X> clazz); 236}