View Javadoc
1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements. See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership. The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License. You may obtain a copy of the License at
9    *
10   * http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied. See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package org.mule.config.spring.util;
21  
22  // import org.apache.axis.utils.Messages;
23  
24  import java.io.IOException;
25  import java.lang.reflect.Constructor;
26  import java.lang.reflect.Member;
27  import java.lang.reflect.Method;
28  import java.lang.reflect.Modifier;
29  import java.lang.reflect.Proxy;
30  import java.util.HashMap;
31  import java.util.Map;
32  
33  /**
34   * This is the class file reader for obtaining the parameter names for declared
35   * methods in a class. The class must have debugging attributes for us to obtain this
36   * information.
37   * <p>
38   * This does not work for inherited methods. To obtain parameter names for inherited
39   * methods, you must use a paramReader for the class that originally declared the
40   * method.
41   * <p>
42   * don't get tricky, it's the bare minimum. Instances of this class are not
43   * threadsafe -- don't share them.
44   * <p>
45   * 
46   * @author Edwin Smith, Macromedia
47   */
48  public class ParamReader extends ClassReader
49  {
50      private String methodName;
51      private Map<String, MethodInfo> methods = new HashMap<String, MethodInfo>();
52      private Class[] paramTypes;
53  
54      /**
55       * process a class file, given it's class. We'll use the defining classloader to
56       * locate the bytecode.
57       * 
58       * @param c
59       * @throws IOException
60       */
61      public ParamReader(Class c) throws IOException
62      {
63          this(getBytes(c));
64      }
65  
66      /**
67       * process the given class bytes directly.
68       * 
69       * @param b
70       * @throws IOException
71       */
72      public ParamReader(byte[] b) throws IOException
73      {
74          super(b, findAttributeReaders(ParamReader.class));
75  
76          // check the magic number
77          if (readInt() != 0xCAFEBABE)
78          {
79              // not a class file!
80              throw new IOException();
81          }
82  
83          readShort(); // minor version
84          readShort(); // major version
85  
86          readCpool(); // slurp in the constant pool
87  
88          readShort(); // access flags
89          readShort(); // this class name
90          readShort(); // super class name
91  
92          int count = readShort(); // ifaces count
93          for (int i = 0; i < count; i++)
94          {
95              readShort(); // interface index
96          }
97  
98          count = readShort(); // fields count
99          for (int i = 0; i < count; i++)
100         {
101             readShort(); // access flags
102             readShort(); // name index
103             readShort(); // descriptor index
104             skipAttributes(); // field attributes
105         }
106 
107         count = readShort(); // methods count
108         for (int i = 0; i < count; i++)
109         {
110             readShort(); // access flags
111             int m = readShort(); // name index
112             String name = resolveUtf8(m);
113             int d = readShort(); // descriptor index
114             this.methodName = name + resolveUtf8(d);
115             readAttributes(); // method attributes
116         }
117 
118     }
119 
120     /**
121      * Retrieve a list of function parameter names from a method Returns null if
122      * unable to read parameter names (i.e. bytecode not built with debug).
123      */
124     public static String[] getParameterNamesFromDebugInfo(Method method)
125     {
126         // Don't worry about it if there are no params.
127         int numParams = method.getParameterTypes().length;
128         if (numParams == 0)
129         {
130             return null;
131         }
132 
133         // get declaring class
134         Class c = method.getDeclaringClass();
135 
136         // Don't worry about it if the class is a Java dynamic proxy
137         if (Proxy.isProxyClass(c))
138         {
139             return null;
140         }
141 
142         try
143         {
144             // get a parameter reader
145             ParamReader pr = new ParamReader(c);
146             // get the parameter names
147             return pr.getParameterNames(method);
148         }
149         catch (IOException e)
150         {
151             // log it and leave
152             // log.info(Messages.getMessage("error00") + ":" + e);
153             return null;
154         }
155     }
156 
157     public void readCode() throws IOException
158     {
159         readShort(); // max stack
160         int maxLocals = readShort(); // max locals
161 
162         MethodInfo info = new MethodInfo(maxLocals);
163         if (methods != null && methodName != null)
164         {
165             methods.put(methodName, info);
166         }
167 
168         skipFully(readInt()); // code
169         skipFully(8 * readShort()); // exception table
170         // read the code attributes (recursive). This is where
171         // we will find the LocalVariableTable attribute.
172         readAttributes();
173     }
174 
175     /**
176      * return the names of the declared parameters for the given constructor. If we
177      * cannot determine the names, return null. The returned array will have one name
178      * per parameter. The length of the array will be the same as the length of the
179      * Class[] array returned by Constructor.getParameterTypes().
180      * 
181      * @param ctor
182      * @return String[] array of names, one per parameter, or null
183      */
184     public String[] getParameterNames(Constructor ctor)
185     {
186         paramTypes = ctor.getParameterTypes();
187         return getParameterNames(ctor, paramTypes);
188     }
189 
190     /**
191      * return the names of the declared parameters for the given method. If we cannot
192      * determine the names, return null. The returned array will have one name per
193      * parameter. The length of the array will be the same as the length of the
194      * Class[] array returned by Method.getParameterTypes().
195      * 
196      * @param method
197      * @return String[] array of names, one per parameter, or null
198      */
199     public String[] getParameterNames(Method method)
200     {
201         paramTypes = method.getParameterTypes();
202         return getParameterNames(method, paramTypes);
203     }
204 
205     protected String[] getParameterNames(Member member, Class[] pTypes)
206     {
207         // look up the names for this method
208         MethodInfo info = (MethodInfo) methods.get(getSignature(member, pTypes));
209 
210         // we know all the local variable names, but we only need to return
211         // the names of the parameters.
212 
213         if (info != null)
214         {
215             String[] paramNames = new String[pTypes.length];
216             int j = Modifier.isStatic(member.getModifiers()) ? 0 : 1;
217 
218             boolean found = false; // did we find any non-null names
219             for (int i = 0; i < paramNames.length; i++)
220             {
221                 if (info.names[j] != null)
222                 {
223                     found = true;
224                     paramNames[i] = info.names[j];
225                 }
226                 j++;
227                 if (pTypes[i] == double.class || pTypes[i] == long.class)
228                 {
229                     // skip a slot for 64bit params
230                     j++;
231                 }
232             }
233 
234             if (found)
235             {
236                 return paramNames;
237             }
238             else
239             {
240                 return null;
241             }
242         }
243         else
244         {
245             return null;
246         }
247     }
248 
249     private static class MethodInfo
250     {
251         String[] names;
252 
253         public MethodInfo(int maxLocals)
254         {
255             names = new String[maxLocals];
256         }
257     }
258 
259     private MethodInfo getMethodInfo()
260     {
261         MethodInfo info = null;
262         if (methods != null && methodName != null)
263         {
264             info = (MethodInfo) methods.get(methodName);
265         }
266         return info;
267     }
268 
269     /**
270      * this is invoked when a LocalVariableTable attribute is encountered.
271      * 
272      * @throws IOException
273      */
274     public void readLocalVariableTable() throws IOException
275     {
276         int len = readShort(); // table length
277         MethodInfo info = getMethodInfo();
278         for (int j = 0; j < len; j++)
279         {
280             readShort(); // start pc
281             readShort(); // length
282             int nameIndex = readShort(); // name_index
283             readShort(); // descriptor_index
284             int index = readShort(); // local index
285             if (info != null)
286             {
287                 info.names[index] = resolveUtf8(nameIndex);
288             }
289         }
290     }
291 }