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}