Author: mmerz
Date: Wed Dec 22 10:12:38 2004
New Revision: 123132

URL: http://svn.apache.org/viewcvs?view=rev&rev=123132
Log:
Fix for BEEHIVE-120 (@WebParam.name default values). Code for reading parameter 
names from debug information (util/bytecode/*) was taken from Apache Axis 
project.


Added:
   
incubator/beehive/trunk/wsm/src/runtime/org/apache/beehive/wsm/jsr181/util/bytecode/
   
incubator/beehive/trunk/wsm/src/runtime/org/apache/beehive/wsm/jsr181/util/bytecode/ClassReader.java
   
incubator/beehive/trunk/wsm/src/runtime/org/apache/beehive/wsm/jsr181/util/bytecode/ParamNameExtractor.java
   
incubator/beehive/trunk/wsm/src/runtime/org/apache/beehive/wsm/jsr181/util/bytecode/ParamReader.java
Modified:
   
incubator/beehive/trunk/wsm/drt/tests/org/apache/beehive/wsm/jsr181/processor/apt/WsmAnnotationProcessorTest.java
   
incubator/beehive/trunk/wsm/drt/tests/org/apache/beehive/wsm/jsr181/processor/reflection/WsmAnnotationProcessorTest.java
   
incubator/beehive/trunk/wsm/src/runtime/org/apache/beehive/wsm/jsr181/processor/reflection/WsmReflectionAnnotationProcessor.java

Modified: 
incubator/beehive/trunk/wsm/drt/tests/org/apache/beehive/wsm/jsr181/processor/apt/WsmAnnotationProcessorTest.java
Url: 
http://svn.apache.org/viewcvs/incubator/beehive/trunk/wsm/drt/tests/org/apache/beehive/wsm/jsr181/processor/apt/WsmAnnotationProcessorTest.java?view=diff&rev=123132&p1=incubator/beehive/trunk/wsm/drt/tests/org/apache/beehive/wsm/jsr181/processor/apt/WsmAnnotationProcessorTest.java&r1=123131&p2=incubator/beehive/trunk/wsm/drt/tests/org/apache/beehive/wsm/jsr181/processor/apt/WsmAnnotationProcessorTest.java&r2=123132
==============================================================================
--- 
incubator/beehive/trunk/wsm/drt/tests/org/apache/beehive/wsm/jsr181/processor/apt/WsmAnnotationProcessorTest.java
   (original)
+++ 
incubator/beehive/trunk/wsm/drt/tests/org/apache/beehive/wsm/jsr181/processor/apt/WsmAnnotationProcessorTest.java
   Wed Dec 22 10:12:38 2004
@@ -63,7 +63,6 @@
     /**
      * We can't derive default values for WebParam from binaries. Thus, we use
      * our own default name "in<number>".
-     */
     @Override
     public void testWebParamGoLocoFirst() throws Exception {
         final int paramNo = 0;
@@ -74,4 +73,5 @@
                 int.class,
                 param.getJavaType());
     }
+     */
 }

Modified: 
incubator/beehive/trunk/wsm/drt/tests/org/apache/beehive/wsm/jsr181/processor/reflection/WsmAnnotationProcessorTest.java
Url: 
http://svn.apache.org/viewcvs/incubator/beehive/trunk/wsm/drt/tests/org/apache/beehive/wsm/jsr181/processor/reflection/WsmAnnotationProcessorTest.java?view=diff&rev=123132&p1=incubator/beehive/trunk/wsm/drt/tests/org/apache/beehive/wsm/jsr181/processor/reflection/WsmAnnotationProcessorTest.java&r1=123131&p2=incubator/beehive/trunk/wsm/drt/tests/org/apache/beehive/wsm/jsr181/processor/reflection/WsmAnnotationProcessorTest.java&r2=123132
==============================================================================
--- 
incubator/beehive/trunk/wsm/drt/tests/org/apache/beehive/wsm/jsr181/processor/reflection/WsmAnnotationProcessorTest.java
    (original)
+++ 
incubator/beehive/trunk/wsm/drt/tests/org/apache/beehive/wsm/jsr181/processor/reflection/WsmAnnotationProcessorTest.java
    Wed Dec 22 10:12:38 2004
