1 11 package org.eclipse.jdt.internal.corext.codemanipulation; 12 13 import java.util.ArrayList ; 14 import java.util.Arrays ; 15 import java.util.HashMap ; 16 import java.util.HashSet ; 17 import java.util.Iterator ; 18 import java.util.List ; 19 import java.util.Map ; 20 import java.util.Set ; 21 22 import org.eclipse.text.edits.TextEdit; 23 24 import org.eclipse.core.runtime.CoreException; 25 import org.eclipse.core.runtime.IProgressMonitor; 26 import org.eclipse.core.runtime.NullProgressMonitor; 27 import org.eclipse.core.runtime.OperationCanceledException; 28 import org.eclipse.core.runtime.SubProgressMonitor; 29 import org.eclipse.core.runtime.jobs.ISchedulingRule; 30 31 import org.eclipse.core.resources.IWorkspaceRunnable; 32 import org.eclipse.core.resources.ResourcesPlugin; 33 34 import org.eclipse.jdt.core.Flags; 35 import org.eclipse.jdt.core.ICompilationUnit; 36 import org.eclipse.jdt.core.IJavaElement; 37 import org.eclipse.jdt.core.IJavaProject; 38 import org.eclipse.jdt.core.IPackageFragment; 39 import org.eclipse.jdt.core.ISourceRange; 40 import org.eclipse.jdt.core.JavaCore; 41 import org.eclipse.jdt.core.JavaModelException; 42 import org.eclipse.jdt.core.compiler.IProblem; 43 import org.eclipse.jdt.core.dom.ASTNode; 44 import org.eclipse.jdt.core.dom.AbstractTypeDeclaration; 45 import org.eclipse.jdt.core.dom.CompilationUnit; 46 import org.eclipse.jdt.core.dom.IBinding; 47 import org.eclipse.jdt.core.dom.ITypeBinding; 48 import org.eclipse.jdt.core.dom.ImportDeclaration; 49 import org.eclipse.jdt.core.dom.Modifier; 50 import org.eclipse.jdt.core.dom.Name; 51 import org.eclipse.jdt.core.dom.SimpleName; 52 import org.eclipse.jdt.core.dom.Type; 53 import org.eclipse.jdt.core.dom.rewrite.ImportRewrite; 54 import org.eclipse.jdt.core.search.IJavaSearchConstants; 55 import org.eclipse.jdt.core.search.IJavaSearchScope; 56 import org.eclipse.jdt.core.search.SearchEngine; 57 import org.eclipse.jdt.core.search.TypeNameMatch; 58 59 import org.eclipse.jdt.internal.corext.SourceRange; 60 import org.eclipse.jdt.internal.corext.dom.Bindings; 61 import org.eclipse.jdt.internal.corext.dom.ScopeAnalyzer; 62 import org.eclipse.jdt.internal.corext.util.JavaModelUtil; 63 import org.eclipse.jdt.internal.corext.util.Messages; 64 import org.eclipse.jdt.internal.corext.util.Strings; 65 import org.eclipse.jdt.internal.corext.util.TypeNameMatchCollector; 66 67 import org.eclipse.jdt.internal.ui.javaeditor.ASTProvider; 68 import org.eclipse.jdt.internal.ui.text.correction.ASTResolving; 69 import org.eclipse.jdt.internal.ui.text.correction.SimilarElementsRequestor; 70 71 public class OrganizeImportsOperation implements IWorkspaceRunnable { 72 public static interface IChooseImportQuery { 73 80 TypeNameMatch[] chooseImports(TypeNameMatch[][] openChoices, ISourceRange[] ranges); 81 } 82 83 84 private static class TypeReferenceProcessor { 85 86 private static class UnresolvedTypeData { 87 final SimpleName ref; 88 final int typeKinds; 89 final List foundInfos; 90 91 public UnresolvedTypeData(SimpleName ref) { 92 this.ref= ref; 93 this.typeKinds= ASTResolving.getPossibleTypeKinds(ref, true); 94 this.foundInfos= new ArrayList (3); 95 } 96 97 public void addInfo(TypeNameMatch info) { 98 for (int i= this.foundInfos.size() - 1; i >= 0; i--) { 99 TypeNameMatch curr= (TypeNameMatch) this.foundInfos.get(i); 100 if (curr.getTypeContainerName().equals(info.getTypeContainerName())) { 101 return; } 103 } 104 foundInfos.add(info); 105 } 106 } 107 108 private Set fOldSingleImports; 109 private Set fOldDemandImports; 110 111 private Set fImplicitImports; 112 113 private ImportRewrite fImpStructure; 114 115 private boolean fDoIgnoreLowerCaseNames; 116 117 private IPackageFragment fCurrPackage; 118 119 private ScopeAnalyzer fAnalyzer; 120 private boolean fAllowDefaultPackageImports; 121 122 private Map fUnresolvedTypes; 123 private Set fImportsAdded; 124 private TypeNameMatch[][] fOpenChoices; 125 private SourceRange[] fSourceRanges; 126 127 128 public TypeReferenceProcessor(Set oldSingleImports, Set oldDemandImports, CompilationUnit root, ImportRewrite impStructure, boolean ignoreLowerCaseNames) { 129 fOldSingleImports= oldSingleImports; 130 fOldDemandImports= oldDemandImports; 131 fImpStructure= impStructure; 132 fDoIgnoreLowerCaseNames= ignoreLowerCaseNames; 133 134 ICompilationUnit cu= impStructure.getCompilationUnit(); 135 136 fImplicitImports= new HashSet (3); 137 fImplicitImports.add(""); fImplicitImports.add("java.lang"); fImplicitImports.add(cu.getParent().getElementName()); 140 141 fAnalyzer= new ScopeAnalyzer(root); 142 143 fCurrPackage= (IPackageFragment) cu.getParent(); 144 145 fAllowDefaultPackageImports= cu.getJavaProject().getOption(JavaCore.COMPILER_COMPLIANCE, true).equals(JavaCore.VERSION_1_3); 146 147 fImportsAdded= new HashSet (); 148 fUnresolvedTypes= new HashMap (); 149 } 150 151 private boolean needsImport(ITypeBinding typeBinding, SimpleName ref) { 152 if (!typeBinding.isTopLevel() && !typeBinding.isMember() || typeBinding.isRecovered()) { 153 return false; } 155 int modifiers= typeBinding.getModifiers(); 156 if (Modifier.isPrivate(modifiers)) { 157 return false; } 159 ITypeBinding currTypeBinding= Bindings.getBindingOfParentType(ref); 160 if (currTypeBinding == null) { 161 return false; } 163 if (!Modifier.isPublic(modifiers)) { 164 if (!currTypeBinding.getPackage().getName().equals(typeBinding.getPackage().getName())) { 165 return false; } 167 } 168 169 ASTNode parent= ref.getParent(); 170 while (parent instanceof Type) { 171 parent= parent.getParent(); 172 } 173 if (parent instanceof AbstractTypeDeclaration && parent.getParent() instanceof CompilationUnit) { 174 return true; 175 } 176 177 if (typeBinding.isMember()) { 178 if (fAnalyzer.isDeclaredInScope(typeBinding, ref, ScopeAnalyzer.TYPES | ScopeAnalyzer.CHECK_VISIBILITY)) 179 return false; 180 } 181 return true; 182 } 183 184 185 189 public void add(SimpleName ref) { 190 String typeName= ref.getIdentifier(); 191 192 if (fImportsAdded.contains(typeName)) { 193 return; 194 } 195 196 IBinding binding= ref.resolveBinding(); 197 if (binding != null) { 198 if (binding.getKind() != IBinding.TYPE) { 199 return; 200 } 201 ITypeBinding typeBinding= (ITypeBinding) binding; 202 if (typeBinding.isArray()) { 203 typeBinding= typeBinding.getElementType(); 204 } 205 typeBinding= typeBinding.getTypeDeclaration(); 206 if (!typeBinding.isRecovered()) { 207 if (needsImport(typeBinding, ref)) { 208 fImpStructure.addImport(typeBinding); 209 fImportsAdded.add(typeName); 210 } 211 return; 212 } 213 } else { 214 if (fDoIgnoreLowerCaseNames && typeName.length() > 0) { 215 char ch= typeName.charAt(0); 216 if (Strings.isLowerCase(ch) && Character.isLetter(ch)) { 217 return; 218 } 219 } 220 } 221 fImportsAdded.add(typeName); 222 fUnresolvedTypes.put(typeName, new UnresolvedTypeData(ref)); 223 } 224 225 public boolean process(IProgressMonitor monitor) throws JavaModelException { 226 try { 227 int nUnresolved= fUnresolvedTypes.size(); 228 if (nUnresolved == 0) { 229 return false; 230 } 231 char[][] allTypes= new char[nUnresolved][]; 232 int i= 0; 233 for (Iterator iter= fUnresolvedTypes.keySet().iterator(); iter.hasNext();) { 234 allTypes[i++]= ((String ) iter.next()).toCharArray(); 235 } 236 final ArrayList typesFound= new ArrayList (); 237 final IJavaProject project= fCurrPackage.getJavaProject(); 238 IJavaSearchScope scope= SearchEngine.createJavaSearchScope(new IJavaElement[] { project }); 239 TypeNameMatchCollector collector= new TypeNameMatchCollector(typesFound); 240 new SearchEngine().searchAllTypeNames(null, allTypes, scope, collector, IJavaSearchConstants.WAIT_UNTIL_READY_TO_SEARCH, monitor); 241 242 boolean is50OrHigher= JavaModelUtil.is50OrHigher(project); 243 244 for (i= 0; i < typesFound.size(); i++) { 245 TypeNameMatch curr= (TypeNameMatch) typesFound.get(i); 246 UnresolvedTypeData data= (UnresolvedTypeData) fUnresolvedTypes.get(curr.getSimpleTypeName()); 247 if (data != null && isVisible(curr) && isOfKind(curr, data.typeKinds, is50OrHigher)) { 248 if (fAllowDefaultPackageImports || curr.getPackageName().length() > 0) { 249 data.addInfo(curr); 250 } 251 } 252 } 253 254 ArrayList openChoices= new ArrayList (nUnresolved); 255 ArrayList sourceRanges= new ArrayList (nUnresolved); 256 for (Iterator iter= fUnresolvedTypes.values().iterator(); iter.hasNext();) { 257 UnresolvedTypeData data= (UnresolvedTypeData) iter.next(); 258 TypeNameMatch[] openChoice= processTypeInfo(data.foundInfos); 259 if (openChoice != null) { 260 openChoices.add(openChoice); 261 sourceRanges.add(new SourceRange(data.ref.getStartPosition(), data.ref.getLength())); 262 } 263 } 264 if (openChoices.isEmpty()) { 265 return false; 266 } 267 fOpenChoices= (TypeNameMatch[][]) openChoices.toArray(new TypeNameMatch[openChoices.size()][]); 268 fSourceRanges= (SourceRange[]) sourceRanges.toArray(new SourceRange[sourceRanges.size()]); 269 return true; 270 } finally { 271 monitor.done(); 272 } 273 } 274 275 private TypeNameMatch[] processTypeInfo(List typeRefsFound) { 276 int nFound= typeRefsFound.size(); 277 if (nFound == 0) { 278 return null; 280 } else if (nFound == 1) { 281 TypeNameMatch typeRef= (TypeNameMatch) typeRefsFound.get(0); 282 fImpStructure.addImport(typeRef.getFullyQualifiedName()); 283 return null; 284 } else { 285 String typeToImport= null; 286 boolean ambiguousImports= false; 287 288 for (int i= 0; i < nFound; i++) { 290 TypeNameMatch typeRef= (TypeNameMatch) typeRefsFound.get(i); 291 String fullName= typeRef.getFullyQualifiedName(); 292 String containerName= typeRef.getTypeContainerName(); 293 if (fOldSingleImports.contains(fullName)) { 294 fImpStructure.addImport(fullName); 296 return null; 297 } else if (fOldDemandImports.contains(containerName) || fImplicitImports.contains(containerName)) { 298 if (typeToImport == null) { 299 typeToImport= fullName; 300 } else { ambiguousImports= true; 302 } 303 } 304 } 305 306 if (typeToImport != null && !ambiguousImports) { 307 fImpStructure.addImport(typeToImport); 308 return null; 309 } 310 return (TypeNameMatch[]) typeRefsFound.toArray(new TypeNameMatch[nFound]); 312 } 313 } 314 315 private boolean isOfKind(TypeNameMatch curr, int typeKinds, boolean is50OrHigher) { 316 int flags= curr.getModifiers(); 317 if (Flags.isAnnotation(flags)) { 318 return is50OrHigher && ((typeKinds & SimilarElementsRequestor.ANNOTATIONS) != 0); 319 } 320 if (Flags.isEnum(flags)) { 321 return is50OrHigher && ((typeKinds & SimilarElementsRequestor.ENUMS) != 0); 322 } 323 if (Flags.isInterface(flags)) { 324 return (typeKinds & SimilarElementsRequestor.INTERFACES) != 0; 325 } 326 return (typeKinds & SimilarElementsRequestor.CLASSES) != 0; 327 } 328 329 private boolean isVisible(TypeNameMatch curr) { 330 int flags= curr.getModifiers(); 331 if (Flags.isPrivate(flags)) { 332 return false; 333 } 334 if (Flags.isPublic(flags) || Flags.isProtected(flags)) { 335 return true; 336 } 337 return curr.getPackageName().equals(fCurrPackage.getElementName()); 338 } 339 340 public TypeNameMatch[][] getChoices() { 341 return fOpenChoices; 342 } 343 344 public ISourceRange[] getChoicesSourceRanges() { 345 return fSourceRanges; 346 } 347 } 348 349 private boolean fDoSave; 350 351 private boolean fIgnoreLowerCaseNames; 352 353 private IChooseImportQuery fChooseImportQuery; 354 355 private int fNumberOfImportsAdded; 356 private int fNumberOfImportsRemoved; 357 358 private IProblem fParsingError; 359 private ICompilationUnit fCompilationUnit; 360 361 private CompilationUnit fASTRoot; 362 363 private final boolean fAllowSyntaxErrors; 364 365 public OrganizeImportsOperation(ICompilationUnit cu, CompilationUnit astRoot, boolean ignoreLowerCaseNames, boolean save, boolean allowSyntaxErrors, IChooseImportQuery chooseImportQuery) throws CoreException { 366 fCompilationUnit= cu; 367 fASTRoot= astRoot; 368 369 fDoSave= save; 370 fIgnoreLowerCaseNames= ignoreLowerCaseNames; 371 fAllowSyntaxErrors= allowSyntaxErrors; 372 fChooseImportQuery= chooseImportQuery; 373 374 fNumberOfImportsAdded= 0; 375 fNumberOfImportsRemoved= 0; 376 377 fParsingError= null; 378 } 379 380 386 public void run(IProgressMonitor monitor) throws CoreException, OperationCanceledException { 387 if (monitor == null) { 388 monitor= new NullProgressMonitor(); 389 } 390 try { 391 monitor.beginTask(Messages.format(CodeGenerationMessages.OrganizeImportsOperation_description, fCompilationUnit.getElementName()), 10); 392 393 TextEdit edit= createTextEdit(new SubProgressMonitor(monitor, 9)); 394 if (edit == null) 395 return; 396 397 JavaModelUtil.applyEdit(fCompilationUnit, edit, fDoSave, new SubProgressMonitor(monitor, 1)); 398 } finally { 399 monitor.done(); 400 } 401 } 402 403 public TextEdit createTextEdit(IProgressMonitor monitor) throws CoreException, OperationCanceledException { 404 if (monitor == null) { 405 monitor= new NullProgressMonitor(); 406 } 407 try { 408 fNumberOfImportsAdded= 0; 409 fNumberOfImportsRemoved= 0; 410 411 monitor.beginTask(Messages.format(CodeGenerationMessages.OrganizeImportsOperation_description, fCompilationUnit.getElementName()), 9); 412 413 CompilationUnit astRoot= fASTRoot; 414 if (astRoot == null) { 415 astRoot= ASTProvider.getASTProvider().getAST(fCompilationUnit, ASTProvider.WAIT_YES, new SubProgressMonitor(monitor, 2)); 416 if (monitor.isCanceled()) 417 throw new OperationCanceledException(); 418 } else { 419 monitor.worked(2); 420 } 421 422 ImportRewrite importsRewrite= StubUtility.createImportRewrite(astRoot, false); 423 424 Set oldSingleImports= new HashSet (); 425 Set oldDemandImports= new HashSet (); 426 List typeReferences= new ArrayList (); 427 List staticReferences= new ArrayList (); 428 429 if (!collectReferences(astRoot, typeReferences, staticReferences, oldSingleImports, oldDemandImports)) 430 return null; 431 432 monitor.worked(1); 433 434 TypeReferenceProcessor processor= new TypeReferenceProcessor(oldSingleImports, oldDemandImports, astRoot, importsRewrite, fIgnoreLowerCaseNames); 435 436 Iterator refIterator= typeReferences.iterator(); 437 while (refIterator.hasNext()) { 438 SimpleName typeRef= (SimpleName) refIterator.next(); 439 processor.add(typeRef); 440 } 441 442 boolean hasOpenChoices= processor.process(new SubProgressMonitor(monitor, 3)); 443 addStaticImports(staticReferences, importsRewrite); 444 445 if (hasOpenChoices && fChooseImportQuery != null) { 446 TypeNameMatch[][] choices= processor.getChoices(); 447 ISourceRange[] ranges= processor.getChoicesSourceRanges(); 448 TypeNameMatch[] chosen= fChooseImportQuery.chooseImports(choices, ranges); 449 if (chosen == null) { 450 throw new OperationCanceledException(); 452 } 453 for (int i= 0; i < chosen.length; i++) { 454 TypeNameMatch typeInfo= chosen[i]; 455 importsRewrite.addImport(typeInfo.getFullyQualifiedName()); 456 } 457 } 458 459 TextEdit result= importsRewrite.rewriteImports(new SubProgressMonitor(monitor, 3)); 460 461 determineImportDifferences(importsRewrite, oldSingleImports, oldDemandImports); 462 463 return result; 464 } finally { 465 monitor.done(); 466 } 467 } 468 469 private void determineImportDifferences(ImportRewrite importsStructure, Set oldSingleImports, Set oldDemandImports) { 470 ArrayList importsAdded= new ArrayList (); 471 importsAdded.addAll(Arrays.asList(importsStructure.getCreatedImports())); 472 importsAdded.addAll(Arrays.asList(importsStructure.getCreatedStaticImports())); 473 474 Object [] content= oldSingleImports.toArray(); 475 for (int i= 0; i < content.length; i++) { 476 String importName= (String ) content[i]; 477 if (importsAdded.remove(importName)) 478 oldSingleImports.remove(importName); 479 } 480 content= oldDemandImports.toArray(); 481 for (int i= 0; i < content.length; i++) { 482 String importName= (String ) content[i]; 483 if (importsAdded.remove(importName + ".*")) oldDemandImports.remove(importName); 485 } 486 fNumberOfImportsAdded= importsAdded.size(); 487 fNumberOfImportsRemoved= oldSingleImports.size() + oldDemandImports.size(); 488 } 489 490 491 private void addStaticImports(List staticReferences, ImportRewrite importsStructure) { 492 for (int i= 0; i < staticReferences.size(); i++) { 493 Name name= (Name) staticReferences.get(i); 494 IBinding binding= name.resolveBinding(); 495 if (binding != null) { importsStructure.addStaticImport(binding); 497 } 498 } 499 } 500 501 502 private boolean collectReferences(CompilationUnit astRoot, List typeReferences, List staticReferences, Set oldSingleImports, Set oldDemandImports) { 504 if (!fAllowSyntaxErrors) { 505 IProblem[] problems= astRoot.getProblems(); 506 for (int i= 0; i < problems.length; i++) { 507 IProblem curr= problems[i]; 508 if (curr.isError() && (curr.getID() & IProblem.Syntax) != 0) { 509 fParsingError= problems[i]; 510 return false; 511 } 512 } 513 } 514 List imports= astRoot.imports(); 515 for (int i= 0; i < imports.size(); i++) { 516 ImportDeclaration curr= (ImportDeclaration) imports.get(i); 517 String id= ASTResolving.getFullName(curr.getName()); 518 if (curr.isOnDemand()) { 519 oldDemandImports.add(id); 520 } else { 521 oldSingleImports.add(id); 522 } 523 } 524 525 IJavaProject project= fCompilationUnit.getJavaProject(); 526 ImportReferencesCollector.collect(astRoot, project, null, typeReferences, staticReferences); 527 528 return true; 529 } 530 531 536 public IProblem getParseError() { 537 return fParsingError; 538 } 539 540 public int getNumberOfImportsAdded() { 541 return fNumberOfImportsAdded; 542 } 543 544 public int getNumberOfImportsRemoved() { 545 return fNumberOfImportsRemoved; 546 } 547 548 551 public ISchedulingRule getScheduleRule() { 552 return ResourcesPlugin.getWorkspace().getRoot(); 553 } 554 555 } 556 | Popular Tags |