/*
 * Bytecode analysis framework
 * Copyright (C) 2005, University of Maryland
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package edu.umd.cs.findbugs.ba;

import org.apache.bcel.Constants;
import org.apache.bcel.Repository;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.classfile.Method;

import edu.umd.cs.findbugs.classfile.DescriptorFactory;
import edu.umd.cs.findbugs.classfile.MethodDescriptor;

/**
 * A JavaClass and a Method belonging to the class.
 * This is useful for answering a method lookup query
 * which must concretely identify both the class and the method.
 * 
 * @author David Hovemeyer
 */
public class JavaClassAndMethod {
	private final JavaClass javaClass;
	private final Method method;

	/**
	 * Constructor.
	 * 
	 * @param javaClass the JavaClass
	 * @param method    a Method belonging to the JavaClass
	 */
	public JavaClassAndMethod(JavaClass javaClass, Method method) {
		this.javaClass = javaClass;
		this.method = method;
	}

	/**
	 * Constructor.
	 * 
	 * @param method an XMethod specifying a specific method in a specific class
	 * @throws ClassNotFoundException 
	 */
	public JavaClassAndMethod(XMethod method) throws ClassNotFoundException {

		this.javaClass = Repository.lookupClass(method.getClassName());
		for(Method m : javaClass.getMethods()) 
			if (m.getName().equals(method.getName())
					&& m.getSignature().equals(method.getSignature())
					&& m.isStatic() == method.isStatic()) {
				this.method = m;
				return;
			}
		throw new IllegalArgumentException("Can't find " + method);
	}

	/**
	 * Get the JavaClass.
	 */
	public JavaClass getJavaClass() {
		return javaClass;
	}

	/**
	 * Get the Method.
	 */
	public Method getMethod() {
		return method;
	}

	/**
	 * Convert to an XMethod.
	 */
	public XMethod toXMethod() {
		return XFactory.createXMethod(javaClass, method);
	}

	/**
	 * Get the MethodDescriptor that (hopefully) uniqely names
	 * this method.
	 * 
	 * @return the MethodDescriptor uniquely naming this method
	 */
	public MethodDescriptor toMethodDescriptor() {
		return DescriptorFactory.instance().getMethodDescriptor(
				getSlashedClassName(),
				method.getName(),
				method.getSignature(),
				method.isStatic());
	}

	private String getSlashedClassName() {
		return javaClass.getConstantPool().getConstantString(javaClass.getClassNameIndex(), Constants.CONSTANT_Class);
	}

	@Override
	public int hashCode() {
		return javaClass.hashCode() + method.hashCode();
	}

	@Override
	public boolean equals(Object obj) {
		if (obj == null || obj.getClass() != this.getClass())
			return false;
		JavaClassAndMethod other = (JavaClassAndMethod) obj;
		return javaClass.equals(other.javaClass) && method.equals(other.method);
	}

	@Override
	public String toString() {
		return SignatureConverter.convertMethodSignature(javaClass, method);
	}
}
