blob: 806faaf3c14dd01aa11e1a0750232c147fbb6e6e [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2017 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.jdt.internal.corext.codemanipulation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.text.edits.TextEdit;
import org.eclipse.jdt.core.Flags;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.ISourceRange;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.SourceRange;
import org.eclipse.jdt.core.compiler.IProblem;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.IBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.ImportDeclaration;
import org.eclipse.jdt.core.dom.Modifier;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.Type;
import org.eclipse.jdt.core.dom.rewrite.ImportRewrite;
import org.eclipse.jdt.core.dom.rewrite.ImportRewrite.ImportRewriteContext;
import org.eclipse.jdt.core.search.IJavaSearchConstants;
import org.eclipse.jdt.core.search.IJavaSearchScope;
import org.eclipse.jdt.core.search.SearchEngine;
import org.eclipse.jdt.core.search.TypeNameMatch;
import org.eclipse.jdt.internal.core.manipulation.dom.ASTResolving;
import org.eclipse.jdt.internal.core.manipulation.util.BasicElementLabels;
import org.eclipse.jdt.internal.core.manipulation.util.Strings;
import org.eclipse.jdt.internal.corext.dom.ASTNodes;
import org.eclipse.jdt.internal.corext.dom.Bindings;
import org.eclipse.jdt.internal.corext.dom.ScopeAnalyzer;
import org.eclipse.jdt.internal.corext.refactoring.util.JavaElementUtil;
import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
import org.eclipse.jdt.internal.corext.util.JdtFlags;
import org.eclipse.jdt.internal.corext.util.Messages;
import org.eclipse.jdt.internal.corext.util.TypeNameMatchCollector;
import org.eclipse.jdt.ui.SharedASTProvider;
import org.eclipse.jdt.internal.ui.text.correction.ProblemLocation;
import org.eclipse.jdt.internal.ui.text.correction.SimilarElementsRequestor;
public class OrganizeImportsOperation implements IWorkspaceRunnable {
public static interface IChooseImportQuery {
/**
* Selects imports from a list of choices.
* @param openChoices From each array, a type reference has to be selected
* @param ranges For each choice the range of the corresponding type reference.
* @return Returns <code>null</code> to cancel the operation, or the
* selected imports.
*/
TypeNameMatch[] chooseImports(TypeNameMatch[][] openChoices, ISourceRange[] ranges);
}
/**
* Matches unresolvable import declarations (those having associated
* {@link IProblem#ImportNotFound} problems) to unresolved simple names.
* <p>
* For a given simple name, looks first for single imports of that simple name and then,
* in the absence of such, for any on-demand imports. Considers type imports for simple names
* of unresolved types and static imports for simple names of unresolved static members.
* <p>
* @see <a href="https://bugs.eclipse.org/357795">Bug 357795</a>
*/
private static class UnresolvableImportMatcher {
static UnresolvableImportMatcher forCompilationUnit(CompilationUnit cu) {
Collection<ImportDeclaration> unresolvableImports= determineUnresolvableImports(cu);
Map<String, Set<String>> typeImportsBySimpleName= new HashMap<>();
Map<String, Set<String>> staticImportsBySimpleName= new HashMap<>();
for (ImportDeclaration importDeclaration : unresolvableImports) {
String qualifiedName= importDeclaration.isOnDemand()
? importDeclaration.getName().getFullyQualifiedName() + ".*" //$NON-NLS-1$
: importDeclaration.getName().getFullyQualifiedName();
String simpleName= qualifiedName.substring(qualifiedName.lastIndexOf('.') + 1);
Map<String, Set<String>> importsBySimpleName= importDeclaration.isStatic()
? staticImportsBySimpleName : typeImportsBySimpleName;
Set<String> importsWithSimpleName= importsBySimpleName.get(simpleName);
if (importsWithSimpleName == null) {
importsWithSimpleName= new HashSet<>();
importsBySimpleName.put(simpleName, importsWithSimpleName);
}
importsWithSimpleName.add(qualifiedName);
}
return new UnresolvableImportMatcher(typeImportsBySimpleName, staticImportsBySimpleName);
}
private static Collection<ImportDeclaration> determineUnresolvableImports(CompilationUnit cu) {
Collection<ImportDeclaration> unresolvableImports= new ArrayList<>(cu.imports().size());
for (IProblem problem : cu.getProblems()) {
if (problem.getID() == IProblem.ImportNotFound) {
ImportDeclaration problematicImport= getProblematicImport(problem, cu);
if (problematicImport != null) {
unresolvableImports.add(problematicImport);
}
}
}
return unresolvableImports;
}
private static ImportDeclaration getProblematicImport(IProblem problem, CompilationUnit cu) {
ASTNode coveringNode= new ProblemLocation(problem).getCoveringNode(cu);
if (coveringNode != null) {
ASTNode importNode= ASTNodes.getParent(coveringNode, ASTNode.IMPORT_DECLARATION);
if (importNode instanceof ImportDeclaration) {
return (ImportDeclaration) importNode;
}
}
return null;
}
private final Map<String, Set<String>> fTypeImportsBySimpleName;
private final Map<String, Set<String>> fStaticImportsBySimpleName;
private UnresolvableImportMatcher(
Map<String, Set<String>> typeImportsBySimpleName, Map<String, Set<String>> staticImportsBySimpleName) {
fTypeImportsBySimpleName= typeImportsBySimpleName;
fStaticImportsBySimpleName= staticImportsBySimpleName;
}
private Set<String> matchImports(boolean isStatic, String simpleName) {
Map<String, Set<String>> importsBySimpleName= isStatic
? fStaticImportsBySimpleName : fTypeImportsBySimpleName;
Set<String> matchingSingleImports= importsBySimpleName.get(simpleName);
if (matchingSingleImports != null) {
return Collections.unmodifiableSet(matchingSingleImports);
}
Set<String> matchingOnDemandImports= importsBySimpleName.get("*"); //$NON-NLS-1$
if (matchingOnDemandImports != null) {
return Collections.unmodifiableSet(matchingOnDemandImports);
}
return Collections.emptySet();
}
Set<String> matchTypeImports(String simpleName) {
return matchImports(false, simpleName);
}
Set<String> matchStaticImports(String simpleName) {
return matchImports(true, simpleName);
}
}
private static class TypeReferenceProcessor {
private static class UnresolvedTypeData {
final SimpleName ref;
final int typeKinds;
final List<TypeNameMatch> foundInfos;
public UnresolvedTypeData(SimpleName ref) {
this.ref= ref;
this.typeKinds= org.eclipse.jdt.internal.ui.text.correction.ASTResolving.getPossibleTypeKinds(ref, true);
this.foundInfos= new ArrayList<>(3);
}
public void addInfo(TypeNameMatch info) {
for (int i= this.foundInfos.size() - 1; i >= 0; i--) {
TypeNameMatch curr= this.foundInfos.get(i);
if (curr.getTypeContainerName().equals(info.getTypeContainerName())) {
return; // not added. already contains type with same name
}
}
foundInfos.add(info);
}
}
private Set<String> fOldSingleImports;
private Set<String> fOldDemandImports;
private Set<String> fImplicitImports;
private ImportRewrite fImpStructure;
private boolean fDoIgnoreLowerCaseNames;
private final UnresolvableImportMatcher fUnresolvableImportMatcher;
private IPackageFragment fCurrPackage;
private ScopeAnalyzer fAnalyzer;
private boolean fAllowDefaultPackageImports;
private Map<String, UnresolvedTypeData> fUnresolvedTypes;
private Set<String> fImportsAdded;
private TypeNameMatch[][] fOpenChoices;
private SourceRange[] fSourceRanges;
public TypeReferenceProcessor(Set<String> oldSingleImports, Set<String> oldDemandImports, CompilationUnit root, ImportRewrite impStructure, boolean ignoreLowerCaseNames, UnresolvableImportMatcher unresolvableImportMatcher) {
fOldSingleImports= oldSingleImports;
fOldDemandImports= oldDemandImports;
fImpStructure= impStructure;
fDoIgnoreLowerCaseNames= ignoreLowerCaseNames;
fUnresolvableImportMatcher= unresolvableImportMatcher;
ICompilationUnit cu= impStructure.getCompilationUnit();
fImplicitImports= new HashSet<>(3);
fImplicitImports.add(""); //$NON-NLS-1$
fImplicitImports.add("java.lang"); //$NON-NLS-1$
fImplicitImports.add(cu.getParent().getElementName());
fAnalyzer= new ScopeAnalyzer(root);
fCurrPackage= (IPackageFragment) cu.getParent();
fAllowDefaultPackageImports= cu.getJavaProject().getOption(JavaCore.COMPILER_SOURCE, true).equals(JavaCore.VERSION_1_3);
fImportsAdded= new HashSet<>();
fUnresolvedTypes= new HashMap<>();
}
private boolean needsImport(ITypeBinding typeBinding, SimpleName ref) {
if (!typeBinding.isTopLevel() && !typeBinding.isMember() || typeBinding.isRecovered()) {
return false; // no imports for anonymous, local, primitive types or parameters types
}
int modifiers= typeBinding.getModifiers();
if (Modifier.isPrivate(modifiers)) {
return false; // imports for privates are not required
}
ITypeBinding currTypeBinding= Bindings.getBindingOfParentType(ref);
if (currTypeBinding == null) {
if (ASTNodes.getParent(ref, ASTNode.PACKAGE_DECLARATION) != null) {
return true; // reference in package-info.java
}
if (ASTNodes.getParent(ref, ASTNode.MODULE_DECLARATION) != null) {
return true; // reference in module-info.java
}
return false; // not in a type
}
if (!Modifier.isPublic(modifiers)) {
if (!currTypeBinding.getPackage().getName().equals(typeBinding.getPackage().getName())) {
return false; // not visible
}
}
ASTNode parent= ref.getParent();
while (parent instanceof Type) {
parent= parent.getParent();
}
if (parent instanceof AbstractTypeDeclaration && parent.getParent() instanceof CompilationUnit) {
return true;
}
if (typeBinding.isMember()) {
if (fAnalyzer.isDeclaredInScope(typeBinding, ref, ScopeAnalyzer.TYPES | ScopeAnalyzer.CHECK_VISIBILITY))
return false;
}
return true;
}
/**
* Tries to find the given type name and add it to the import structure.
* @param ref the name node
*/
public void add(SimpleName ref) {
String typeName= ref.getIdentifier();
if (fImportsAdded.contains(typeName)) {
return;
}
IBinding binding= ref.resolveBinding();
if (binding != null) {
if (binding.getKind() != IBinding.TYPE) {
return;
}
ITypeBinding typeBinding= (ITypeBinding) binding;
if (typeBinding.isArray()) {
typeBinding= typeBinding.getElementType();
}
typeBinding= typeBinding.getTypeDeclaration();
if (!typeBinding.isRecovered()) {
if (needsImport(typeBinding, ref)) {
fImpStructure.addImport(typeBinding);
fImportsAdded.add(typeName);
}
return;
}
} else {
if (fDoIgnoreLowerCaseNames && typeName.length() > 0) {
char ch= typeName.charAt(0);
if (Strings.isLowerCase(ch) && Character.isLetter(ch)) {
return;
}
}
}
fImportsAdded.add(typeName);
fUnresolvedTypes.put(typeName, new UnresolvedTypeData(ref));
}
public boolean process(IProgressMonitor monitor) throws JavaModelException {
try {
int nUnresolved= fUnresolvedTypes.size();
if (nUnresolved == 0) {
return false;
}
char[][] allTypes= new char[nUnresolved][];
int i= 0;
for (Iterator<String> iter= fUnresolvedTypes.keySet().iterator(); iter.hasNext();) {
allTypes[i++]= iter.next().toCharArray();
}
final ArrayList<TypeNameMatch> typesFound= new ArrayList<>();
final IJavaProject project= fCurrPackage.getJavaProject();
IJavaSearchScope scope= SearchEngine.createJavaSearchScope(new IJavaElement[] { project });
TypeNameMatchCollector collector= new TypeNameMatchCollector(typesFound);
new SearchEngine().searchAllTypeNames(null, allTypes, scope, collector, IJavaSearchConstants.WAIT_UNTIL_READY_TO_SEARCH, monitor);
boolean is50OrHigher= JavaModelUtil.is50OrHigher(project);
for (i= 0; i < typesFound.size(); i++) {
TypeNameMatch curr= typesFound.get(i);
UnresolvedTypeData data= fUnresolvedTypes.get(curr.getSimpleTypeName());
if (data != null && isVisible(curr) && isOfKind(curr, data.typeKinds, is50OrHigher)) {
if (fAllowDefaultPackageImports || curr.getPackageName().length() > 0) {
data.addInfo(curr);
}
}
}
for (Entry<String, UnresolvedTypeData> entry : fUnresolvedTypes.entrySet()) {
if (entry.getValue().foundInfos.size() == 0) { // No result found in search
Set<String> matchingUnresolvableImports= fUnresolvableImportMatcher.matchTypeImports(entry.getKey());
if (!matchingUnresolvableImports.isEmpty()) {
// If there are matching unresolvable import(s), rely on them to provide the type.
for (String string : matchingUnresolvableImports) {
fImpStructure.addImport(string, UNRESOLVABLE_IMPORT_CONTEXT);
}
}
}
}
ArrayList<TypeNameMatch[]> openChoices= new ArrayList<>(nUnresolved);
ArrayList<SourceRange> sourceRanges= new ArrayList<>(nUnresolved);
for (Iterator<UnresolvedTypeData> iter= fUnresolvedTypes.values().iterator(); iter.hasNext();) {
UnresolvedTypeData data= iter.next();
TypeNameMatch[] openChoice= processTypeInfo(data.foundInfos);
if (openChoice != null) {
openChoices.add(openChoice);
sourceRanges.add(new SourceRange(data.ref.getStartPosition(), data.ref.getLength()));
}
}
if (openChoices.isEmpty()) {
return false;
}
fOpenChoices= openChoices.toArray(new TypeNameMatch[openChoices.size()][]);
fSourceRanges= sourceRanges.toArray(new SourceRange[sourceRanges.size()]);
return true;
} finally {
monitor.done();
}
}
private TypeNameMatch[] processTypeInfo(List<TypeNameMatch> typeRefsFound) {
int nFound= typeRefsFound.size();
if (nFound == 0) {
// nothing found
return null;
} else if (nFound == 1) {
TypeNameMatch typeRef= typeRefsFound.get(0);
fImpStructure.addImport(typeRef.getFullyQualifiedName());
return null;
} else {
String typeToImport= null;
boolean ambiguousImports= false;
// multiple found, use old imports to find an entry
for (int i= 0; i < nFound; i++) {
TypeNameMatch typeRef= typeRefsFound.get(i);
String fullName= typeRef.getFullyQualifiedName();
String containerName= typeRef.getTypeContainerName();
if (fOldSingleImports.contains(fullName)) {
// was single-imported
fImpStructure.addImport(fullName);
return null;
} else if (fOldDemandImports.contains(containerName) || fImplicitImports.contains(containerName)) {
if (typeToImport == null) {
typeToImport= fullName;
} else { // more than one import-on-demand
ambiguousImports= true;
}
}
}
if (typeToImport != null && !ambiguousImports) {
fImpStructure.addImport(typeToImport);
return null;
}
// return the open choices
return typeRefsFound.toArray(new TypeNameMatch[nFound]);
}
}
private boolean isOfKind(TypeNameMatch curr, int typeKinds, boolean is50OrHigher) {
int flags= curr.getModifiers();
if (Flags.isAnnotation(flags)) {
return is50OrHigher && (typeKinds & SimilarElementsRequestor.ANNOTATIONS) != 0;
}
if (Flags.isEnum(flags)) {
return is50OrHigher && (typeKinds & SimilarElementsRequestor.ENUMS) != 0;
}
if (Flags.isInterface(flags)) {
return (typeKinds & SimilarElementsRequestor.INTERFACES) != 0;
}
return (typeKinds & SimilarElementsRequestor.CLASSES) != 0;
}
private boolean isVisible(TypeNameMatch curr) {
int flags= curr.getModifiers();
if (Flags.isPrivate(flags)) {
return false;
}
boolean isPublic;
try {
isPublic= JdtFlags.isPublic(curr.getType());
} catch (JavaModelException e) {
isPublic= Flags.isPublic(flags);
}
if (isPublic || Flags.isProtected(flags)) {
return true;
}
return curr.getPackageName().equals(fCurrPackage.getElementName());
}
public TypeNameMatch[][] getChoices() {
return fOpenChoices;
}
public ISourceRange[] getChoicesSourceRanges() {
return fSourceRanges;
}
}
/**
* Used to ensure that unresolvable imports don't get reduced into on-demand imports.
*/
private static ImportRewriteContext UNRESOLVABLE_IMPORT_CONTEXT= new ImportRewriteContext() {
@Override
public int findInContext(String qualifier, String name, int kind) {
return RES_NAME_UNKNOWN_NEEDS_EXPLICIT_IMPORT;
}
};
private boolean fDoSave;
private boolean fIgnoreLowerCaseNames;
private IChooseImportQuery fChooseImportQuery;
private int fNumberOfImportsAdded;
private int fNumberOfImportsRemoved;
private IProblem fParsingError;
private ICompilationUnit fCompilationUnit;
private CompilationUnit fASTRoot;
private final boolean fAllowSyntaxErrors;
public OrganizeImportsOperation(ICompilationUnit cu, CompilationUnit astRoot, boolean ignoreLowerCaseNames, boolean save, boolean allowSyntaxErrors, IChooseImportQuery chooseImportQuery) {
fCompilationUnit= cu;
fASTRoot= astRoot;
fDoSave= save;
fIgnoreLowerCaseNames= ignoreLowerCaseNames;
fAllowSyntaxErrors= allowSyntaxErrors;
fChooseImportQuery= chooseImportQuery;
fNumberOfImportsAdded= 0;
fNumberOfImportsRemoved= 0;
fParsingError= null;
}
/**
* Runs the operation.
* @param monitor the progress monitor
* @throws CoreException thrown when the operation failed
* @throws OperationCanceledException Runtime error thrown when operation is canceled.
*/
@Override
public void run(IProgressMonitor monitor) throws CoreException, OperationCanceledException {
if (monitor == null) {
monitor= new NullProgressMonitor();
}
try {
monitor.beginTask(Messages.format(CodeGenerationMessages.OrganizeImportsOperation_description, BasicElementLabels.getFileName(fCompilationUnit)), 10);
TextEdit edit= createTextEdit(new SubProgressMonitor(monitor, 9));
if (edit == null)
return;
JavaElementUtil.applyEdit(fCompilationUnit, edit, fDoSave, new SubProgressMonitor(monitor, 1));
} finally {
monitor.done();
}
}
public TextEdit createTextEdit(IProgressMonitor monitor) throws CoreException, OperationCanceledException {
if (monitor == null) {
monitor= new NullProgressMonitor();
}
try {
fNumberOfImportsAdded= 0;
fNumberOfImportsRemoved= 0;
monitor.beginTask(Messages.format(CodeGenerationMessages.OrganizeImportsOperation_description, BasicElementLabels.getFileName(fCompilationUnit)), 9);
CompilationUnit astRoot= fASTRoot;
if (astRoot == null) {
astRoot= SharedASTProvider.getAST(fCompilationUnit, SharedASTProvider.WAIT_YES, new SubProgressMonitor(monitor, 2));
if (monitor.isCanceled())
throw new OperationCanceledException();
} else {
monitor.worked(2);
}
ImportRewrite importsRewrite= StubUtility.createImportRewrite(astRoot, false);
Set<String> oldSingleImports= new HashSet<>();
Set<String> oldDemandImports= new HashSet<>();
List<SimpleName> typeReferences= new ArrayList<>();
List<SimpleName> staticReferences= new ArrayList<>();
if (!collectReferences(astRoot, typeReferences, staticReferences, oldSingleImports, oldDemandImports))
return null;
monitor.worked(1);
UnresolvableImportMatcher unresolvableImportMatcher =
UnresolvableImportMatcher.forCompilationUnit(astRoot);
TypeReferenceProcessor processor= new TypeReferenceProcessor(
oldSingleImports,
oldDemandImports,
astRoot,
importsRewrite,
fIgnoreLowerCaseNames,
unresolvableImportMatcher);
Iterator<SimpleName> refIterator= typeReferences.iterator();
while (refIterator.hasNext()) {
SimpleName typeRef= refIterator.next();
processor.add(typeRef);
}
boolean hasOpenChoices= processor.process(new SubProgressMonitor(monitor, 3));
addStaticImports(staticReferences, importsRewrite, unresolvableImportMatcher);
if (hasOpenChoices && fChooseImportQuery != null) {
TypeNameMatch[][] choices= processor.getChoices();
ISourceRange[] ranges= processor.getChoicesSourceRanges();
TypeNameMatch[] chosen= fChooseImportQuery.chooseImports(choices, ranges);
if (chosen == null) {
// cancel pressed by the user
throw new OperationCanceledException();
}
for (int i= 0; i < chosen.length; i++) {
TypeNameMatch typeInfo= chosen[i];
if (typeInfo != null) {
importsRewrite.addImport(typeInfo.getFullyQualifiedName());
} else { // Skipped by user
String typeName= choices[i][0].getSimpleTypeName();
Set<String> matchingUnresolvableImports= unresolvableImportMatcher.matchTypeImports(typeName);
if (!matchingUnresolvableImports.isEmpty()) {
// If there are matching unresolvable import(s), rely on them to provide the type.
for (String string : matchingUnresolvableImports) {
importsRewrite.addImport(string, UNRESOLVABLE_IMPORT_CONTEXT);
}
}
}
}
}
TextEdit result= importsRewrite.rewriteImports(new SubProgressMonitor(monitor, 3));
determineImportDifferences(importsRewrite, oldSingleImports, oldDemandImports);
return result;
} finally {
monitor.done();
}
}
private void determineImportDifferences(ImportRewrite importsStructure, Set<String> oldSingleImports, Set<String> oldDemandImports) {
ArrayList<String> importsAdded= new ArrayList<>();
importsAdded.addAll(Arrays.asList(importsStructure.getCreatedImports()));
importsAdded.addAll(Arrays.asList(importsStructure.getCreatedStaticImports()));
Object[] content= oldSingleImports.toArray();
for (int i= 0; i < content.length; i++) {
String importName= (String) content[i];
if (importsAdded.remove(importName))
oldSingleImports.remove(importName);
}
content= oldDemandImports.toArray();
for (int i= 0; i < content.length; i++) {
String importName= (String) content[i];
if (importsAdded.remove(importName + ".*")) //$NON-NLS-1$
oldDemandImports.remove(importName);
}
fNumberOfImportsAdded= importsAdded.size();
fNumberOfImportsRemoved= oldSingleImports.size() + oldDemandImports.size();
}
private void addStaticImports(
Collection<SimpleName> staticReferences,
ImportRewrite importRewrite,
UnresolvableImportMatcher unresolvableImportMatcher) {
for (SimpleName name : staticReferences) {
IBinding binding= name.resolveBinding();
if (binding != null) {
importRewrite.addStaticImport(binding);
} else {
// This could be an unresolvable reference to a static member.
String identifier= name.getIdentifier();
Set<String> unresolvableImports= unresolvableImportMatcher.matchStaticImports(identifier);
for (String unresolvableImport : unresolvableImports) {
int lastDotIndex= unresolvableImport.lastIndexOf('.');
// It's OK to skip invalid imports.
if (lastDotIndex != -1) {
String declaringTypeName= unresolvableImport.substring(0, lastDotIndex);
String simpleName= unresolvableImport.substring(lastDotIndex + 1);
// Whether name refers to a field or to a method is unknown.
boolean isField= false;
importRewrite.addStaticImport(declaringTypeName, simpleName, isField, UNRESOLVABLE_IMPORT_CONTEXT);
}
}
}
}
}
// find type references in a compilation unit
private boolean collectReferences(CompilationUnit astRoot, List<SimpleName> typeReferences, List<SimpleName> staticReferences, Set<String> oldSingleImports, Set<String> oldDemandImports) {
if (!fAllowSyntaxErrors) {
IProblem[] problems= astRoot.getProblems();
for (int i= 0; i < problems.length; i++) {
IProblem curr= problems[i];
if (curr.isError() && (curr.getID() & IProblem.Syntax) != 0) {
fParsingError= problems[i];
return false;
}
}
}
List<ImportDeclaration> imports= astRoot.imports();
for (int i= 0; i < imports.size(); i++) {
ImportDeclaration curr= imports.get(i);
String id= ASTResolving.getFullName(curr.getName());
if (curr.isOnDemand()) {
oldDemandImports.add(id);
} else {
oldSingleImports.add(id);
}
}
IJavaProject project= fCompilationUnit.getJavaProject();
ImportReferencesCollector.collect(astRoot, project, null, typeReferences, staticReferences);
return true;
}
/**
* After executing the operation, returns <code>null</code> if the operation has been executed successfully or
* the range where parsing failed.
* @return returns the parse error
*/
public IProblem getParseError() {
return fParsingError;
}
public int getNumberOfImportsAdded() {
return fNumberOfImportsAdded;
}
public int getNumberOfImportsRemoved() {
return fNumberOfImportsRemoved;
}
/**
* @return Returns the scheduling rule for this operation
*/
public ISchedulingRule getScheduleRule() {
return fCompilationUnit.getResource();
}
}