View Javadoc

1   /*
2    * Copyright 2010 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.callbackparams.internal;
18  
19  import java.lang.reflect.Method;
20  import java.lang.reflect.Type;
21  import java.util.ArrayList;
22  import java.util.Arrays;
23  import java.util.Collections;
24  import java.util.HashMap;
25  import java.util.HashSet;
26  import java.util.Iterator;
27  import java.util.List;
28  import java.util.Map;
29  import java.util.Set;
30  
31  /**
32   * Convenience class for generating method-names for new no-arg methods that
33   * are created by {@link CallbackMethodProxyingRebyter}.
34   *
35   * @author Henrik Kaipe
36   */
37  class NoargMethodProxyMap {
38  
39      private final Set/*String*/ originalMethodNames = new HashSet();
40      private final Map/*String,StingBuffer*/ methodNameBuilders = new HashMap();
41      private final Map/*String,List<String>*/ noArgMethodNames = new HashMap();
42      private final Map/*String,List<Method>*/ proxiedMethods = new HashMap();
43      private final Set/*String*/ declaringClassNames = new HashSet();
44  
45      /**
46       * @param template original no-arg method names are collected from this class
47       */
48      NoargMethodProxyMap(Class template) {
49          for (; Object.class != template ; template = template.getSuperclass()) {
50              final Method[] declaredMethods = template.getDeclaredMethods();
51              for (int i = 0 ; i < declaredMethods.length ; ++i) {
52                  final Method m = declaredMethods[i];
53                  if (0 == m.getParameterTypes().length) {
54                      originalMethodNames.add(m.getName());
55                  }
56              }
57          }
58      }
59  
60      private String newNoargMethodName(String callbackMethodName) {
61          StringBuffer sb = (StringBuffer)
62                  methodNameBuilders.get(callbackMethodName);
63          if (null == sb) {
64              String suffixBase = "$callbackProxy";
65              sb = new StringBuffer(
66                      callbackMethodName.length() + suffixBase.length() + 4)
67                      .append(callbackMethodName)
68                      .append(suffixBase);
69              methodNameBuilders.put(callbackMethodName, sb);
70              String candidate = sb.toString();
71              sb.append("_1");
72              if (false == originalMethodNames.contains(candidate)) {
73                  return candidate;
74              }
75          }
76          String candidate;
77          do {
78              candidate = increase(sb);
79          } while (originalMethodNames.contains(candidate));
80          return candidate;
81      }
82  
83      private String increase(StringBuffer sb) {
84          for (int i = sb.length() - 1 ; true ; --i) {
85              char loopChar = sb.charAt(i);
86              switch (loopChar) {
87                  case '9':
88                      sb.setCharAt(i, '0');
89                      continue;
90                  case '_':
91                      return sb.insert(++i, '1').toString();
92                  default:
93                      sb.setCharAt(i, ++loopChar);
94                      return sb.toString();
95              }
96          }
97      }
98  
99      /**
100      * @param m method to be proxied
101      * @return the name of the no-arg proxy-method
102      */
103     String addMethodToBeProxied(Method m) {
104         declaringClassNames.add(m.getDeclaringClass().getName());
105 
106         String noargProxyMethodName = findProxyNameOfOverriddenMethod(m);
107         if (null == noargProxyMethodName) {
108             noargProxyMethodName = newNoargMethodName(m.getName());
109             ((List)noArgMethodNames.get(m.getName())).add(noargProxyMethodName);
110             proxiedMethods.put(noargProxyMethodName, new ArrayList(4));
111         }
112         ((List)proxiedMethods.get(noargProxyMethodName)).add(m);
113         return noargProxyMethodName;
114     }
115 
116     private String findProxyNameOfOverriddenMethod(Method m) {
117         List/*String*/ existingProxyNames =
118                 (List) noArgMethodNames.get(m.getName());
119         if (null == existingProxyNames) {
120             noArgMethodNames.put(m.getName(), new ArrayList());
121             return null;
122 
123         } else {
124             final Type[] parameterTypes = m.getGenericParameterTypes();
125             for (Iterator iterNames = existingProxyNames.iterator()
126                     ; iterNames.hasNext() ;) {
127                 final String eachName = (String) iterNames.next();
128                 for (Iterator iterOo = ((List)proxiedMethods.get(eachName))
129                         .iterator() ; iterOo.hasNext() ;) {
130                     final Method oo = (Method) iterOo.next();
131                     if (oo.getDeclaringClass()
132                             .isAssignableFrom(m.getDeclaringClass())
133                             || m.getDeclaringClass().isAssignableFrom(
134                             oo.getDeclaringClass())) {
135                         if (Arrays.equals(parameterTypes,
136                                 oo.getGenericParameterTypes())){
137                             return eachName;
138                         }
139                     }
140                 }
141             }
142             return null;
143         }
144     }
145 
146     Map/*String,Method*/ getProxyMethodsForClass(String className) {
147         if (false == declaringClassNames.contains(className)) {
148             return Collections.EMPTY_MAP;
149         }
150 
151         Map/*String,Method*/ methodsForClass = new HashMap();
152         for (Iterator iterEntr = proxiedMethods.entrySet().iterator()
153                 ; iterEntr.hasNext() ;) {
154             final Map.Entry/*String,List<Method>*/ eachProxyEntry =
155                     (Map.Entry) iterEntr.next();
156             Iterator iterProxiedMethods =
157                     ((List)eachProxyEntry.getValue()).iterator();
158             while (iterProxiedMethods.hasNext()) {
159                 final Method proxiedMethod = (Method) iterProxiedMethods.next();
160                 if (className.equals(proxiedMethod.getDeclaringClass().getName())) {
161                     methodsForClass.put(eachProxyEntry.getKey(), proxiedMethod);
162                 }
163             }
164         }
165         return methodsForClass;
166     }
167 
168     String getNameOfProxiedMethod(String proxyMethodName) {
169         List/*Method*/ methods = (List) proxiedMethods.get(proxyMethodName);
170         return null == methods || methods.isEmpty() ? null
171                 : ((Method)methods.get(0)).getName();
172     }
173 }