001    package railo.transformer.bytecode.reflection;
002    
003    
004    import java.lang.reflect.InvocationTargetException;
005    import java.lang.reflect.Member;
006    import java.lang.reflect.Method;
007    import java.lang.reflect.Modifier;
008    
009    import railo.commons.lang.ClassUtil;
010    
011    /**
012     * A {@code Method} provides information about, and access to, a single method
013     * on a class or interface.  The reflected method may be a class method
014     * or an instance method (including an abstract method).
015     *
016     * <p>A {@code Method} permits widening conversions to occur when matching the
017     * actual parameters to invoke with the underlying method's formal
018     * parameters, but it throws an {@code IllegalArgumentException} if a
019     * narrowing conversion would occur.
020     *
021     * @see Member
022     * @see java.lang.Class
023     * @see java.lang.Class#getMethods()
024     * @see java.lang.Class#getMethod(String, Class[])
025     * @see java.lang.Class#getDeclaredMethods()
026     * @see java.lang.Class#getDeclaredMethod(String, Class[])
027     */
028    public abstract class ASMMethod  {
029        private Class[]             exceptionTypes;
030        private Class               returnType;
031        private Class[]             parameterTypes;
032        //private int                 modifiers;
033        private Class               clazz;
034        
035    
036        /**
037         * Package-private constructor used by ReflectAccess to enable
038         * instantiation of these objects in Java code from the java.lang
039         * package via sun.reflect.LangReflectAccess.
040         */
041        public ASMMethod(Class declaringClass, Class[] parameterTypes) {
042            this.clazz = declaringClass;
043            this.parameterTypes = parameterTypes;
044        }
045        
046    
047        /**
048         * Returns the {@code Class} object representing the class or interface
049         * that declares the method represented by this {@code Method} object.
050         */
051        public Class<?> getDeclaringClass() {
052            return clazz;
053        }
054    
055        /**
056         * Returns the name of the method represented by this {@code Method}
057         * object, as a {@code String}.
058         */
059        public abstract String getName();
060    
061        /**
062         * Returns the Java language modifiers for the method represented
063         * by this {@code Method} object, as an integer. The {@code Modifier} class should
064         * be used to decode the modifiers.
065         *
066         * @see Modifier
067         */
068        public abstract int getModifiers();
069    
070        /**
071         * Returns a {@code Class} object that represents the formal return type
072         * of the method represented by this {@code Method} object.
073         *
074         * @return the return type for the method this object represents
075         */
076    
077        public  Class<?> getReturnType() {
078            if(returnType==null) {
079                    returnType = ClassUtil.loadClass(getReturnTypeAsString(),null);
080                    if(returnType==null) initAddionalParams();
081            }
082            return returnType;
083        }
084        
085        protected Class<?> _getReturnType() {
086            if(returnType==null) initAddionalParams();
087            return returnType;
088        }
089        
090        public abstract String getReturnTypeAsString();
091    
092        /**
093         * Returns an array of {@code Class} objects that represent the formal
094         * parameter types, in declaration order, of the method
095         * represented by this {@code Method} object.  Returns an array of length
096         * 0 if the underlying method takes no parameters.
097         *
098         * @return the parameter types for the method this object
099         * represents
100         */
101        public Class<?>[] getParameterTypes() {
102            return parameterTypes.clone();
103        }
104    
105    
106        /**
107         * Returns an array of {@code Class} objects that represent
108         * the types of the exceptions declared to be thrown
109         * by the underlying method
110         * represented by this {@code Method} object.  Returns an array of length
111         * 0 if the method declares no exceptions in its {@code throws} clause.
112         *
113         * @return the exception types declared as being thrown by the
114         * method this object represents
115         */
116        public Class<?>[] getExceptionTypes() {
117            if(exceptionTypes==null) initAddionalParams();
118            return exceptionTypes.clone();
119        }
120    
121        private void initAddionalParams() {
122                    try {
123                            Method m = clazz.getMethod(getName(), getParameterTypes());
124                            exceptionTypes=m.getExceptionTypes();
125                            returnType=m.getReturnType();
126                    }
127                    catch (SecurityException e) {
128                            // TODO remove block
129                            e.printStackTrace();
130                    }
131                    catch (NoSuchMethodException e) {
132                            // TODO remove block
133                            e.printStackTrace();
134                    }
135                    
136            }
137    
138    
139            /**
140         * Compares this {@code Method} against the specified object.  Returns
141         * true if the objects are the same.  Two {@code Methods} are the same if
142         * they were declared by the same class and have the same name
143         * and formal parameter types and return type.
144         */
145        public boolean equals(Object obj) {
146            if(this==obj) return true;
147            if(!(obj instanceof ASMMethod)) return false;
148            
149            ASMMethod other=(ASMMethod) obj;
150            
151            return 
152            this.getModifiers()==other.getModifiers() &&
153            this.getName().equals(other.getName()) &&
154            this.clazz.equals(other.clazz) &&
155            this.returnType.equals(other.returnType) &&
156            eq(this.getExceptionTypes(),other.getExceptionTypes()) &&
157            eq(this.parameterTypes,other.parameterTypes);
158        }
159    
160        private boolean eq(Class[] left, Class[] right) {
161            int l=left==null?0:left.length;
162            int r=right==null?0:right.length;
163                    if(l!=r) return false;
164                    if(l==0) return true;
165                    
166                    for(int i=0;i<left.length;i++){
167                            if(!left[i].equals(right[i])) return false;
168                    }
169                    return true;
170            }
171    
172            /**
173         * Invokes the underlying method represented by this {@code Method}
174         * object, on the specified object with the specified parameters.
175         * Individual parameters are automatically unwrapped to match
176         * primitive formal parameters, and both primitive and reference
177         * parameters are subject to method invocation conversions as
178         * necessary.
179         *
180         * <p>If the underlying method is static, then the specified {@code obj}
181         * argument is ignored. It may be null.
182         *
183         * <p>If the number of formal parameters required by the underlying method is
184         * 0, the supplied {@code args} array may be of length 0 or null.
185         *
186         * <p>If the underlying method is an instance method, it is invoked
187         * using dynamic method lookup as documented in The Java Language
188         * Specification, Second Edition, section 15.12.4.4; in particular,
189         * overriding based on the runtime type of the target object will occur.
190         *
191         * <p>If the underlying method is static, the class that declared
192         * the method is initialized if it has not already been initialized.
193         *
194         * <p>If the method completes normally, the value it returns is
195         * returned to the caller of invoke; if the value has a primitive
196         * type, it is first appropriately wrapped in an object. However,
197         * if the value has the type of an array of a primitive type, the
198         * elements of the array are <i>not</i> wrapped in objects; in
199         * other words, an array of primitive type is returned.  If the
200         * underlying method return type is void, the invocation returns
201         * null.
202         *
203         * @param obj  the object the underlying method is invoked from
204         * @param args the arguments used for the method call
205         * @return the result of dispatching the method represented by
206         * this object on {@code obj} with parameters
207         * {@code args}
208         *
209         * @exception IllegalAccessException    if this {@code Method} object
210         *              enforces Java language access control and the underlying
211         *              method is inaccessible.
212         * @exception IllegalArgumentException  if the method is an
213         *              instance method and the specified object argument
214         *              is not an instance of the class or interface
215         *              declaring the underlying method (or of a subclass
216         *              or implementor thereof); if the number of actual
217         *              and formal parameters differ; if an unwrapping
218         *              conversion for primitive arguments fails; or if,
219         *              after possible unwrapping, a parameter value
220         *              cannot be converted to the corresponding formal
221         *              parameter type by a method invocation conversion.
222         * @exception InvocationTargetException if the underlying method
223         *              throws an exception.
224         * @exception NullPointerException      if the specified object is null
225         *              and the method is an instance method.
226         * @exception ExceptionInInitializerError if the initialization
227         * provoked by this method fails.
228         */
229        public abstract Object invoke(Object obj, Object[] args) throws Throwable;//IllegalAccessException, IllegalArgumentException, InvocationTargetException;
230    }