001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied. See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019
020package org.apache.bytecode;
021
022import java.io.IOException;
023import java.lang.reflect.Constructor;
024import java.lang.reflect.Method;
025import java.util.ArrayList;
026import java.util.HashMap;
027import java.util.Iterator;
028import java.util.List;
029import java.util.Map;
030
031/**
032 * Description: In ParamReader class, user cannot get inherited method parameter
033 * from the class they passed in for performance reasons This class
034 * is walks up the inheritance chain. If the method is not found in
035 * the derived class, search in super class. If not found in the immedidate super
036 * class, search super class's super class, until the root, which is java.lang.Object,
037 * is reached. This is not an eager load since it only start searching the super class
038 * when it is asked to.
039 */
040public class ChainedParamReader {
041    private List chain = new ArrayList();
042    private List clsChain = new ArrayList();
043    private Map methodToParamMap = new HashMap();
044
045    /**
046     * Processes a given class's parameter names.
047     *
048     * @param cls the class which user wants to get parameter info from
049     * @throws IOException
050     */
051    public ChainedParamReader(Class cls) throws IOException {
052        ParamReader reader = new ParamReader(cls);
053        chain.add(reader);
054        clsChain.add(cls);
055    }
056
057    //now I need to create deligate methods
058
059    /**
060     * Returns the names of the declared parameters for the given constructor.
061     * If we cannot determine the names, return null.  The returned array will
062     * have one name per parameter.  The length of the array will be the same
063     * as the length of the Class[] array returned by Constructor.getParameterTypes().
064     *
065     * @param ctor
066     * @return Returns array of names, one per parameter, or null
067     */
068    public String[] getParameterNames(Constructor ctor) {
069        //there is no need for the constructor chaining.
070        return ((ParamReader) chain.get(0)).getParameterNames(ctor);
071    }
072
073    /**
074     * Returns the names of the declared parameters for the given method.
075     * If cannot determine the names in the current class, search its parent 
076     * class until we reach java.lang.Object. If still can not find the method,
077     * returns null. The returned array has one name per parameter. The length 
078     * of the array will be the same as the length of the Class[] array 
079     * returned by Method.getParameterTypes().
080     *
081     * @param method
082     * @return String[] Returns array of names, one per parameter, or null
083     */
084    public String[] getParameterNames(Method method) {
085        //go find the one from the cache first
086        if (methodToParamMap.containsKey(method)) {
087            return (String[]) methodToParamMap.get(method);
088        }
089
090        String[] ret = null;
091        for (Iterator it = chain.iterator(); it.hasNext();) {
092            ParamReader reader = (ParamReader) it.next();
093            ret = reader.getParameterNames(method);
094            if (ret != null) {
095                methodToParamMap.put(method, ret);
096                return ret;
097            }
098        }
099        //if we here, it means we need to create new chain.
100        Class cls = (Class) clsChain.get(chain.size() - 1);
101        while (cls != null && cls != java.lang.Object.class && cls.getSuperclass() != null) {
102            Class superClass = cls.getSuperclass();
103            try {
104                ParamReader _reader = new ParamReader(superClass);
105                chain.add(_reader);
106                clsChain.add(cls);
107                ret = _reader.getParameterNames(method);
108                if (ret != null) { //we found it so just return it.
109                    methodToParamMap.put(method, ret);
110                    return ret;
111                }
112            } catch (IOException e) {
113                //can not find the super class in the class path, abort here
114                return null;
115            }
116            cls = superClass;
117        }
118        methodToParamMap.put(method, ret);
119        return null;
120    }
121}