ObjectParameterMapper.java

/***************************************************************************
   Copyright 2012 Emily Estes

   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.
***************************************************************************/
package net.metanotion.util;


import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;

public final class ObjectParameterMapper {
	/** Since this class is stateless, this is a convenience instance. */
	public static final ObjectParameterMapper INSTANCE = new ObjectParameterMapper();

	/** Use byte disassembly to extract the names of the parameters to the methods in the class provided.
		@param klazz The class to extract parameter names from.
		@return A map of the method names, whose keys are the parameter names in order.
		@throws IOException if byte code disassembly failes.
	*/
	public Map<String,Iterable<String>> read(final Class klazz) throws IOException {
		return this.read(klazz, new HashMap<String,Iterable<String>>());
	}

	/** Use byte disassembly to extract the names of the parameters to the methods in the class provided.
		@param klazz The class to extract parameter names from.
		@param methods A map to store the methods and their parameters.
		@return A map of the method names, whose keys are the parameter names in order.
		@throws IOException if byte code disassembly failes.
	*/
	public Map<String,Iterable<String>> read(final Class klazz, final Map<String,Iterable<String>> methods) throws IOException {
		final ClassReader reader = new ClassReader(klazz.getName());
		final InterfaceVisitor iv = new InterfaceVisitor(methods);
		reader.accept(iv, 0);
		return methods;
	}

	private static final class InterfaceVisitor extends ClassVisitor {
		private final Map<String,Iterable<String>> methods;
		public InterfaceVisitor(Map<String,Iterable<String>> methods) {
			super(Opcodes.ASM4);
			this.methods = methods;
		}

		@Override public MethodVisitor visitMethod(final int access,
				final String name, final String desc,
				final String signature,
				final String[] exceptions) {
			if("<init>".equals(name)) { return null; }
			final ArrayList<String> params = new ArrayList<>();
			methods.put(name, params);
			return new MethodParameterVisitor(params, Type.getArgumentTypes(desc).length);
		}
	}

	private static final class MethodParameterVisitor extends MethodVisitor {
		private final List<String> params;
		private final int total;
		private int ct = 0;
		public MethodParameterVisitor(final List<String> params, final int total) {
			super(Opcodes.ASM4);
			this.params = params;
			this.total = total;
		}

		@Override public void visitLocalVariable(final String name,
				final String desc,
				final String signature,
				final Label start,
				final Label end,
				final int index) {
			if((!"this".equals(name)) && (ct <= total)) { params.add(name); }
			ct++;
		}
	}
}