@@ -42,7 +42,6 @@
     /**
      * We can't derive default values for WebParam from binaries. Thus, we use
      * our own default name "in<number>".
-     */
     @Override
     public void testWebParamGoLocoFirst() throws Exception {
         final int paramNo = 0;
@@ -53,5 +52,6 @@
                 int.class,
                 param.getJavaType());
     }
+     */
 }
 

Modified: 
incubator/beehive/trunk/wsm/src/runtime/org/apache/beehive/wsm/jsr181/processor/reflection/WsmReflectionAnnotationProcessor.java
Url: 
http://svn.apache.org/viewcvs/incubator/beehive/trunk/wsm/src/runtime/org/apache/beehive/wsm/jsr181/processor/reflection/WsmReflectionAnnotationProcessor.java?view=diff&rev=123132&p1=incubator/beehive/trunk/wsm/src/runtime/org/apache/beehive/wsm/jsr181/processor/reflection/WsmReflectionAnnotationProcessor.java&r1=123131&p2=incubator/beehive/trunk/wsm/src/runtime/org/apache/beehive/wsm/jsr181/processor/reflection/WsmReflectionAnnotationProcessor.java&r2=123132
==============================================================================
--- 
incubator/beehive/trunk/wsm/src/runtime/org/apache/beehive/wsm/jsr181/processor/reflection/WsmReflectionAnnotationProcessor.java
    (original)
+++ 
incubator/beehive/trunk/wsm/src/runtime/org/apache/beehive/wsm/jsr181/processor/reflection/WsmReflectionAnnotationProcessor.java
    Wed Dec 22 10:12:38 2004
@@ -126,20 +126,34 @@
      * @param method
      * @return
      */
