blob: 93a3d5f81092d28526f8df218be56245bfad6b95 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2004 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.core;
import java.util.ArrayList;
import org.eclipse.jdt.core.Flags;
import org.eclipse.jdt.core.IField;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.ISourceRange;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.Signature;
import org.eclipse.jdt.core.compiler.*;
import org.eclipse.jdt.core.compiler.IProblem;
import org.eclipse.jdt.internal.codeassist.ISelectionRequestor;
import org.eclipse.jdt.internal.codeassist.SelectionEngine;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
import org.eclipse.jdt.internal.core.util.HandleFactory;
/**
* Implementation of <code>ISelectionRequestor</code> to assist with
* code resolve in a compilation unit. Translates names to elements.
*/
public class SelectionRequestor implements ISelectionRequestor {
/*
* The name lookup facility used to resolve packages
*/
protected NameLookup nameLookup;
/*
* The compilation unit or class file we are resolving in
*/
protected Openable openable;
/*
* The collection of resolved elements.
*/
protected IJavaElement[] elements = JavaElement.NO_ELEMENTS;
protected int elementIndex = -1;
protected HandleFactory handleFactory = new HandleFactory();
/**
* Creates a selection requestor that uses that given
* name lookup facility to resolve names.
*
* Fix for 1FVXGDK
*/
public SelectionRequestor(NameLookup nameLookup, Openable openable) {
super();
this.nameLookup = nameLookup;
this.openable = openable;
}
/**
* Resolve the binary method
*
* fix for 1FWFT6Q
*/
protected void acceptBinaryMethod(IType type, char[] selector, char[][] parameterPackageNames, char[][] parameterTypeNames) {
String[] parameterTypes= null;
if (parameterTypeNames != null) {
parameterTypes= new String[parameterTypeNames.length];
for (int i= 0, max = parameterTypeNames.length; i < max; i++) {
String pkg = IPackageFragment.DEFAULT_PACKAGE_NAME;
if (parameterPackageNames[i] != null && parameterPackageNames[i].length > 0) {
pkg = new String(parameterPackageNames[i]) + "."; //$NON-NLS-1$
}
String typeName = new String(parameterTypeNames[i]);
if (typeName.indexOf('.') > 0)
typeName = typeName.replace('.', '$');
parameterTypes[i]= Signature.createTypeSignature(
pkg + typeName, true);
}
}
IMethod method= type.getMethod(new String(selector), parameterTypes);
if (method.exists()) {
addElement(method);
if(SelectionEngine.DEBUG){
System.out.print("SELECTION - accept method("); //$NON-NLS-1$
System.out.print(method.toString());
System.out.println(")"); //$NON-NLS-1$
}
}
}
/**
* Resolve the class.
*/
public void acceptClass(char[] packageName, char[] className, boolean needQualification, boolean isDeclaration, int start, int end) {
acceptType(packageName, className, NameLookup.ACCEPT_CLASSES, needQualification, isDeclaration, start, end);
}
/**
* @see ISelectionRequestor#acceptError
*/
public void acceptError(IProblem error) {
// do nothing
}
/**
* Resolve the field.
*/
public void acceptField(char[] declaringTypePackageName, char[] declaringTypeName, char[] name, boolean isDeclaration, int start, int end) {
if(isDeclaration) {
IType type= resolveTypeByLocation(declaringTypePackageName, declaringTypeName,
NameLookup.ACCEPT_CLASSES | NameLookup.ACCEPT_INTERFACES,
start, end);
if(type != null) {
try {
IField[] fields = type.getFields();
for (int i = 0; i < fields.length; i++) {
IField field = fields[i];
ISourceRange range = field.getNameRange();
if(range.getOffset() <= start
&& range.getOffset() + range.getLength() >= end
&& field.getElementName().equals(new String(name))) {
addElement(fields[i]);
if(SelectionEngine.DEBUG){
System.out.print("SELECTION - accept field("); //$NON-NLS-1$
System.out.print(field.toString());
System.out.println(")"); //$NON-NLS-1$
}
return; // only one method is possible
}
}
} catch (JavaModelException e) {
return;
}
}
} else {
IType type= resolveType(declaringTypePackageName, declaringTypeName,
NameLookup.ACCEPT_CLASSES | NameLookup.ACCEPT_INTERFACES);
if (type != null) {
IField field= type.getField(new String(name));
if (field.exists()) {
addElement(field);
if(SelectionEngine.DEBUG){
System.out.print("SELECTION - accept field("); //$NON-NLS-1$
System.out.print(field.toString());
System.out.println(")"); //$NON-NLS-1$
}
}
}
}
}
/**
* Resolve the interface
*/
public void acceptInterface(char[] packageName, char[] interfaceName, boolean needQualification, boolean isDeclaration, int start, int end) {
acceptType(packageName, interfaceName, NameLookup.ACCEPT_INTERFACES, needQualification, isDeclaration, start, end);
}
public void acceptLocalField(SourceTypeBinding typeBinding, char[] name, CompilationUnitDeclaration parsedUnit) {
IType type = (IType)this.handleFactory.createElement(typeBinding.scope.referenceContext, parsedUnit, this.openable);
if (type != null) {
IField field= type.getField(new String(name));
if (field.exists()) {
addElement(field);
if(SelectionEngine.DEBUG){
System.out.print("SELECTION - accept field("); //$NON-NLS-1$
System.out.print(field.toString());
System.out.println(")"); //$NON-NLS-1$
}
}
}
}
public void acceptLocalMethod(SourceTypeBinding typeBinding, char[] selector, char[][] parameterPackageNames, char[][] parameterTypeNames, boolean isConstructor, CompilationUnitDeclaration parsedUnit, boolean isDeclaration, int start, int end) {
IType type = (IType)this.handleFactory.createElement(typeBinding.scope.referenceContext, parsedUnit, this.openable);
// fix for 1FWFT6Q
if (type != null) {
if (type.isBinary()) {
// need to add a paramater for constructor in binary type
IType declaringDeclaringType = type.getDeclaringType();
boolean isStatic = false;
try {
isStatic = Flags.isStatic(type.getFlags());
} catch (JavaModelException e) {
// isStatic == false
}
if(declaringDeclaringType != null && isConstructor && !isStatic) {
int length = parameterPackageNames.length;
System.arraycopy(parameterPackageNames, 0, parameterPackageNames = new char[length+1][], 1, length);
System.arraycopy(parameterTypeNames, 0, parameterTypeNames = new char[length+1][], 1, length);
parameterPackageNames[0] = declaringDeclaringType.getPackageFragment().getElementName().toCharArray();
parameterTypeNames[0] = declaringDeclaringType.getTypeQualifiedName().toCharArray();
}
acceptBinaryMethod(type, selector, parameterPackageNames, parameterTypeNames);
} else {
acceptSourceMethod(type, selector, parameterPackageNames, parameterTypeNames);
}
}
}
public void acceptLocalType(SourceTypeBinding typeBinding, CompilationUnitDeclaration parsedUnit) {
IJavaElement type = this.handleFactory.createElement(typeBinding.scope.referenceContext, parsedUnit, this.openable);
if (type != null) {
addElement(type);
if(SelectionEngine.DEBUG){
System.out.print("SELECTION - accept local type("); //$NON-NLS-1$
System.out.print(type.toString());
System.out.println(")"); //$NON-NLS-1$
}
}
}
public void acceptLocalVariable(LocalVariableBinding binding, CompilationUnitDeclaration parsedUnit) {
IJavaElement localVar = this.handleFactory.createElement(binding.declaration, parsedUnit, this.openable);
if (localVar != null) {
addElement(localVar);
if(SelectionEngine.DEBUG){
System.out.print("SELECTION - accept local variable("); //$NON-NLS-1$
System.out.print(localVar.toString());
System.out.println(")"); //$NON-NLS-1$
}
}
}
/**
* Resolve the method
*/
public void acceptMethod(char[] declaringTypePackageName, char[] declaringTypeName, char[] selector, char[][] parameterPackageNames, char[][] parameterTypeNames, boolean isConstructor, boolean isDeclaration, int start, int end) {
if(isDeclaration) {
IType type = resolveTypeByLocation(declaringTypePackageName, declaringTypeName,
NameLookup.ACCEPT_CLASSES | NameLookup.ACCEPT_INTERFACES,
start, end);
if(type != null) {
this.acceptMethodDeclaration(type, selector, start, end);
}
} else {
IType type = resolveType(declaringTypePackageName, declaringTypeName,
NameLookup.ACCEPT_CLASSES | NameLookup.ACCEPT_INTERFACES);
// fix for 1FWFT6Q
if (type != null) {
if (type.isBinary()) {
// need to add a paramater for constructor in binary type
IType declaringDeclaringType = type.getDeclaringType();
boolean isStatic = false;
try {
isStatic = Flags.isStatic(type.getFlags());
} catch (JavaModelException e) {
// isStatic == false
}
if(declaringDeclaringType != null && isConstructor && !isStatic) {
int length = parameterPackageNames.length;
System.arraycopy(parameterPackageNames, 0, parameterPackageNames = new char[length+1][], 1, length);
System.arraycopy(parameterTypeNames, 0, parameterTypeNames = new char[length+1][], 1, length);
parameterPackageNames[0] = declaringDeclaringType.getPackageFragment().getElementName().toCharArray();
parameterTypeNames[0] = declaringDeclaringType.getTypeQualifiedName().toCharArray();
}
acceptBinaryMethod(type, selector, parameterPackageNames, parameterTypeNames);
} else {
acceptSourceMethod(type, selector, parameterPackageNames, parameterTypeNames);
}
}
}
}
/**
* Resolve the package
*/
public void acceptPackage(char[] packageName) {
IPackageFragment[] pkgs = this.nameLookup.findPackageFragments(new String(packageName), false);
if (pkgs != null) {
for (int i = 0, length = pkgs.length; i < length; i++) {
addElement(pkgs[i]);
if(SelectionEngine.DEBUG){
System.out.print("SELECTION - accept package("); //$NON-NLS-1$
System.out.print(pkgs[i].toString());
System.out.println(")"); //$NON-NLS-1$
}
}
}
}
/**
* Resolve the source method
*
* fix for 1FWFT6Q
*/
protected void acceptSourceMethod(IType type, char[] selector, char[][] parameterPackageNames, char[][] parameterTypeNames) {
String name = new String(selector);
IMethod[] methods = null;
try {
methods = type.getMethods();
for (int i = 0; i < methods.length; i++) {
if (methods[i].getElementName().equals(name)
&& methods[i].getParameterTypes().length == parameterTypeNames.length) {
addElement(methods[i]);
}
}
} catch (JavaModelException e) {
return;
}
// if no matches, nothing to report
if (this.elementIndex == -1) {
// no match was actually found, but a method was originally given -> default constructor
addElement(type);
if(SelectionEngine.DEBUG){
System.out.print("SELECTION - accept type("); //$NON-NLS-1$
System.out.print(type.toString());
System.out.println(")"); //$NON-NLS-1$
}
return;
}
// if there is only one match, we've got it
if (this.elementIndex == 0) {
if(SelectionEngine.DEBUG){
System.out.print("SELECTION - accept method("); //$NON-NLS-1$
System.out.print(this.elements[0].toString());
System.out.println(")"); //$NON-NLS-1$
}
return;
}
// more than one match - must match simple parameter types
IJavaElement[] matches = this.elements;
int matchesIndex = this.elementIndex;
this.elements = JavaElement.NO_ELEMENTS;
this.elementIndex = -1;
for (int i = 0; i <= matchesIndex; i++) {
IMethod method= (IMethod)matches[i];
String[] signatures = method.getParameterTypes();
boolean match= true;
for (int p = 0; p < signatures.length; p++) {
String simpleName= Signature.getSimpleName(Signature.toString(signatures[p]));
char[] simpleParameterName = CharOperation.lastSegment(parameterTypeNames[p], '.');
if (!simpleName.equals(new String(simpleParameterName))) {
match = false;
break;
}
}
if (match) {
addElement(method);
if(SelectionEngine.DEBUG){
System.out.print("SELECTION - accept method("); //$NON-NLS-1$
System.out.print(method.toString());
System.out.println(")"); //$NON-NLS-1$
}
}
}
}
protected void acceptMethodDeclaration(IType type, char[] selector, int start, int end) {
String name = new String(selector);
IMethod[] methods = null;
try {
methods = type.getMethods();
for (int i = 0; i < methods.length; i++) {
ISourceRange range = methods[i].getNameRange();
if(range.getOffset() <= start
&& range.getOffset() + range.getLength() >= end
&& methods[i].getElementName().equals(name)) {
addElement(methods[i]);
if(SelectionEngine.DEBUG){
System.out.print("SELECTION - accept method("); //$NON-NLS-1$
System.out.print(this.elements[0].toString());
System.out.println(")"); //$NON-NLS-1$
}
return; // only one method is possible
}
}
} catch (JavaModelException e) {
return;
}
// no match was actually found
addElement(type);
if(SelectionEngine.DEBUG){
System.out.print("SELECTION - accept type("); //$NON-NLS-1$
System.out.print(type.toString());
System.out.println(")"); //$NON-NLS-1$
}
return;
}
/**
* Resolve the type, adding to the resolved elements.
*/
protected void acceptType(char[] packageName, char[] typeName, int acceptFlags, boolean needQualification, boolean isDeclaration, int start, int end) {
IType type = null;
if(isDeclaration) {
type = resolveTypeByLocation(packageName, typeName, acceptFlags, start, end);
} else {
type = resolveType(packageName, typeName, acceptFlags);
}
if (type != null) {
addElement(type);
if(SelectionEngine.DEBUG){
System.out.print("SELECTION - accept type("); //$NON-NLS-1$
System.out.print(type.toString());
System.out.println(")"); //$NON-NLS-1$
}
}
}
/*
* Adds the given element to the list of resolved elements.
*/
protected void addElement(IJavaElement element) {
int elementLength = this.elementIndex + 1;
if (elementLength == this.elements.length) {
System.arraycopy(this.elements, 0, this.elements = new IJavaElement[(elementLength*2) + 1], 0, elementLength);
}
this.elements[++this.elementIndex] = element;
}
/**
* Returns the resolved elements.
*/
public IJavaElement[] getElements() {
int elementLength = this.elementIndex + 1;
if (this.elements.length != elementLength) {
System.arraycopy(this.elements, 0, this.elements = new IJavaElement[elementLength], 0, elementLength);
}
return this.elements;
}
/**
* Resolve the type
*/
protected IType resolveType(char[] packageName, char[] typeName, int acceptFlags) {
IType type= null;
if (this.openable instanceof CompilationUnit && ((CompilationUnit)this.openable).isWorkingCopy()) {
CompilationUnit wc = (CompilationUnit) this.openable;
try {
if(((packageName == null || packageName.length == 0) && wc.getPackageDeclarations().length == 0) ||
(!(packageName == null || packageName.length == 0) && wc.getPackageDeclaration(new String(packageName)).exists())) {
char[][] compoundName = CharOperation.splitOn('.', typeName);
if(compoundName.length > 0) {
type = wc.getType(new String(compoundName[0]));
for (int i = 1, length = compoundName.length; i < length; i++) {
type = type.getType(new String(compoundName[i]));
}
}
if(type != null && !type.exists()) {
type = null;
}
}
}catch (JavaModelException e) {
type = null;
}
}
if(type == null) {
IPackageFragment[] pkgs = this.nameLookup.findPackageFragments(
(packageName == null || packageName.length == 0) ? IPackageFragment.DEFAULT_PACKAGE_NAME : new String(packageName),
false);
// iterate type lookup in each package fragment
for (int i = 0, length = pkgs == null ? 0 : pkgs.length; i < length; i++) {
type= this.nameLookup.findType(new String(typeName), pkgs[i], false, acceptFlags);
if (type != null) break;
}
if (type == null) {
String pName= IPackageFragment.DEFAULT_PACKAGE_NAME;
if (packageName != null) {
pName = new String(packageName);
}
if (this.openable != null && this.openable.getParent().getElementName().equals(pName)) {
// look inside the type in which we are resolving in
String tName= new String(typeName);
tName = tName.replace('.','$');
IType[] allTypes= null;
try {
ArrayList list = this.openable.getChildrenOfType(IJavaElement.TYPE);
allTypes = new IType[list.size()];
list.toArray(allTypes);
} catch (JavaModelException e) {
return null;
}
for (int i= 0; i < allTypes.length; i++) {
if (allTypes[i].getTypeQualifiedName().equals(tName)) {
return allTypes[i];
}
}
}
}
}
return type;
}
protected IType resolveTypeByLocation(char[] packageName, char[] typeName, int acceptFlags, int start, int end) {
IType type= null;
// TODO (david) post 3.0 should remove isOpen check, and investigate reusing ICompilationUnit#getElementAt. may need to optimize #getElementAt to remove recursions
if (this.openable instanceof CompilationUnit && ((CompilationUnit)this.openable).isOpen()) {
CompilationUnit wc = (CompilationUnit) this.openable;
try {
if(((packageName == null || packageName.length == 0) && wc.getPackageDeclarations().length == 0) ||
(!(packageName == null || packageName.length == 0) && wc.getPackageDeclaration(new String(packageName)).exists())) {
char[][] compoundName = CharOperation.splitOn('.', typeName);
if(compoundName.length > 0) {
IType[] tTypes = wc.getTypes();
int i = 0;
int depth = 0;
done : while(i < tTypes.length) {
ISourceRange range = tTypes[i].getSourceRange();
if(range.getOffset() <= start
&& range.getOffset() + range.getLength() >= end
&& tTypes[i].getElementName().equals(new String(compoundName[depth]))) {
if(depth == compoundName.length - 1) {
type = tTypes[i];
break done;
}
tTypes = tTypes[i].getTypes();
i = 0;
depth++;
continue done;
}
i++;
}
}
if(type != null && !type.exists()) {
type = null;
}
}
}catch (JavaModelException e) {
type = null;
}
}
if(type == null) {
IPackageFragment[] pkgs = this.nameLookup.findPackageFragments(
(packageName == null || packageName.length == 0) ? IPackageFragment.DEFAULT_PACKAGE_NAME : new String(packageName),
false);
// iterate type lookup in each package fragment
for (int i = 0, length = pkgs == null ? 0 : pkgs.length; i < length; i++) {
type= this.nameLookup.findType(new String(typeName), pkgs[i], false, acceptFlags);
if (type != null) break;
}
if (type == null) {
String pName= IPackageFragment.DEFAULT_PACKAGE_NAME;
if (packageName != null) {
pName = new String(packageName);
}
if (this.openable != null && this.openable.getParent().getElementName().equals(pName)) {
// look inside the type in which we are resolving in
String tName= new String(typeName);
tName = tName.replace('.','$');
IType[] allTypes= null;
try {
ArrayList list = this.openable.getChildrenOfType(IJavaElement.TYPE);
allTypes = new IType[list.size()];
list.toArray(allTypes);
} catch (JavaModelException e) {
return null;
}
for (int i= 0; i < allTypes.length; i++) {
if (allTypes[i].getTypeQualifiedName().equals(tName)) {
return allTypes[i];
}
}
}
}
}
return type;
}
}