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     @Override
158     public void readCode() throws IOException
159     {
160         readShort(); // max stack
161         int maxLocals = readShort(); // max locals
162 
163         MethodInfo info = new MethodInfo(maxLocals);
164         if (methods != null && methodName != null)
165         {
166             methods.put(methodName, info);
167         }
168 
169         skipFully(readInt()); // code
170         skipFully(8 * readShort()); // exception table
171         // read the code attributes (recursive). This is where
172         // we will find the LocalVariableTable attribute.
173         readAttributes();
174     }
175 
176     /**
177      * return the names of the declared parameters for the given constructor. If we
178      * cannot determine the names, return null. The returned array will have one name
179      * per parameter. The length of the array will be the same as the length of the
180      * Class[] array returned by Constructor.getParameterTypes().
181      * 
182      * @param ctor
183      * @return String[] array of names, one per parameter, or null
184      */
185     public String[] getParameterNames(Constructor ctor)
186     {
187         paramTypes = ctor.getParameterTypes();
188         return getParameterNames(ctor, paramTypes);
189     }
190 
191     /**
192      * return the names of the declared parameters for the given method. If we cannot
193      * determine the names, return null. The returned array will have one name per
194      * parameter. The length of the array will be the same as the length of the
195      * Class[] array returned by Method.getParameterTypes().
196      * 
197      * @param method
198      * @return String[] array of names, one per parameter, or null
199      */
200     public String[] getParameterNames(Method method)
201     {
202         paramTypes = method.getParameterTypes();
203         return getParameterNames(method, paramTypes);
204     }
205 
206     protected String[] getParameterNames(Member member, Class[] pTypes)
207     {
208         // look up the names for this method
209         MethodInfo info = methods.get(getSignature(member, pTypes));
210 
211         // we know all the local variable names, but we only need to return
212         // the names of the parameters.
213 
214         if (info != null)
215         {
216             String[] paramNames = new String[pTypes.length];
217             int j = Modifier.isStatic(member.getModifiers()) ? 0 : 1;
218 
219             boolean found = false; // did we find any non-null names
220             for (int i = 0; i < paramNames.length; i++)
221             {
222                 if (info.names[j] != null)
223                 {
224                     found = true;
225                     paramNames[i] = info.names[j];
226                 }
227                 j++;
228                 if (pTypes[i] == double.class || pTypes[i] == long.class)
229                 {
230                     // skip a slot for 64bit params
231                     j++;
232                 }
233             }
234 
235             if (found)
236             {
237                 return paramNames;
238             }
239             else
240             {
241                 return null;
242             }
243         }
244         else
245         {
246             return null;
247         }
248     }
249 
250     private static class MethodInfo
251     {
252         String[] names;
253 
254         public MethodInfo(int maxLocals)
255         {
256             names = new String[maxLocals];
257         }
258     }
259 
260     private MethodInfo getMethodInfo()
261     {
262         MethodInfo info = null;
263         if (methods != null && methodName != null)
264         {
265             info = methods.get(methodName);
266         }
267         return info;
268     }
269 
270     /**
271      * this is invoked when a LocalVariableTable attribute is encountered.
272      * 
273      * @throws IOException
274      */
275     public void readLocalVariableTable() throws IOException
276     {
277         int len = readShort(); // table length
278         MethodInfo info = getMethodInfo();
279         for (int j = 0; j < len; j++)
280         {
281             readShort(); // start pc
282             readShort(); // length
283             int nameIndex = readShort(); // name_index
284             readShort(); // descriptor_index
285             int index = readShort(); // local index
286             if (info != null)
287             {
288                 info.names[index] = resolveUtf8(nameIndex);
289             }
290         }
291     }
292 }