Objects initialized through property paths are now deproxied
[cdmlib.git] / cdmlib-persistence / src / main / java / eu / etaxonomy / cdm / persistence / dao / MethodCacheImpl.java
1 // $Id$
2 /**
3 * Copyright (C) 2007 EDIT
4 * European Distributed Institute of Taxonomy
5 * http://www.e-taxonomy.eu
6 *
7 * The contents of this file are subject to the Mozilla Public License Version 1.1
8 * See LICENSE.TXT at the top of this package for the full license terms.
9 */
10
11 package eu.etaxonomy.cdm.persistence.dao;
12
13 import java.lang.reflect.Method;
14 import java.util.ArrayList;
15 import java.util.Arrays;
16 import java.util.HashMap;
17 import java.util.List;
18 import java.util.Map;
19
20 import org.apache.log4j.Logger;
21 import org.springframework.stereotype.Component;
22
23 /**
24 * @author n.hoffmann
25 * @created Mar 11, 2010
26 * @version 1.0
27 */
28 @Component
29 public class MethodCacheImpl implements IMethodCache {
30
31 // MethodUtils
32
33 private Map<MethodDescriptor, Method> methodMap = new HashMap<MethodDescriptor, Method>();
34
35 /*
36 * (non-Javadoc)
37 * @see eu.etaxonomy.cdm.persistence.dao.IMethodCache#getMethod(java.lang.Class, java.lang.String, java.lang.Class)
38 */
39 public Method getMethod(Class clazz, String methodName, Class parameterType) {
40 MethodDescriptor methodDescriptor = new MethodDescriptor(clazz, methodName, new Class[]{parameterType});
41
42 if(methodMap.containsKey(methodDescriptor)){
43 return methodMap.get(methodDescriptor);
44 }
45
46 Method method = getMethodInternal(clazz, methodName, parameterType);
47 if(method != null){
48 method.setAccessible(true);
49 }
50 // we also put null methods into the map to benefit from caching
51 put(methodDescriptor, method);
52
53 return method;
54 }
55
56 /**
57 * Checks class hierarchy of the given class for a method that fits to the given name and parameter type
58 *
59 * @param clazz
60 * @param methodName
61 * @param parameterType
62 * @return
63 */
64 private Method getMethodInternal(Class clazz, String methodName,
65 Class parameterType){
66 // stop recursing when there are no more superclasses
67 if(clazz == null){
68 return null;
69 }
70
71 Method method = null;
72
73 for(Class includedType : getIncludedTypes(parameterType, new ArrayList<Class>())){
74 try {
75 method = clazz.getDeclaredMethod(methodName, includedType);
76 }catch (NoSuchMethodException e) {
77 ;
78 }
79 }
80
81 // if we have a method return it
82 if(method != null){
83 return method;
84 }
85
86 // recurse into superclass if no method was found
87 return getMethodInternal(clazz.getSuperclass(), methodName, parameterType);
88 }
89
90 /**
91 * Create a list containing the type and all supertypes of a given type
92 *
93 * @param clazz
94 * @param classList
95 * @return
96 */
97 private List<Class> getIncludedTypes(Class clazz, List<Class> classList){
98 if(clazz == null){
99 return classList;
100 }
101 classList.add(clazz);
102 Class[] interfaces = clazz.getInterfaces();
103 if(interfaces != null){
104 classList.addAll(Arrays.asList(interfaces));
105 }
106 return getIncludedTypes(clazz.getSuperclass(), classList);
107 }
108
109 /**
110 * Fill the cache
111 *
112 * @param methodDescriptor
113 * @param method
114 */
115 private void put(MethodDescriptor methodDescriptor, Method method) {
116 methodMap.put(methodDescriptor, method);
117 }
118
119 /**
120 *
121 * @author n.hoffmann
122 * @created Mar 11, 2010
123 * @version 1.0
124 */
125 private static class MethodDescriptor{
126 private static final Logger logger = Logger
127 .getLogger(MethodDescriptor.class);
128
129 /** An empty class array */
130 private static final Class[] emptyClassArray = new Class[0];
131
132 private Class clazz;
133 private String methodName;
134 private Class[] parameterTypes;
135 private int hashCode;
136
137 /**
138 * The sole constructor.
139 *
140 * @param clazz the class to reflect, must not be null
141 * @param methodName the method name to obtain
142 * @param paramTypes the array of classes representing the paramater types
143 * @param exact whether the match has to be exact.
144 */
145 public MethodDescriptor(Class clazz, String methodName, Class[] paramTypes) {
146 if (clazz == null) {
147 throw new IllegalArgumentException("Class cannot be null");
148 }
149 if (methodName == null) {
150 throw new IllegalArgumentException("Method Name cannot be null");
151 }
152 if (paramTypes == null) {
153 paramTypes = emptyClassArray;
154 }
155
156 this.clazz = clazz;
157 this.methodName = methodName;
158 this.parameterTypes = paramTypes;
159
160 this.hashCode = methodName.length();
161 }
162 /**
163 * Checks for equality.
164 * @param object object to be tested for equality
165 * @return true, if the object describes the same Method.
166 */
167 public boolean equals(Object object) {
168 if (!(object instanceof MethodDescriptor)) {
169 return false;
170 }
171 MethodDescriptor methodDescriptor = (MethodDescriptor)object;
172
173 return (
174 methodName.equals(methodDescriptor.methodName) &&
175 clazz.equals(methodDescriptor.clazz) &&
176 java.util.Arrays.equals(parameterTypes, methodDescriptor.parameterTypes)
177 );
178 }
179 /**
180 * Returns the string length of method name. I.e. if the
181 * hashcodes are different, the objects are different. If the
182 * hashcodes are the same, need to use the equals method to
183 * determine equality.
184 * @return the string length of method name.
185 */
186 public int hashCode() {
187 return hashCode;
188 }
189 }
190 }