/*
* This file is part of JOP, the Java Optimized Processor
* see <http://www.jopdesign.com/>
*
* Copyright (C) 2010, Stefan Hepp (stefan@stefant.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.jopdesign.common.type;
import com.jopdesign.common.ClassInfo;
import com.jopdesign.common.MethodInfo;
import com.jopdesign.common.misc.Ternary;
/**
* A container of a class reference.
* Holds either a ClassInfo object or a classname with some infos if the
* classInfo has not been loaded for some reason.
*
* @author Stefan Hepp (stefan@stefant.org)
*/
public class MethodRef {
// Implementation Notice:
// At least one of classRef and methodInfo is not null.
// If methodInfo is null, methodName and descriptor must be set.
// MethodInfo can be set lazily by getMethodInfo(). If methodInfo should be checked,
// if (getMethodInfo()==null) must be used, but if only a check is needed if the other fields
// are not null, if (methodInfo==null) is sufficient.
private final ClassRef classRef;
private MethodInfo methodInfo;
private String methodName;
private Descriptor descriptor;
public MethodRef(ClassRef classRef, String methodName, Descriptor descriptor) {
this.classRef = classRef;
this.methodName = methodName;
this.descriptor = descriptor;
methodInfo = null;
}
public MethodRef(MethodInfo methodInfo) {
this.methodInfo = methodInfo;
this.classRef = null;
}
public MethodRef(ClassRef classRef, MethodInfo methodInfo) {
this.methodInfo = methodInfo;
this.classRef = classRef;
}
/**
* Get the referenced method info. If the method is inherited, get the inherited methodInfo,
* if the method is only defined by an implemented interface, get the interface methodInfo.
*
* @see ClassInfo#getMethodInfoInherited(MemberID, boolean)
* @return the referenced method.
*/
public MethodInfo getMethodInfo() {
if (methodInfo == null) {
// We could cache somehow if lookup fails, so we do not need to try again, but then how do we
// know when to try again (e.g. after AppInfo has been updated)
ClassInfo classInfo = classRef.getClassInfo();
if ( classInfo != null ) {
methodInfo = classInfo.getMethodInfoInherited(new MemberID(methodName, descriptor), true);
/*
if ( methodInfo != null && methodInfo.getClassName().equals(classRef.getClassName())) {
// method is defined in the given class
classRef = null;
}
*/
}
}
return methodInfo;
}
public ClassRef getClassRef() {
return classRef != null ? classRef : methodInfo.getClassInfo().getClassRef();
}
public ClassInfo getClassInfo() {
return classRef != null ? classRef.getClassInfo() : methodInfo.getClassInfo();
}
public Descriptor getDescriptor() {
return methodInfo != null ? methodInfo.getDescriptor() : descriptor;
}
public MemberID getMemberID() {
if (classRef != null && methodInfo != null && !classRef.getClassName().equals(methodInfo.getClassName())) {
// we have a MethodRef to a method which is inherited
return new MemberID(classRef.getClassName(), methodInfo.getShortName(), methodInfo.getDescriptor());
}
//noinspection ConstantConditions
return methodInfo != null ? methodInfo.getMemberID()
: new MemberID(classRef.getClassName(), methodName, descriptor);
}
public Ternary isInterfaceMethod() {
// We do not use getMethodInfo() here, else we would run into problems because this method
// is needed during classloading, and resolving the method does not work until classloading is completed.
if ( methodInfo != null ) {
return Ternary.valueOf(methodInfo.getClassInfo().isInterface());
}
return classRef.isInterface();
}
public Ternary exists() {
if ( getMethodInfo() != null ) return Ternary.TRUE;
if ( classRef.getClassInfo() != null ) {
return classRef.getClassInfo().getMethodInfo(getMethodSignature()) != null ?
Ternary.TRUE : Ternary.FALSE;
}
return Ternary.UNKNOWN;
}
public String getName() {
return methodInfo != null ? methodInfo.getShortName() : methodName;
}
public String getClassName() {
return classRef != null ? classRef.getClassName() : methodInfo.getClassInfo().getClassName();
}
public String getMethodSignature() {
if ( methodInfo != null ) {
return methodInfo.getMethodSignature();
}
return methodName + descriptor;
}
public String getFQMethodName() {
return getClassName() + "." + getName();
}
@Override
public int hashCode() {
int hash = getClassName().hashCode();
hash = 31 * hash + getMethodSignature().hashCode();
return hash;
}
/**
* Check if this MethodRef refers to the same method as the given object.
*
* <p>Note that an unresolved method reference and a MethodInfo reference are equal
* if they refer to the same method, but a MethodInfo object is never equal to a MethodRef.</p>
*
* @param obj the object to test.
* @return true if the object is a methodref and refers to the same method.
*/
@Override
public boolean equals(Object obj) {
// we might even allow equality for MethodInfo.getMethodRef(), but then we should
// also test for MethodRef objects in MethodInfo.equals(), which is somehow creepy..
if (!(obj instanceof MethodRef)) {
return false;
}
MethodRef ref = (MethodRef) obj;
return getClassName().equals(ref.getClassName()) && getMethodSignature().equals(ref.getMethodSignature());
}
@Override
public String toString() {
return getMemberID().toString();
}
}