blob: 3dde2632691d9ad7abdde4b8cde70a68b16c71ca [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2008 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.pde.api.tools.internal.search;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.pde.api.tools.internal.provisional.IApiAnnotations;
import org.eclipse.pde.api.tools.internal.provisional.IApiComponent;
import org.eclipse.pde.api.tools.internal.provisional.IClassFile;
import org.eclipse.pde.api.tools.internal.provisional.descriptors.IMethodDescriptor;
import org.eclipse.pde.api.tools.internal.provisional.descriptors.IReferenceTypeDescriptor;
import org.eclipse.pde.api.tools.internal.provisional.search.ILocation;
import org.eclipse.pde.api.tools.internal.provisional.search.IReference;
import org.eclipse.pde.api.tools.internal.provisional.search.ReferenceModifiers;
import org.eclipse.pde.api.tools.internal.util.Util;
/**
* Base Implementation of {@link IReference}
*
* @since 1.0.0
*/
public class Reference implements IReference {
private ILocation target = null;
private ILocation source = null;
private ILocation resolved = null;
private int kind;
private IApiAnnotations description = null;
/**
* Bit mask of method reference kinds that need resolution
*/
private final static int METHODS_TO_RESOLVE =
ReferenceModifiers.REF_VIRTUALMETHOD | // these have to be resolved to see where they are implemented
ReferenceModifiers.REF_OVERRIDE | // resolve inherited methods to see if @noextend
ReferenceModifiers.REF_STATICMETHOD | // resolve static, as could be synthetic
ReferenceModifiers.REF_SPECIALMETHOD | // resolve specials (super)
ReferenceModifiers.REF_INTERFACEMETHOD; // resolves interface methods
/**
* Constructor
* @param source the source of the reference
* @param target the target of the reference
* @param kind the kind of the reference. See {@link IReference} for a listing of kinds
*/
public Reference(ILocation source, ILocation target, int kind) {
this.source = source;
this.target = target;
this.kind = kind;
}
/* (non-Javadoc)
* @see org.eclipse.pde.api.tools.search.IReference#getReferenceKind()
*/
public int getReferenceKind() {
return kind;
}
/* (non-Javadoc)
* @see org.eclipse.pde.api.tools.search.IReference#getSourceLocation()
*/
public ILocation getSourceLocation() {
return source;
}
/* (non-Javadoc)
* @see org.eclipse.pde.api.tools.search.IReference#getTargetApiDescription()
*/
public IApiAnnotations getResolvedAnnotations() {
return description;
}
/* (non-Javadoc)
* @see org.eclipse.pde.api.tools.search.IReference#getTargetLocation()
*/
public ILocation getReferencedLocation() {
return target;
}
/**
* Resolves this reference in the given profile.
*
* @param engine search engine resolving the reference
* @throws CoreException
*/
void resolve(SearchEngine engine) throws CoreException {
if(resolved == null) {
IApiComponent sourceComponent = source.getApiComponent();
if(sourceComponent != null) {
if ((getReferenceKind() & METHODS_TO_RESOLVE) > 0) {
IMethodDescriptor method = (IMethodDescriptor) target.getMember();
resolveVirtualMethod(sourceComponent, method, engine);
} else {
IApiComponent cpackage = Util.getComponent(
sourceComponent.getProfile().resolvePackage(sourceComponent, target.getType().getPackage().getName()),
target.getType().getQualifiedName());
if(cpackage != null) {
ILocation res = new Location(cpackage, target.getMember());
res.setLineNumber(target.getLineNumber());
IApiAnnotations ann = cpackage.getApiDescription().resolveAnnotations(sourceComponent.getId(), res.getMember());
setResolution(ann, res);
}
}
}
}
// TODO: throw exception on failure
}
/**
* Resolves a virtual method and returns whether the method lookup was successful.
* We need to resolve the actual type that implements the method - i.e. do the virtual
* method lookup.
*
* @param profile profile in which method lookup is to be resolved
* @param callSiteComponent the component where the method call site was located
* @param method the method that has been called
* @param engine search engine (used for class file reader cache)
* @returns whether the lookup succeeded
* @throws CoreException if something goes terribly wrong
*/
private boolean resolveVirtualMethod(IApiComponent callSiteComponent, IMethodDescriptor method, SearchEngine engine) throws CoreException {
// resolve the package in which to start the lookup
IApiComponent[] implComponents = callSiteComponent.getProfile().resolvePackage(callSiteComponent, method.getPackage().getName());
String receivingTypeName = method.getEnclosingType().getQualifiedName();
IApiComponent implComponent = Util.getComponent(implComponents, receivingTypeName);
if (implComponent != null) {
IClassFile classFile = implComponent.findClassFile(receivingTypeName);
if (classFile != null) {
MethodExtractor extractor = engine.getExtraction(classFile);
IMethodDescriptor[] methods = extractor.getMethods();
for (int i = 0; i < methods.length; i++) {
IMethodDescriptor methodInfo = methods[i];
if (methodInfo.equals(method)) {
if (methodInfo.isSynthetic()) {
// don't resolve references to synthetic methods
return false;
} else {
ILocation res = new Location(implComponent, method);
IApiAnnotations ann = implComponent.getApiDescription().resolveAnnotations(
getSourceLocation().getApiComponent().getId(), res.getMember());
setResolution(ann, res);
return true;
}
}
}
if (kind == ReferenceModifiers.REF_INTERFACEMETHOD) {
// resolve method in super interfaces rather than class
String[] interaces = extractor.getInteraces();
if (interaces != null) {
for (int i = 0; i < interaces.length; i++) {
IReferenceTypeDescriptor supertype = Util.getType(interaces[i]);
if (resolveVirtualMethod(implComponent, supertype.getMethod(method.getName(), method.getSignature()), engine)) {
return true;
}
}
}
} else {
String superName = extractor.getSuperclassName();
if (superName != null) {
IReferenceTypeDescriptor supertype = Util.getType(superName);
return resolveVirtualMethod(implComponent, supertype.getMethod(method.getName(), method.getSignature()), engine);
}
}
}
}
return false;
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
public String toString() {
String kindstr = Util.getReferenceKind(this.kind);
if(kindstr == null) {
kindstr = "UNKNOWN_KIND"; //$NON-NLS-1$
}
StringBuffer ms = new StringBuffer();
ms.append(source.getMember().toString());
ms.append(" references "); //$NON-NLS-1$
ms.append(target.getMember().toString());
ms.append(" via "); //$NON-NLS-1$
ms.append(kindstr);
int lineNumber = getSourceLocation().getLineNumber();
if (lineNumber != -1) {
ms.append(" [line: "); //$NON-NLS-1$
ms.append(lineNumber);
ms.append("]"); //$NON-NLS-1$
}
return ms.toString();
}
/**
* Sets the resolution of this reference.
*
* @param resolution API description
* @param targetLocation resolved target location
*/
void setResolution(IApiAnnotations resolution, ILocation targetLocation) {
description = resolution;
resolved = targetLocation;
}
/* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
public boolean equals(Object obj) {
if (obj instanceof Reference) {
Reference ref = (Reference) obj;
return getReferenceKind() == ref.getReferenceKind() &&
equalOrNull(getResolvedLocation(), ref.getResolvedLocation()) &&
getSourceLocation().equals(ref.getSourceLocation()) &&
getReferencedLocation().equals(ref.getReferencedLocation()) &&
Util.equalsOrNull(getResolvedAnnotations(), ref.getResolvedAnnotations());
}
return false;
}
private boolean equalOrNull(ILocation l1, ILocation l2) {
if (l1 == null) {
return l2 == null;
}
return l1.equals(l2);
}
/* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
public int hashCode() {
return kind + source.hashCode() + target.hashCode();
}
/* (non-Javadoc)
* @see org.eclipse.pde.api.tools.internal.provisional.search.IReference#getResolvedLocation()
*/
public ILocation getResolvedLocation() {
return resolved;
}
}