-    protected Jsr181MethodMetadata getWebServiceMETHODMetadata(Method method) {
+    protected Jsr181MethodMetadata getWebServiceMETHODMetadata(Method method)
+    {
         List<Jsr181ParameterMetadata> webParams =
                 new ArrayList<Jsr181ParameterMetadata>();
 
         // publish all params
         Annotation[][] allAnnotations = method.getParameterAnnotations();
-        int offset = 0;
-        for (Class paramType : method.getParameterTypes()) {
-            webParams.add(getWebServicePARAMETERMetadata(paramType,
-                    offset,
-                    Arrays.asList(allAnnotations[offset])));
-            offset++;
+        final Class[] parameterTypes = method.getParameterTypes();
+        if ((null != parameterTypes) && (0 < parameterTypes.length))
+        {
+            final String[] defaultNames =
+                
org.apache.beehive.wsm.jsr181.util.bytecode.ParamNameExtractor.getParameterNamesFromDebugInfo(method);
+            int offset = 0;
+            for (Class paramType : parameterTypes) {
+                String defaultName = "in" + offset;
+                if ((null != defaultNames) && (offset < defaultNames.length))
+                {
+                    defaultName = defaultNames[offset];
+                }
+                webParams.add(getWebServicePARAMETERMetadata(
+                    paramType,
+                    defaultName,
+                    Arrays.asList(allAnnotations[offset])
+                ));
+                offset++;
+            }
         }
-
+        
         // create & return webMethod
         Jsr181MethodMetadata wsmm = null;
         try {
@@ -157,18 +171,18 @@
 
     /**
      * @param paramType
-     * @param offset
+     * @param defaultName
      * @param annotations
      * @return
      */
-    protected Jsr181ParameterMetadata getWebServicePARAMETERMetadata(Class 
paramType, int offset, List<Annotation> annotations) {
+    protected Jsr181ParameterMetadata getWebServicePARAMETERMetadata(Class 
paramType, String defaultName, List<Annotation> annotations) {
+        
         // create & return webParam
         Jsr181ParameterMetadata wspm = null;
         try {
-            wspm = new Jsr181ParameterMetadataImpl(paramType,
-                    "in" + offset, // by default parameter should be IN
-                    annotations);
-        } catch (Throwable t) {
+            wspm = new Jsr181ParameterMetadataImpl(paramType, defaultName, 
annotations);
+        }
+        catch (Throwable t) {
             t.printStackTrace(); // todo: proper error handling
             wspm = null;
         }

Added: 
incubator/beehive/trunk/wsm/src/runtime/org/apache/beehive/wsm/jsr181/util/bytecode/ClassReader.java
Url: 
http://svn.apache.org/viewcvs/incubator/beehive/trunk/wsm/src/runtime/org/apache/beehive/wsm/jsr181/util/bytecode/ClassReader.java?view=auto&rev=123132
==============================================================================
--- (empty file)
+++ 
incubator/beehive/trunk/wsm/src/runtime/org/apache/beehive/wsm/jsr181/util/bytecode/ClassReader.java
        Wed Dec 22 10:12:38 2004
@@ -0,0 +1,426 @@
+package org.apache.beehive.wsm.jsr181.util.bytecode;
+
+/*
+ * Copyright 2002-2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * This is the class file reader for obtaining the parameter names
+ * for declared methods in a class.  The class must have debugging
+ * attributes for us to obtain this information. <p>
+ *
+ * This does not work for inherited methods.  To obtain parameter
+ * names for inherited methods, you must use a paramReader for the
+ * class that originally declared the method. <p>
+ *
+ * don't get tricky, it's the bare minimum.  Instances of this class
+ * are not threadsafe -- don't share them. <p>
+ *
+ * @author Edwin Smith, Macromedia
+ */
+public class ClassReader extends ByteArrayInputStream {
+    // constants values that appear in java class files,
+    // from jvm spec 2nd ed, section 4.4, pp 103
+    private static final int CONSTANT_Class = 7;
+    private static final int CONSTANT_Fieldref = 9;
+    private static final int CONSTANT_Methodref = 10;
+    private static final int CONSTANT_InterfaceMethodref = 11;
+    private static final int CONSTANT_String = 8;
+    private static final int CONSTANT_Integer = 3;
+    private static final int CONSTANT_Float = 4;
+    private static final int CONSTANT_Long = 5;
+    private static final int CONSTANT_Double = 6;
+    private static final int CONSTANT_NameAndType = 12;
+    private static final int CONSTANT_Utf8 = 1;
+    /**
+     * the constant pool.  constant pool indices in the class file
+     * directly index into this array.  The value stored in this array
+     * is the position in the class file where that constant begins.
+     */
+    private int[] cpoolIndex;
+    private Object[] cpool;
+
+    private Map attrMethods;
+
+    /**
+     * load the bytecode for a given class, by using the class's defining
+     * classloader and assuming that for a class named P.C, the bytecodes are
+     * in a resource named /P/C.class.
+     * @param c the class of interest
+     * @return a byte array containing the bytecode
+     * @throws IOException
+     */
+    protected static byte[] getBytes(Class c) throws IOException {
+        InputStream fin = c.getResourceAsStream('/' + c.getName().replace('.', 
'/') + ".class");
+        if (fin == null) {
+            throw new IOException("cantLoadByecode for " + c.getName());
+        }
+        try {
+            ByteArrayOutputStream out = new ByteArrayOutputStream();
+            byte[] buf = new byte[1024];
+            int actual;
+            do {
+                actual = fin.read(buf);
+                if (actual > 0) {
+                    out.write(buf, 0, actual);
+                }
+            } while (actual > 0);
+            return out.toByteArray();
+        } finally {
+            fin.close();
+        }
+    }
+
+    static String classDescriptorToName(String desc) {
+        return desc.replace('/', '.');
+    }
+
+    protected static Map findAttributeReaders(Class c) {
+        HashMap map = new HashMap();
+        Method[] methods = c.getMethods();
+
+        for (int i = 0; i < methods.length; i++) {
+            String name = methods[i].getName();
+            if (name.startsWith("read") && methods[i].getReturnType() == 
void.class) {
+                map.put(name.substring(4), methods[i]);
+            }
+        }
+
+        return map;
+    }
+
+
+    protected static String getSignature(Member method, Class[] paramTypes) {
+        // compute the method descriptor
+
+        StringBuffer b = new StringBuffer((method instanceof Method) ? 
method.getName() : "<init>");
+        b.append('(');
+
+        for (int i = 0; i < paramTypes.length; i++) {
+            addDescriptor(b, paramTypes[i]);
+        }
+
+        b.append(')');
+        if (method instanceof Method) {
+            addDescriptor(b, ((Method) method).getReturnType());
+        } else if (method instanceof Constructor) {
+            addDescriptor(b, void.class);
+        }
+
+        return b.toString();
+    }
+
+    private static void addDescriptor(StringBuffer b, Class c) {
+        if (c.isPrimitive()) {
+            if (c == void.class)
+                b.append('V');
+            else if (c == int.class)
+                b.append('I');
+            else if (c == boolean.class)
+                b.append('Z');
+            else if (c == byte.class)
+                b.append('B');
+            else if (c == short.class)
+                b.append('S');
+            else if (c == long.class)
+                b.append('J');
+            else if (c == char.class)
+                b.append('C');
+            else if (c == float.class)
+                b.append('F');
+            else if (c == double.class) b.append('D');
+        } else if (c.isArray()) {
+            b.append('[');
+            addDescriptor(b, c.getComponentType());
+        } else {
+            b.append('L').append(c.getName().replace('.', '/')).append(';');
+        }
+    }
+
+
+    /**
+     * @return the next unsigned 16 bit value
+     */
+    protected final int readShort() {
+        return (read() << 8) | read();
+    }
+
+    /**
+     * @return the next signed 32 bit value
+     */
+    protected final int readInt() {
+        return (read() << 24) | (read() << 16) | (read() << 8) | read();
+    }
+
+    /**
+     * skip n bytes in the input stream.
+     */
+    protected void skipFully(int n) throws IOException {
+        while (n > 0) {
+            int c = (int) skip(n);
+            if (c <= 0)
+                throw new EOFException("Unexpected end of file");
+            n -= c;
+        }
+    }
+
+    protected final Member resolveMethod(int index) throws IOException, 
ClassNotFoundException, NoSuchMethodException {
+        int oldPos = pos;
+        try {
+            Member m = (Member) cpool[index];
+            if (m == null) {
+                pos = cpoolIndex[index];
+                Class owner = resolveClass(readShort());
+                NameAndType nt = resolveNameAndType(readShort());
+                String signature = nt.name + nt.type;
+                if (nt.name.equals("<init>")) {
+                    Constructor[] ctors = owner.getConstructors();
+                    for (int i = 0; i < ctors.length; i++) {
+                        String sig = getSignature(ctors[i], 
ctors[i].getParameterTypes());
+                        if (sig.equals(signature)) {
+                            cpool[index] = m = ctors[i];
+                            return m;
+                        }
+                    }
+                } else {
+                    Method[] methods = owner.getDeclaredMethods();
+                    for (int i = 0; i < methods.length; i++) {
+                        String sig = getSignature(methods[i], 
methods[i].getParameterTypes());
+                        if (sig.equals(signature)) {
+                            cpool[index] = m = methods[i];
+                            return m;
+                        }
+                    }
+                }
+                throw new NoSuchMethodException(signature);
+            }
+            return m;
+        } finally {
+            pos = oldPos;
+        }
+
+    }
+
+    protected final Field resolveField(int i) throws IOException, 
ClassNotFoundException, NoSuchFieldException {
+        int oldPos = pos;
+        try {
+            Field f = (Field) cpool[i];
+            if (f == null) {
+                pos = cpoolIndex[i];
+                Class owner = resolveClass(readShort());
+                NameAndType nt = resolveNameAndType(readShort());
+                cpool[i] = f = owner.getDeclaredField(nt.name);
+            }
+            return f;
+        } finally {
+            pos = oldPos;
+        }
+    }
+
+    private static class NameAndType {
+        String name;
+        String type;
+
+        public NameAndType(String name, String type) {
+            this.name = name;
+            this.type = type;
+        }
+    }
+
+    protected final NameAndType resolveNameAndType(int i) throws IOException {
+        int oldPos = pos;
+        try {
+            NameAndType nt = (NameAndType) cpool[i];
+            if (nt == null) {
+                pos = cpoolIndex[i];
+                String name = resolveUtf8(readShort());
+                String type = resolveUtf8(readShort());
+                cpool[i] = nt = new NameAndType(name, type);
+            }
+            return nt;
+        } finally {
+            pos = oldPos;
+        }
+    }
+
+
+    protected final Class resolveClass(int i) throws IOException, 
ClassNotFoundException {
+        int oldPos = pos;
+        try {
+            Class c = (Class) cpool[i];
+            if (c == null) {
+                pos = cpoolIndex[i];
+                String name = resolveUtf8(readShort());
+                cpool[i] = c = Class.forName(classDescriptorToName(name));
+            }
+            return c;
+        } finally {
+            pos = oldPos;
+        }
+    }
+
+    protected final String resolveUtf8(int i) throws IOException {
+        int oldPos = pos;
+        try {
+            String s = (String) cpool[i];
+            if (s == null) {
+                pos = cpoolIndex[i];
+                int len = readShort();
+                skipFully(len);
+                cpool[i] = s = new String(buf, pos - len, len, "utf-8");
+            }
+            return s;
+        } finally {
+            pos = oldPos;
+        }
+    }
+
+    protected final void readCpool() throws IOException {
+        int count = readShort(); // cpool count
+        cpoolIndex = new int[count];
+        cpool = new Object[count];
+        for (int i = 1; i < count; i++) {
+            int c = read();
+            cpoolIndex[i] = super.pos;
+            switch (c) // constant pool tag
+            {
+                case CONSTANT_Fieldref:
+                case CONSTANT_Methodref:
+                case CONSTANT_InterfaceMethodref:
+                case CONSTANT_NameAndType:
+
+                    readShort(); // class index or (12) name index
+                    // fall through
+
+                case CONSTANT_Class:
+                case CONSTANT_String:
+
+                    readShort(); // string index or class index
+                    break;
+
+                case CONSTANT_Long:
+                case CONSTANT_Double:
+
+                    readInt(); // hi-value
+
+                    // see jvm spec section 4.4.5 - double and long cpool
+                    // entries occupy two "slots" in the cpool table.
+                    i++;
+                    // fall through
+
+                case CONSTANT_Integer:
+                case CONSTANT_Float:
+
+                    readInt(); // value
+                    break;
+
+                case CONSTANT_Utf8:
+
+                    int len = readShort();
+                    skipFully(len);
+                    break;
+
+                default:
+                    // corrupt class file
+                    throw new IllegalStateException("Unexpected Byte");
+            }
+        }
+    }
+
+    protected final void skipAttributes() throws IOException {
+        int count = readShort();
+        for (int i = 0; i < count; i++) {
+            readShort(); // name index
+            skipFully(readInt());
+        }
+    }
+
+    /**
+     * read an attributes array.  the elements of a class file that
+     * can contain attributes are: fields, methods, the class itself,
+     * and some other types of attributes.
+     */
+    protected final void readAttributes() throws IOException {
+        int count = readShort();
+        for (int i = 0; i < count; i++) {
+            int nameIndex = readShort(); // name index
+            int attrLen = readInt();
+            int curPos = pos;
+
+            String attrName = resolveUtf8(nameIndex);
+
+            Method m = (Method) attrMethods.get(attrName);
+
+            if (m != null) {
+                try {
+                    m.invoke(this, new Object[]{});
+                } catch (IllegalAccessException e) {
+                    pos = curPos;
+                    skipFully(attrLen);
+                } catch (InvocationTargetException e) {
+                    try {
+                        throw e.getTargetException();
+                    } catch (Error ex) {
+                        throw ex;
+                    } catch (RuntimeException ex) {
+                        throw ex;
+                    } catch (IOException ex) {
+                        throw ex;
+                    } catch (Throwable ex) {
+                        pos = curPos;
+                        skipFully(attrLen);
+                    }
+                }
+            } else {
+                // don't care what attribute this is
+                skipFully(attrLen);
+            }
+        }
+    }
+
+    /**
+     * read a code attribute
+     * @throws IOException
+     */
+    public void readCode() throws IOException {
+        readShort(); // max stack
+        readShort(); // max locals
+        skipFully(readInt()); // code
+        skipFully(8 * readShort()); // exception table
+
+        // read the code attributes (recursive).  This is where
+        // we will find the LocalVariableTable attribute.
+        readAttributes();
+    }
+
+    protected ClassReader(byte buf[], Map attrMethods) {
+        super(buf);
+
+        this.attrMethods = attrMethods;
+    }
+}
\ No newline at end of file

Added: 
incubator/beehive/trunk/wsm/src/runtime/org/apache/beehive/wsm/jsr181/util/bytecode/ParamNameExtractor.java
Url: 
http://svn.apache.org/viewcvs/incubator/beehive/trunk/wsm/src/runtime/org/apache/beehive/wsm/jsr181/util/bytecode/ParamNameExtractor.java?view=auto&rev=123132
==============================================================================
--- (empty file)
+++ 
incubator/beehive/trunk/wsm/src/runtime/org/apache/beehive/wsm/jsr181/util/bytecode/ParamNameExtractor.java
 Wed Dec 22 10:12:38 2004
@@ -0,0 +1,65 @@
+package org.apache.beehive.wsm.jsr181.util.bytecode;
+
+/*
+ * Copyright 2001-2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+/**
+ * This class retieves function parameter names from bytecode built with
+ * debugging symbols.  Used as a last resort when creating WSDL.
+ *
+ * @author <a href="mailto:[EMAIL PROTECTED]">Tom Jordahl</a>
+ */
+public class ParamNameExtractor {
+
+//    protected static Log log =
+//            LogFactory.getLog(ParamNameExtractor.class.getName());
+
+    /**
+     * Retrieve a list of function parameter names from a method
+     * Returns null if unable to read parameter names (i.e. bytecode not
+     * built with debug).
+     */
+    public static String[] getParameterNamesFromDebugInfo(Method method) {
+        // Don't worry about it if there are no params.
+        int numParams = method.getParameterTypes().length;
+        if (numParams == 0)
+            return null;
+
+        // get declaring class
+        Class c = method.getDeclaringClass();
+        
+        // Don't worry about it if the class is a Java dynamic proxy 
+        if(Proxy.isProxyClass(c)) {
+            return null;
+        }
+        
+        try {
+            // get a parameter reader
+            ParamReader pr = new ParamReader(c);
+            // get the paramter names
+            String[] names = pr.getParameterNames(method);
+            return names;
+        } catch (IOException e) {
+            // log it and leave
+//            log.info(Messages.getMessage("error00") + ":" + e);
+            return null;
+        }
+    }
+}

Added: 
incubator/beehive/trunk/wsm/src/runtime/org/apache/beehive/wsm/jsr181/util/bytecode/ParamReader.java
Url: 
http://svn.apache.org/viewcvs/incubator/beehive/trunk/wsm/src/runtime/org/apache/beehive/wsm/jsr181/util/bytecode/ParamReader.java?view=auto&rev=123132
==============================================================================
--- (empty file)
+++ 
incubator/beehive/trunk/wsm/src/runtime/org/apache/beehive/wsm/jsr181/util/bytecode/ParamReader.java
        Wed Dec 22 10:12:38 2004
@@ -0,0 +1,223 @@
+package org.apache.beehive.wsm.jsr181.util.bytecode;
+
+/*
+ * Copyright 2002-2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Member;
+import java.lang.reflect.Modifier;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * This is the class file reader for obtaining the parameter names
+ * for declared methods in a class.  The class must have debugging
+ * attributes for us to obtain this information. <p>
+ *
+ * This does not work for inherited methods.  To obtain parameter
+ * names for inherited methods, you must use a paramReader for the
+ * class that originally declared the method. <p>
+ *
+ * don't get tricky, it's the bare minimum.  Instances of this class
+ * are not threadsafe -- don't share them. <p>
+ *
+ * @author Edwin Smith, Macromedia
+ */
+public class ParamReader
+        extends ClassReader {
+    private String methodName;
+    private Map methods = new HashMap();
+    private Class[] paramTypes;
+
+    /**
+     * process a class file, given it's class.  We'll use the defining
+     * classloader to locate the bytecode.
+     * @param c
+     * @throws IOException
+     */
+    public ParamReader(Class c) throws IOException {
+        this(getBytes(c));
+    }
+
+    /**
+     * process the given class bytes directly.
+     * @param b
+     * @throws IOException
+     */
+    public ParamReader(byte[] b) throws IOException {
+        super(b, findAttributeReaders(ParamReader.class));
+
+        // check the magic number
+        if (readInt() != 0xCAFEBABE) {
+            // not a class file!
+            throw new IOException("badClassFile");
+        }
+
+        readShort(); // minor version
+        readShort(); // major version
+
+        readCpool(); // slurp in the constant pool
+
+        readShort(); // access flags
+        readShort(); // this class name
+        readShort(); // super class name
+
+        int count = readShort(); // ifaces count
+        for (int i = 0; i < count; i++) {
+            readShort(); // interface index
+        }
+
+        count = readShort(); // fields count
+        for (int i = 0; i < count; i++) {
+            readShort(); // access flags
+            readShort(); // name index
+            readShort(); // descriptor index
+            skipAttributes(); // field attributes
+        }
+
+        count = readShort(); // methods count
+        for (int i = 0; i < count; i++) {
+            readShort(); // access flags
+            int m = readShort(); // name index
+            String name = resolveUtf8(m);
+            int d = readShort(); // descriptor index
+            this.methodName = name + resolveUtf8(d);
+            readAttributes(); // method attributes
+        }
+
+    }
+
+    public void readCode() throws IOException
+    {
+        readShort(); // max stack
+        int maxLocals = readShort(); // max locals
+
+        MethodInfo info = new MethodInfo(maxLocals);
+        if (methods != null && methodName != null)
+        {
+            methods.put(methodName, info);
+        }
+
+        skipFully(readInt()); // code
+        skipFully(8 * readShort()); // exception table
+        // read the code attributes (recursive).  This is where
+        // we will find the LocalVariableTable attribute.
+        readAttributes();
+    }
+
+    /**
+     * return the names of the declared parameters for the given constructor.
+     * If we cannot determine the names, return null.  The returned array will
+     * have one name per parameter.  The length of the array will be the same
+     * as the length of the Class[] array returned by 
Constructor.getParameterTypes().
+     * @param ctor
+     * @return String[] array of names, one per parameter, or null
+     */
+    public String[] getParameterNames(Constructor ctor) {
+        paramTypes = ctor.getParameterTypes();
+        return getParameterNames(ctor, paramTypes);
+    }
+
+    /**
+     * return the names of the declared parameters for the given method.
+     * If we cannot determine the names, return null.  The returned array will
+     * have one name per parameter.  The length of the array will be the same
+     * as the length of the Class[] array returned by 
Method.getParameterTypes().
+     * @param method
+     * @return String[] array of names, one per parameter, or null
+     */
+    public String[] getParameterNames(Method method) {
+        paramTypes = method.getParameterTypes();
+        return getParameterNames(method, paramTypes);
+    }
+
+    protected String[] getParameterNames(Member member,Class [] paramTypes) {
+        // look up the names for this method
+        MethodInfo info = (MethodInfo) methods.get(getSignature(member, 
paramTypes));
+
+        // we know all the local variable names, but we only need to return
+        // the names of the parameters.
+
+        if (info != null) {
+            String[] paramNames = new String[paramTypes.length];
+            int j = Modifier.isStatic(member.getModifiers()) ? 0 : 1;
+
+            boolean found = false;  // did we find any non-null names
+            for (int i = 0; i < paramNames.length; i++) {
+                if (info.names[j] != null) {
+                    found = true;
+                    paramNames[i] = info.names[j];
+                }
+                j++;
+                if (paramTypes[i] == double.class || paramTypes[i] == 
long.class) {
+                    // skip a slot for 64bit params
+                    j++;
+                }
+            }
+
+            if (found) {
+                return paramNames;
+            } else {
+                return null;
+            }
+        } else {
+            return null;
+        }
+    }
+
+    private static class MethodInfo
+    {
+        String[] names;
+        int maxLocals;
+
+        public MethodInfo(int maxLocals)
+        {
+            this.maxLocals = maxLocals;
+            names = new String[maxLocals];
+        }
+    }
+
+    private MethodInfo getMethodInfo()
+    {
+        MethodInfo info = null;
+        if (methods != null && methodName != null)
+        {
+            info = (MethodInfo) methods.get(methodName);
+        }
+        return info;
+    }
+
+    /**
+     * this is invoked when a LocalVariableTable attribute is encountered.
+     * @throws IOException
+     */
+    public void readLocalVariableTable() throws IOException {
+        int len = readShort(); // table length
+        MethodInfo info = getMethodInfo();
+        for (int j = 0; j < len; j++) {
+            readShort(); // start pc
+            readShort(); // length
+            int nameIndex = readShort(); // name_index
+            readShort(); // descriptor_index
+            int index = readShort(); // local index
+            if (info != null) {
+                info.names[index] = resolveUtf8(nameIndex);
+            }
+        }
+    }
+}
\ No newline at end of file

Reply via email to