blob: 51f587bd8b143467108f23b1c047626ae8e82afd [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2016 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
*
* This is an implementation of an early-draft specification developed under the Java
* Community Process (JCP) and is made available for testing and evaluation purposes
* only. The code is not compatible with any specification of the JCP.
*
* Contributors:
* IBM Corporation - initial API and implementation
* Lukas Hanke <hanke@yatta.de> - [templates][content assist] Content assist for 'for' loop should suggest member variables - https://bugs.eclipse.org/117215
*******************************************************************************/
package org.eclipse.jdt.internal.corext.template.java;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jdt.core.CompletionProposal;
import org.eclipse.jdt.core.CompletionRequestor;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.ITypeHierarchy;
import org.eclipse.jdt.core.ITypeParameter;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.Signature;
import org.eclipse.jdt.core.compiler.IProblem;
import org.eclipse.jdt.internal.ui.JavaPlugin;
/**
* A completion requester to collect informations on local variables.
* This class is used for guessing variable names like arrays, collections, etc.
*/
final class CompilationUnitCompletion extends CompletionRequestor {
/**
* Describes a local variable (including parameters) inside the method where
* code completion was invoked. Special predicates exist to query whether
* a variable can be iterated over.
*/
public final class Variable {
private static final int UNKNOWN= 0, NONE= 0;
private static final int ARRAY= 1;
private static final int COLLECTION= 2;
private static final int ITERABLE= 4;
/**
* The name of the local variable.
*/
private final String name;
/**
* The signature of the local variable's type.
*/
private final String signature;
/* lazily computed properties */
private int fType= UNKNOWN;
private int fChecked= NONE;
private String[] fMemberTypes;
private Variable(String name, String signature) {
this.name= name;
this.signature= signature;
}
/**
* Returns the name of the variable.
*
* @return the name of the variable
*/
public String getName() {
return name;
}
/**
* Returns <code>true</code> if the type of the local variable is an
* array type.
*
* @return <code>true</code> if the receiver's type is an array,
* <code>false</code> if not
*/
public boolean isArray() {
if (fType == UNKNOWN && (fChecked & ARRAY) == 0 && Signature.getTypeSignatureKind(signature) == Signature.ARRAY_TYPE_SIGNATURE)
fType= ARRAY;
fChecked |= ARRAY;
return fType == ARRAY;
}
/**
* Returns <code>true</code> if the receiver's type is a subclass of
* <code>java.util.Collection</code>, <code>false</code> otherwise.
*
* @return <code>true</code> if the receiver's type is a subclass of
* <code>java.util.Collection</code>, <code>false</code>
* otherwise
*/
public boolean isCollection() {
// Collection extends Iterable
if ((fType == UNKNOWN || fType == ITERABLE) && (fChecked & COLLECTION) == 0 && isSubtypeOf("java.util.Collection")) //$NON-NLS-1$
fType= COLLECTION;
fChecked |= COLLECTION;
return fType == COLLECTION;
}
/**
* Returns <code>true</code> if the receiver's type is a subclass of
* <code>java.lang.Iterable</code>, <code>false</code> otherwise.
*
* @return <code>true</code> if the receiver's type is a subclass of
* <code>java.lang.Iterable</code>, <code>false</code>
* otherwise
*/
public boolean isIterable() {
if (fType == UNKNOWN && (fChecked & ITERABLE) == 0 && isSubtypeOf("java.lang.Iterable")) //$NON-NLS-1$
fType= ITERABLE;
fChecked |= ITERABLE;
return fType == ITERABLE || fType == COLLECTION; // Collection extends Iterable
}
/**
* Returns <code>true</code> if the receiver's type is an implementor
* of <code>interfaceName</code>.
*
* @param supertype the fully qualified name of the interface
* @return <code>true</code> if the receiver's type implements the
* type named <code>interfaceName</code>
*/
private boolean isSubtypeOf(String supertype) {
String implementorName= SignatureUtil.stripSignatureToFQN(signature);
if (implementorName.length() == 0)
return false;
int implementorDims= Signature.getArrayCount(signature);
int superDimsIndex= supertype.indexOf("[]"); //$NON-NLS-1$
int superDims;
if (superDimsIndex != -1) {
superDims= (supertype.length() - superDimsIndex) / 2;
supertype= supertype.substring(0, superDimsIndex);
} else {
superDims= 0;
}
if (implementorDims > superDims) {
return "java.lang.Object".equals(supertype); //$NON-NLS-1$
} else if (superDims != implementorDims) {
return false;
}
boolean qualified= supertype.indexOf('.') != -1;
// try cheap test first
if (implementorName.equals(supertype) || !qualified && Signature.getSimpleName(implementorName).equals(supertype))
return true;
if (fUnit == null)
return false;
IJavaProject project= fUnit.getJavaProject();
try {
IType sub= project.findType(implementorName);
if (sub == null)
return false;
if (qualified) {
IType sup= project.findType(supertype);
if (sup == null)
return false;
ITypeHierarchy hierarchy= sub.newSupertypeHierarchy(null);
return hierarchy.contains(sup);
} else {
ITypeHierarchy hierarchy= sub.newSupertypeHierarchy(null);
IType[] allTypes= hierarchy.getAllTypes();
for (int i= 0; i < allTypes.length; i++) {
IType type= allTypes[i];
if (type.getElementName().equals(supertype))
return true;
}
}
} catch (JavaModelException e) {
// ignore and return false
}
return false;
}
private IType[] getSupertypes(String supertype) {
IType[] empty= new IType[0];
String implementorName= SignatureUtil.stripSignatureToFQN(signature);
if (implementorName.length() == 0)
return empty;
boolean qualified= supertype.indexOf('.') != -1;
if (fUnit == null)
return empty;
IJavaProject project= fUnit.getJavaProject();
try {
IType sub= project.findType(implementorName);
if (sub == null)
return empty;
if (qualified) {
IType sup= project.findType(supertype);
if (sup == null)
return empty;
return new IType[] {sup};
} else {
ITypeHierarchy hierarchy= sub.newSupertypeHierarchy(null);
IType[] allTypes= hierarchy.getAllTypes();
List<IType> matches= new ArrayList<>();
for (int i= 0; i < allTypes.length; i++) {
IType type= allTypes[i];
if (type.getElementName().equals(supertype))
matches.add(type);
}
return matches.toArray(new IType[matches.size()]);
}
} catch (JavaModelException e) {
// ignore and return false
}
return empty;
}
/**
* Returns the signature of the member type.
*
* @return the signature of the member type
*/
public String getMemberTypeSignature() {
return getMemberTypeSignatures()[0];
}
/**
* Returns the signatures of all member type bounds.
*
* @return the signatures of all member type bounds
*/
public String[] getMemberTypeSignatures() {
if (isArray()) {
return new String[] {Signature.createArraySignature(Signature.getElementType(signature), Signature.getArrayCount(signature) - 1)};
} else if (fUnit != null && (isIterable() || isCollection())) {
if (fMemberTypes == null) {
try {
try {
TypeParameterResolver util= new TypeParameterResolver(this);
fMemberTypes= util.computeBinding("java.lang.Iterable", 0); //$NON-NLS-1$
} catch (JavaModelException e) {
try {
TypeParameterResolver util= new TypeParameterResolver(this);
fMemberTypes= util.computeBinding("java.util.Collection", 0); //$NON-NLS-1$
} catch (JavaModelException x) {
fMemberTypes= new String[0];
}
}
} catch (IndexOutOfBoundsException e) {
fMemberTypes= new String[0];
}
}
if (fMemberTypes.length > 0)
return fMemberTypes;
}
return new String[] {Signature.createTypeSignature("java.lang.Object", true)}; //$NON-NLS-1$
}
/**
* Returns the type names of all member type bounds, as they would be
* appear when referenced in the current compilation unit.
*
* @return type names of all member type bounds
*/
public String[] getMemberTypeNames() {
String[] signatures= getMemberTypeSignatures();
String[] names= new String[signatures.length];
for (int i= 0; i < signatures.length; i++) {
String sig= signatures[i];
String local= fLocalTypes.get(Signature.getElementType(sig));
int dim= Signature.getArrayCount(sig);
if (local != null && dim > 0) {
StringBuffer array= new StringBuffer(local);
for (int j= 0; j < dim; j++)
array.append("[]"); //$NON-NLS-1$
local= array.toString();
}
if (local != null)
names[i]= local;
else
names[i]= Signature.getSimpleName(Signature.getSignatureSimpleName(sig));
}
return names;
}
/**
* Returns the type arguments of the declared type of the variable. Returns
* an empty array if it is not a parameterized type.
*
* @param type the fully qualified type name of which to match a type argument
* @param index the index of the type parameter in the type
* @return the type bounds for the specified type argument in this local variable
* @since 3.3
*/
public String[] getTypeArgumentBoundSignatures(String type, int index) {
List<String> all= new ArrayList<>();
IType[] supertypes= getSupertypes(type);
if (fUnit != null) {
for (int i= 0; i < supertypes.length; i++) {
try {
TypeParameterResolver util= new TypeParameterResolver(this);
String[] result= util.computeBinding(supertypes[i].getFullyQualifiedName(), index);
all.addAll(Arrays.asList(result));
} catch (JavaModelException e) {
} catch (IndexOutOfBoundsException e) {
}
}
}
if (all.isEmpty())
return new String[] {Signature.createTypeSignature("java.lang.Object", true)}; //$NON-NLS-1$
return all.toArray(new String[all.size()]);
}
/*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
String type;
switch (fType) {
case ITERABLE:
type= "ITERABLE"; //$NON-NLS-1$
break;
case COLLECTION:
type= "COLLECTION"; //$NON-NLS-1$
break;
case ARRAY:
type= "ARRAY"; //$NON-NLS-1$
break;
default:
type= "UNKNOWN"; //$NON-NLS-1$
break;
}
return "LocalVariable [name=\"" + name + "\" signature=\"" + signature + "\" type=\"" + type + "\" member=\"" + getMemberTypeSignature() + "\"]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
}
}
/**
* Given a java type, a resolver computes the bounds of type variables
* declared in a super type, considering any type constraints along the
* inheritance path.
*/
private final class TypeParameterResolver {
private static final String OBJECT_SIGNATURE= "Ljava.lang.Object;"; //$NON-NLS-1$
private final ITypeHierarchy fHierarchy;
private final Variable fVariable;
private final IType fType;
private final List<String> fBounds= new ArrayList<>();
/**
* Creates a new type parameter resolver to compute the bindings of type
* parameters for the declared type of <code>variable</code>. For any
* super type of the type of <code>variable</code>, calling
* {@link #computeBinding(IType, int) computeBinding} will find the type
* bounds of type variables in the super type, considering any type
* constraints along the inheritance path.
*
* @param variable the local variable under investigation
* @throws JavaModelException if the type of <code>variable</code>
* cannot be found
*/
public TypeParameterResolver(Variable variable) throws JavaModelException {
String typeName= SignatureUtil.stripSignatureToFQN(variable.signature);
IJavaProject project= fUnit.getJavaProject();
fType= project.findType(typeName);
fHierarchy= fType.newSupertypeHierarchy(null);
fVariable= variable;
}
/**
* Given a type parameter of <code>superType</code> at position
* <code>index</code>, this method computes and returns the (lower)
* type bound(s) of that parameter for an instance of <code>fType</code>.
* <p>
* <code>superType</code> must be a super type of <code>fType</code>,
* and <code>superType</code> must have at least
* <code>index + 1</code> type parameters.
* </p>
*
* @param superType the qualified type name of the super type to compute
* the type parameter binding for
* @param index the index into the list of type parameters of
* <code>superType</code>
* @return the binding
* @throws JavaModelException if any java model operation fails
* @throws IndexOutOfBoundsException if the index is not valid
*/
public String[] computeBinding(String superType, int index) throws JavaModelException, IndexOutOfBoundsException {
IJavaProject project= fUnit.getJavaProject();
IType type= project.findType(superType);
if (type == null)
throw new JavaModelException(new CoreException(new Status(IStatus.ERROR, JavaPlugin.getPluginId(), IStatus.OK, "No such type", null))); //$NON-NLS-1$
return computeBinding(type, index);
}
/**
* Given a type parameter of <code>superType</code> at position
* <code>index</code>, this method computes and returns the (lower)
* type bound(s) of that parameter for an instance of <code>fType</code>.
* <p>
* <code>superType</code> must be a super type of <code>fType</code>,
* and <code>superType</code> must have at least
* <code>index + 1</code> type parameters.
* </p>
*
* @param superType the super type to compute the type parameter binding
* for
* @param index the index into the list of type parameters of
* <code>superType</code>
* @return the binding
* @throws JavaModelException if any java model operation fails
* @throws IndexOutOfBoundsException if the index is not valid
*/
public String[] computeBinding(IType superType, int index) throws JavaModelException, IndexOutOfBoundsException {
initBounds();
computeTypeParameterBinding(superType, index);
return fBounds.toArray(new String[fBounds.size()]);
}
/**
* Given a type parameter of <code>superType</code> at position
* <code>index</code>, this method recursively computes the (lower)
* type bound(s) of that parameter for an instance of <code>fType</code>.
* <p>
* <code>superType</code> must be a super type of <code>fType</code>,
* and <code>superType</code> must have at least
* <code>index + 1</code> type parameters.
* </p>
* <p>
* The type bounds are stored in <code>fBounds</code>.
* </p>
*
* @param superType the super type to compute the type parameter binding
* for
* @param index the index into the list of type parameters of
* <code>superType</code>
* @throws JavaModelException if any java model operation fails
* @throws IndexOutOfBoundsException if the index is not valid
*/
private void computeTypeParameterBinding(final IType superType, final int index) throws JavaModelException, IndexOutOfBoundsException {
int nParameters= superType.getTypeParameters().length;
if (nParameters <= index)
throw new IndexOutOfBoundsException();
IType[] subTypes= fHierarchy.getSubtypes(superType);
if (subTypes.length == 0) {
// we have reached down to the base type
Assert.isTrue(superType.equals(fType));
String match= findMatchingTypeArgument(fVariable.signature, index, fUnit.findPrimaryType());
String bound= SignatureUtil.getUpperBound(match);
// use the match whether it is a concrete type or not - if not,
// the generic type will at least be in visible in our context
// and can be referenced
addBound(bound);
return;
}
IType subType= subTypes[0]; // take the first, as they all lead to fType
String signature= findMatchingSuperTypeSignature(subType, superType);
String match= findMatchingTypeArgument(signature, index, subType);
if (isConcreteType(match, subType)) {
addBound(match);
return;
}
ITypeParameter[] typeParameters= subType.getTypeParameters();
for (int k= 0; k < typeParameters.length; k++) {
ITypeParameter formalParameter= typeParameters[k];
if (formalParameter.getElementName().equals(SignatureUtil.stripSignatureToFQN(match))) {
String[] bounds= formalParameter.getBounds();
for (int i= 0; i < bounds.length; i++) {
String boundSignature= Signature.createTypeSignature(bounds[i], true);
addBound(SignatureUtil.qualifySignature(boundSignature, subType));
}
computeTypeParameterBinding(subType, k);
return;
}
}
// We have a non-concrete type argument T, but no matching type
// parameter in the sub type. This can happen if T is declared in
// the enclosing type. Since it the declaration is probably visible
// then, its fine to simply copy the match to the bounds and return.
addBound(match);
return;
}
/**
* Finds and returns the type argument with index <code>index</code>
* in the given type super type signature. If <code>signature</code>
* is a generic signature, the type parameter at <code>index</code> is
* extracted. If the type parameter is an upper bound (<code>? super SomeType</code>),
* the type signature of <code>java.lang.Object</code> is returned.
* <p>
* Also, if <code>signature</code> has no type parameters (i.e. is a
* reference to the raw type), the type signature of
* <code>java.lang.Object</code> is returned.
* </p>
*
* @param signature the super type signature from a type's
* <code>extends</code> or <code>implements</code> clause
* @param index the index of the type parameter to extract from
* <code>signature</code>
* @param context the type context inside which unqualified types should
* be resolved
* @return the type argument signature of the type parameter at
* <code>index</code> in <code>signature</code>
* @throws IndexOutOfBoundsException if the index is not valid
*/
private String findMatchingTypeArgument(String signature, int index, IType context) throws IndexOutOfBoundsException {
String[] typeArguments= Signature.getTypeArguments(signature);
if (typeArguments.length > 0 && typeArguments.length <= index)
throw new IndexOutOfBoundsException();
if (typeArguments.length == 0) {
// raw binding - bound to Object
return OBJECT_SIGNATURE;
} else {
String bound= SignatureUtil.getUpperBound(typeArguments[index]);
return SignatureUtil.qualifySignature(bound, context);
}
}
/**
* Finds and returns the super type signature in the
* <code>extends</code> or <code>implements</code> clause of
* <code>subType</code> that corresponds to <code>superType</code>.
*
* @param subType a direct and true sub type of <code>superType</code>
* @param superType a direct super type (super class or interface) of
* <code>subType</code>
* @return the super type signature of <code>subType</code> referring
* to <code>superType</code>
* @throws JavaModelException if extracting the super type signatures
* fails, or if <code>subType</code> contains no super type
* signature to <code>superType</code>
*/
private String findMatchingSuperTypeSignature(IType subType, IType superType) throws JavaModelException {
String[] signatures= getSuperTypeSignatures(subType, superType);
for (int i= 0; i < signatures.length; i++) {
String signature= signatures[i];
String qualified= SignatureUtil.qualifySignature(signature, subType);
String subFQN= SignatureUtil.stripSignatureToFQN(qualified);
String superFQN= superType.getFullyQualifiedName();
if (subFQN.equals(superFQN)) {
return signature;
}
// handle local types
if (fLocalTypes.containsValue(subFQN)) {
return signature;
}
}
throw new JavaModelException(new CoreException(new Status(IStatus.ERROR, JavaPlugin.getPluginId(), IStatus.OK, "Illegal hierarchy", null))); //$NON-NLS-1$
}
/**
* Returns the super interface signatures of <code>subType</code> if
* <code>superType</code> is an interface, otherwise returns the super
* type signature.
*
* @param subType the sub type signature
* @param superType the super type signature
* @return the super type signatures of <code>subType</code>
* @throws JavaModelException if any java model operation fails
*/
private String[] getSuperTypeSignatures(IType subType, IType superType) throws JavaModelException {
if (superType.isInterface())
return subType.getSuperInterfaceTypeSignatures();
else
return new String[] {subType.getSuperclassTypeSignature()};
}
/**
* Clears the collected type bounds and initializes it with
* <code>java.lang.Object</code>.
*/
private void initBounds() {
fBounds.clear();
fBounds.add(OBJECT_SIGNATURE);
}
/**
* Filters the current list of type bounds through the additional type
* bound described by <code>boundSignature</code>.
*
* @param boundSignature the additional bound to add to the list of
* collected bounds
*/
private void addBound(String boundSignature) {
if (SignatureUtil.isJavaLangObject(boundSignature))
return;
boolean found= false;
for (ListIterator<String> it= fBounds.listIterator(); it.hasNext();) {
String old= it.next();
if (isTrueSubtypeOf(boundSignature, old)) {
if (!found) {
it.set(boundSignature);
found= true;
} else {
it.remove();
}
}
}
if (!found)
fBounds.add(boundSignature);
}
/**
* Returns <code>true</code> if <code>subTypeSignature</code>
* describes a type which is a true sub type of the type described by
* <code>superTypeSignature</code>.
*
* @param subTypeSignature the potential subtype's signature
* @param superTypeSignature the potential supertype's signature
* @return <code>true</code> if the inheritance relationship holds
*/
private boolean isTrueSubtypeOf(String subTypeSignature, String superTypeSignature) {
// try cheap test first
if (subTypeSignature.equals(superTypeSignature))
return true;
if (SignatureUtil.isJavaLangObject(subTypeSignature))
return false; // Object has no super types
if (Signature.getTypeSignatureKind(subTypeSignature) != Signature.BASE_TYPE_SIGNATURE && SignatureUtil.isJavaLangObject(superTypeSignature))
return true;
IJavaProject project= fUnit.getJavaProject();
try {
if ((Signature.getTypeSignatureKind(subTypeSignature) & (Signature.TYPE_VARIABLE_SIGNATURE | Signature.CLASS_TYPE_SIGNATURE)) == 0)
return false;
IType subType= project.findType(SignatureUtil.stripSignatureToFQN(subTypeSignature));
if (subType == null)
return false;
if ((Signature.getTypeSignatureKind(superTypeSignature) & (Signature.TYPE_VARIABLE_SIGNATURE | Signature.CLASS_TYPE_SIGNATURE)) == 0)
return false;
IType superType= project.findType(SignatureUtil.stripSignatureToFQN(superTypeSignature));
if (superType == null)
return false;
ITypeHierarchy hierarchy= subType.newSupertypeHierarchy(null);
IType[] types= hierarchy.getAllSupertypes(subType);
for (int i= 0; i < types.length; i++)
if (types[i].equals(superType))
return true;
} catch (JavaModelException e) {
// ignore and return false
}
return false;
}
/**
* Returns <code>true</code> if <code>signature</code> is a concrete type signature,
* <code>false</code> if it is a type variable.
*
* @param signature the signature to check
* @param context the context inside which to resolve the type
* @return <code>true</code> if the given signature is a concrete type signature
* @throws JavaModelException if finding the type fails
*/
private boolean isConcreteType(String signature, IType context) throws JavaModelException {
// Inexpensive check for the variable type first
if (Signature.TYPE_VARIABLE_SIGNATURE == Signature.getTypeSignatureKind(signature))
return false;
// try and resolve otherwise
if (context.isBinary()) {
return fUnit.getJavaProject().findType(SignatureUtil.stripSignatureToFQN(signature)) != null;
} else {
return context.resolveType(SignatureUtil.stripSignatureToFQN(signature)) != null;
}
}
}
private ICompilationUnit fUnit;
private List<Variable> fLocalVariables= new ArrayList<>();
private List<Variable> fFields= new ArrayList<>();
private Map<String, String> fLocalTypes= new HashMap<>();
private boolean fError;
/**
* Creates a compilation unit completion.
*
* @param unit the compilation unit, may be <code>null</code>.
*/
CompilationUnitCompletion(ICompilationUnit unit) {
reset(unit);
setIgnored(CompletionProposal.ANONYMOUS_CLASS_DECLARATION, true);
setIgnored(CompletionProposal.ANONYMOUS_CLASS_CONSTRUCTOR_INVOCATION, true);
setIgnored(CompletionProposal.KEYWORD, true);
setIgnored(CompletionProposal.LABEL_REF, true);
setIgnored(CompletionProposal.METHOD_DECLARATION, true);
setIgnored(CompletionProposal.METHOD_NAME_REFERENCE, true);
setIgnored(CompletionProposal.METHOD_REF, true);
setIgnored(CompletionProposal.CONSTRUCTOR_INVOCATION, true);
setIgnored(CompletionProposal.METHOD_REF_WITH_CASTED_RECEIVER, true);
setIgnored(CompletionProposal.PACKAGE_REF, true);
setIgnored(CompletionProposal.MODULE_REF, true);
setIgnored(CompletionProposal.POTENTIAL_METHOD_DECLARATION, true);
setIgnored(CompletionProposal.VARIABLE_DECLARATION, true);
setIgnored(CompletionProposal.TYPE_REF, true);
}
/**
* Resets the completion requester.
*
* @param unit the compilation unit, may be <code>null</code>.
*/
private void reset(ICompilationUnit unit) {
fUnit= unit;
fLocalVariables.clear();
fFields.clear();
fLocalTypes.clear();
if (fUnit != null) {
try {
IType[] cuTypes= fUnit.getAllTypes();
for (int i= 0; i < cuTypes.length; i++) {
String fqn= cuTypes[i].getFullyQualifiedName();
String sig= Signature.createTypeSignature(fqn, true);
fLocalTypes.put(sig, cuTypes[i].getElementName());
}
} catch (JavaModelException e) {
// ignore
}
}
fError= false;
}
/*
* @see org.eclipse.jdt.core.CompletionRequestor#accept(org.eclipse.jdt.core.CompletionProposal)
*/
@Override
public void accept(CompletionProposal proposal) {
String name= String.valueOf(proposal.getCompletion());
String signature= String.valueOf(proposal.getSignature());
switch (proposal.getKind()) {
case CompletionProposal.LOCAL_VARIABLE_REF:
// collect local variables
fLocalVariables.add(new Variable(name, signature));
break;
case CompletionProposal.FIELD_REF:
// collect local variables
fFields.add(new Variable(name, signature));
break;
default:
break;
}
}
/*
* @see org.eclipse.jdt.core.CompletionRequestor#completionFailure(org.eclipse.jdt.core.compiler.IProblem)
*/
@Override
public void completionFailure(IProblem problem) {
fError= true;
}
/**
* Tests if the code completion process produced errors.
*
* @return <code>true</code> if there are errors, <code>false</code>
* otherwise
*/
public boolean hasErrors() {
return fError;
}
/**
* Returns all local variable names.
*
* @return all local variable names
*/
public String[] getLocalVariableNames() {
String[] names= new String[fLocalVariables.size()];
int i= 0;
for (ListIterator<Variable> iterator= fLocalVariables.listIterator(fLocalVariables.size()); iterator.hasPrevious();) {
Variable localVariable= iterator.previous();
names[i++]= localVariable.getName();
}
return names;
}
/**
* Returns all field names.
*
* @return all field names
* @since 3.3
*/
public String[] getFieldNames() {
String[] names= new String[fFields.size()];
int i= 0;
for (ListIterator<Variable> iterator= fFields.listIterator(fFields.size()); iterator.hasPrevious();) {
Variable field= iterator.previous();
names[i++]= field.getName();
}
return names;
}
/**
* Returns all arrays, visible in the current context's scope, in the order that they appear.
*
* @return all visible arrays
*/
public Variable[] findArraysInCurrentScope() {
List<Variable> arrays= new ArrayList<>();
// local variables
for (ListIterator<Variable> iterator= fLocalVariables.listIterator(fLocalVariables.size()); iterator.hasPrevious();) {
Variable localVariable= iterator.previous();
if (localVariable.isArray())
arrays.add(localVariable);
}
// fields
for (ListIterator<Variable> iterator= fFields.listIterator(fFields.size()); iterator.hasPrevious();) {
Variable field= iterator.previous();
if (field.isArray())
arrays.add(field);
}
return arrays.toArray(new Variable[arrays.size()]);
}
/**
* Returns all local variables implementing or extending
* <code>clazz</code> in the order that they appear.
*
* @param clazz the fully qualified type name of the class to match
* @return all local variables matching <code>clazz</code>
*/
public Variable[] findLocalVariables(String clazz) {
List<Variable> matches= new ArrayList<>();
for (ListIterator<Variable> iterator= fLocalVariables.listIterator(fLocalVariables.size()); iterator.hasPrevious();) {
Variable localVariable= iterator.previous();
if (localVariable.isSubtypeOf(clazz))
matches.add(localVariable);
}
return matches.toArray(new Variable[matches.size()]);
}
/**
* Returns all local variables implementing or extending
* <code>clazz</code> in the order that they appear.
*
* @param clazz the fully qualified type name of the class to match
* @return all local variables matching <code>clazz</code>
*/
public Variable[] findFieldVariables(String clazz) {
List<Variable> matches= new ArrayList<>();
for (ListIterator<Variable> iterator= fFields.listIterator(fFields.size()); iterator.hasPrevious();) {
Variable localVariable= iterator.previous();
if (localVariable.isSubtypeOf(clazz))
matches.add(localVariable);
}
return matches.toArray(new Variable[matches.size()]);
}
/**
* Returns all variables, visible in the current context's scope, implementing
* <code>java.lang.Iterable</code> <em>and</em> all arrays, in the order that they appear. That
* is, the returned variables can be used within the <code>foreach</code> language construct.
*
* @return all visible <code>Iterable</code>s and arrays
*/
public Variable[] findIterablesInCurrentScope() {
List<Variable> iterables= new ArrayList<>();
// local variables
for (ListIterator<Variable> iterator= fLocalVariables.listIterator(fLocalVariables.size()); iterator.hasPrevious();) {
Variable localVariable= iterator.previous();
if (localVariable.isArray() || localVariable.isIterable())
iterables.add(localVariable);
}
// fields
for (ListIterator<Variable> iterator= fFields.listIterator(fFields.size()); iterator.hasPrevious();) {
Variable field= iterator.previous();
if (field.isArray() || field.isIterable())
iterables.add(field);
}
return iterables.toArray(new Variable[iterables.size()]);
}
}