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