1 11 package org.eclipse.jdt.internal.corext.refactoring.rename; 12 13 import java.util.ArrayList ; 14 import java.util.Arrays ; 15 import java.util.Collection ; 16 import java.util.HashMap ; 17 import java.util.HashSet ; 18 import java.util.Iterator ; 19 import java.util.List ; 20 import java.util.Set ; 21 import java.util.Map.Entry; 22 23 import org.eclipse.text.edits.MalformedTreeException; 24 import org.eclipse.text.edits.ReplaceEdit; 25 import org.eclipse.text.edits.TextEdit; 26 27 import org.eclipse.core.runtime.Assert; 28 import org.eclipse.core.runtime.CoreException; 29 import org.eclipse.core.runtime.IPath; 30 import org.eclipse.core.runtime.IProgressMonitor; 31 import org.eclipse.core.runtime.SubProgressMonitor; 32 33 import org.eclipse.core.resources.IContainer; 34 import org.eclipse.core.resources.IFile; 35 import org.eclipse.core.resources.IFolder; 36 import org.eclipse.core.resources.IResource; 37 38 import org.eclipse.ltk.core.refactoring.Change; 39 import org.eclipse.ltk.core.refactoring.CompositeChange; 40 import org.eclipse.ltk.core.refactoring.IResourceMapper; 41 import org.eclipse.ltk.core.refactoring.RefactoringDescriptor; 42 import org.eclipse.ltk.core.refactoring.RefactoringStatus; 43 import org.eclipse.ltk.core.refactoring.RefactoringStatusContext; 44 import org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext; 45 import org.eclipse.ltk.core.refactoring.participants.RefactoringArguments; 46 import org.eclipse.ltk.core.refactoring.participants.RenameArguments; 47 48 import org.eclipse.jdt.core.Flags; 49 import org.eclipse.jdt.core.IClassFile; 50 import org.eclipse.jdt.core.ICompilationUnit; 51 import org.eclipse.jdt.core.IImportDeclaration; 52 import org.eclipse.jdt.core.IJavaElement; 53 import org.eclipse.jdt.core.IJavaProject; 54 import org.eclipse.jdt.core.IMethod; 55 import org.eclipse.jdt.core.IPackageFragment; 56 import org.eclipse.jdt.core.IPackageFragmentRoot; 57 import org.eclipse.jdt.core.IType; 58 import org.eclipse.jdt.core.JavaModelException; 59 import org.eclipse.jdt.core.Signature; 60 import org.eclipse.jdt.core.dom.rewrite.ImportRewrite; 61 import org.eclipse.jdt.core.refactoring.IJavaElementMapper; 62 import org.eclipse.jdt.core.refactoring.IJavaRefactorings; 63 import org.eclipse.jdt.core.refactoring.descriptors.JavaRefactoringDescriptor; 64 import org.eclipse.jdt.core.refactoring.descriptors.RenameJavaElementDescriptor; 65 import org.eclipse.jdt.core.search.IJavaSearchConstants; 66 import org.eclipse.jdt.core.search.IJavaSearchScope; 67 import org.eclipse.jdt.core.search.SearchEngine; 68 import org.eclipse.jdt.core.search.SearchMatch; 69 import org.eclipse.jdt.core.search.SearchPattern; 70 import org.eclipse.jdt.core.search.SearchRequestor; 71 72 import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility; 73 import org.eclipse.jdt.internal.corext.refactoring.Checks; 74 import org.eclipse.jdt.internal.corext.refactoring.JDTRefactoringDescriptor; 75 import org.eclipse.jdt.internal.corext.refactoring.JDTRefactoringDescriptorComment; 76 import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringArguments; 77 import org.eclipse.jdt.internal.corext.refactoring.RefactoringAvailabilityTester; 78 import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages; 79 import org.eclipse.jdt.internal.corext.refactoring.RefactoringScopeFactory; 80 import org.eclipse.jdt.internal.corext.refactoring.RefactoringSearchEngine; 81 import org.eclipse.jdt.internal.corext.refactoring.SearchResultGroup; 82 import org.eclipse.jdt.internal.corext.refactoring.base.JavaStatusContext; 83 import org.eclipse.jdt.internal.corext.refactoring.changes.DynamicValidationRefactoringChange; 84 import org.eclipse.jdt.internal.corext.refactoring.changes.RenamePackageChange; 85 import org.eclipse.jdt.internal.corext.refactoring.changes.TextChangeCompatibility; 86 import org.eclipse.jdt.internal.corext.refactoring.code.ScriptableRefactoring; 87 import org.eclipse.jdt.internal.corext.refactoring.participants.JavaProcessors; 88 import org.eclipse.jdt.internal.corext.refactoring.rename.RenamePackageProcessor.ImportsManager.ImportChange; 89 import org.eclipse.jdt.internal.corext.refactoring.tagging.IQualifiedNameUpdating; 90 import org.eclipse.jdt.internal.corext.refactoring.tagging.IReferenceUpdating; 91 import org.eclipse.jdt.internal.corext.refactoring.tagging.ITextUpdating; 92 import org.eclipse.jdt.internal.corext.refactoring.util.Changes; 93 import org.eclipse.jdt.internal.corext.refactoring.util.CommentAnalyzer; 94 import org.eclipse.jdt.internal.corext.refactoring.util.JavaElementUtil; 95 import org.eclipse.jdt.internal.corext.refactoring.util.QualifiedNameFinder; 96 import org.eclipse.jdt.internal.corext.refactoring.util.QualifiedNameSearchResult; 97 import org.eclipse.jdt.internal.corext.refactoring.util.ResourceUtil; 98 import org.eclipse.jdt.internal.corext.refactoring.util.TextChangeManager; 99 import org.eclipse.jdt.internal.corext.util.Messages; 100 import org.eclipse.jdt.internal.corext.util.Resources; 101 import org.eclipse.jdt.internal.corext.util.SearchUtils; 102 103 import org.eclipse.jdt.internal.ui.JavaPlugin; 104 import org.eclipse.jdt.internal.ui.refactoring.RefactoringSaveHelper; 105 106 public class RenamePackageProcessor extends JavaRenameProcessor implements 107 IReferenceUpdating, ITextUpdating, IQualifiedNameUpdating, IResourceMapper, IJavaElementMapper { 108 109 private static final String ATTRIBUTE_QUALIFIED= "qualified"; private static final String ATTRIBUTE_TEXTUAL_MATCHES= "textual"; private static final String ATTRIBUTE_PATTERNS= "patterns"; private static final String ATTRIBUTE_HIERARCHICAL= "hierarchical"; 114 private IPackageFragment fPackage; 115 116 private TextChangeManager fChangeManager; 117 private ImportsManager fImportsManager; 118 private QualifiedNameSearchResult fQualifiedNameSearchResult; 119 120 private boolean fUpdateReferences; 121 private boolean fUpdateTextualMatches; 122 private boolean fUpdateQualifiedNames; 123 private String fFilePatterns; 124 private boolean fRenameSubpackages; 125 126 public static final String IDENTIFIER= "org.eclipse.jdt.ui.renamePackageProcessor"; private RenamePackageChange fRenamePackageChange; 128 129 133 public RenamePackageProcessor(IPackageFragment fragment) { 134 fPackage= fragment; 135 if (fPackage != null) 136 setNewElementName(fPackage.getElementName()); 137 fUpdateReferences= true; 138 fUpdateTextualMatches= false; 139 fRenameSubpackages= false; 140 } 141 142 public String getIdentifier() { 143 return IDENTIFIER; 144 } 145 146 public boolean isApplicable() throws CoreException { 147 return RefactoringAvailabilityTester.isRenameAvailable(fPackage); 148 } 149 150 public String getProcessorName(){ 151 return RefactoringCoreMessages.RenamePackageRefactoring_name; 152 } 153 154 protected String [] getAffectedProjectNatures() throws CoreException { 155 return JavaProcessors.computeAffectedNatures(fPackage); 156 } 157 158 public Object [] getElements() { 159 return new Object [] {fPackage}; 160 } 161 162 protected RenameModifications computeRenameModifications() throws CoreException { 163 RenameModifications result= new RenameModifications(); 164 result.rename(fPackage, new RenameArguments(getNewElementName(), getUpdateReferences()), fRenameSubpackages); 165 return result; 166 } 167 168 protected IFile[] getChangedFiles() throws CoreException { 169 Set combined= new HashSet (); 170 combined.addAll(Arrays.asList(ResourceUtil.getFiles(fChangeManager.getAllCompilationUnits()))); 171 if (fRenameSubpackages) { 172 IPackageFragment[] allPackages= JavaElementUtil.getPackageAndSubpackages(fPackage); 173 for (int i= 0; i < allPackages.length; i++) { 174 combined.addAll(Arrays.asList(ResourceUtil.getFiles(allPackages[i].getCompilationUnits()))); 175 } 176 } else { 177 combined.addAll(Arrays.asList(ResourceUtil.getFiles(fPackage.getCompilationUnits()))); 178 } 179 if (fQualifiedNameSearchResult != null) 180 combined.addAll(Arrays.asList(fQualifiedNameSearchResult.getAllFiles())); 181 return (IFile[]) combined.toArray(new IFile[combined.size()]); 182 } 183 184 public int getSaveMode() { 185 return RefactoringSaveHelper.SAVE_ALL; 186 } 187 188 190 public boolean canEnableTextUpdating() { 191 return true; 192 } 193 194 public boolean getUpdateTextualMatches() { 195 return fUpdateTextualMatches; 196 } 197 198 public void setUpdateTextualMatches(boolean update) { 199 fUpdateTextualMatches= update; 200 } 201 202 204 public boolean canEnableUpdateReferences() { 205 return true; 206 } 207 208 public void setUpdateReferences(boolean update) { 209 fUpdateReferences= update; 210 } 211 212 public boolean getUpdateReferences(){ 213 return fUpdateReferences; 214 } 215 216 218 public boolean canEnableQualifiedNameUpdating() { 219 return !fPackage.isDefaultPackage(); 220 } 221 222 public boolean getUpdateQualifiedNames() { 223 return fUpdateQualifiedNames; 224 } 225 226 public void setUpdateQualifiedNames(boolean update) { 227 fUpdateQualifiedNames= update; 228 } 229 230 public String getFilePatterns() { 231 return fFilePatterns; 232 } 233 234 public void setFilePatterns(String patterns) { 235 Assert.isNotNull(patterns); 236 fFilePatterns= patterns; 237 } 238 239 241 public IResource getRefactoredResource(IResource element) { 242 IFolder packageFolder= (IFolder) fPackage.getResource(); 243 if (packageFolder == null) 244 return element; 245 246 IContainer newPackageFolder= (IContainer) getNewPackage().getResource(); 247 248 if (packageFolder.equals(element)) 249 return newPackageFolder; 250 251 IPath packagePath= packageFolder.getProjectRelativePath(); 252 IPath elementPath= element.getProjectRelativePath(); 253 254 if (packagePath.isPrefixOf(elementPath)) { 255 if (fRenameSubpackages || (element instanceof IFile && packageFolder.equals(element.getParent()))) { 256 IPath pathInPackage= elementPath.removeFirstSegments(packagePath.segmentCount()); 257 if (element instanceof IFile) 258 return newPackageFolder.getFile(pathInPackage); 259 else 260 return newPackageFolder.getFolder(pathInPackage); 261 } 262 } 263 return element; 264 } 265 266 268 public IJavaElement getRefactoredJavaElement(IJavaElement element) { 269 return new GenericRefactoringHandleTransplanter() { 270 protected IPackageFragment transplantHandle(IPackageFragmentRoot parent, IPackageFragment element) { 271 if (! fRenameSubpackages) { 272 if (fPackage.equals(element)) 273 return getNewPackage(); 274 } else { 275 String packName= element.getElementName(); 276 String packageName= fPackage.getElementName(); 277 if (fPackage.getParent().equals(parent) 278 && packName.startsWith(packageName + '.')) { 279 String newPackName= getNewElementName() + packName.substring(packageName.length() - 1); 280 return getPackageFragmentRoot().getPackageFragment(newPackName); 281 } 282 } 283 return super.transplantHandle(parent, element); 284 } 285 286 protected IMethod transplantHandle(IType parent, IMethod element) { 287 String [] parameterTypes= resolveParameterTypes(element); 288 return parent.getMethod(element.getElementName(), parameterTypes); 289 } 290 291 private String [] resolveParameterTypes(IMethod method) { 292 final String [] oldParameterTypes= method.getParameterTypes(); 293 final String [] newparams= new String [oldParameterTypes.length]; 294 295 final String [] possibleOldSigs= new String [2]; 296 possibleOldSigs[0]= Signature.createTypeSignature(fPackage.getElementName(), false); 298 possibleOldSigs[1]= Signature.createTypeSignature(fPackage.getElementName(), true); 299 300 final String [] possibleNewSigs= new String [2]; 301 possibleNewSigs[0]= Signature.createTypeSignature(getNewElementName(), false); 302 possibleNewSigs[1]= Signature.createTypeSignature(getNewElementName(), true); 303 304 for (int i= 0; i < oldParameterTypes.length; i++) { 307 newparams[i]= oldParameterTypes[i]; 308 for (int j= 0; j < possibleOldSigs.length; j++) { 309 newparams[i]= replaceAll(newparams[i], possibleOldSigs[j], possibleNewSigs[j]); 310 } 311 } 312 return newparams; 313 } 314 315 private String replaceAll(final String source, final String replaceFrom, final String replaceTo) { 316 final StringBuffer buf= new StringBuffer (source.length()); 317 int currentIndex= 0; 318 int matchIndex; 319 while ((matchIndex= source.indexOf(replaceFrom, currentIndex)) != -1) { 320 buf.append(source.substring(currentIndex, matchIndex)); 321 buf.append(replaceTo); 322 currentIndex= matchIndex + replaceFrom.length(); 323 } 324 buf.append(source.substring(currentIndex)); 325 return buf.toString(); 326 } 327 }.transplantHandle(element); 328 } 329 330 332 public boolean canEnableRenameSubpackages() throws JavaModelException { 333 return fPackage.hasSubpackages(); 334 } 335 336 public boolean getRenameSubpackages() { 337 return fRenameSubpackages; 338 } 339 340 public void setRenameSubpackages(boolean rename) { 341 fRenameSubpackages= rename; 342 } 343 344 346 public final String getCurrentElementName(){ 347 return fPackage.getElementName(); 348 } 349 350 public String getCurrentElementQualifier() { 351 return ""; } 353 354 public RefactoringStatus checkNewElementName(String newName) throws CoreException { 355 Assert.isNotNull(newName, "new name"); RefactoringStatus result= Checks.checkPackageName(newName); 357 if (result.hasFatalError()) 358 return result; 359 if (Checks.isAlreadyNamed(fPackage, newName)) { 360 result.addFatalError(RefactoringCoreMessages.RenamePackageRefactoring_another_name); 361 return result; 362 } 363 result.merge(checkPackageInCurrentRoot(newName)); 364 return result; 365 } 366 367 public Object getNewElement(){ 368 return getNewPackage(); 369 } 370 371 private IPackageFragment getNewPackage() { 372 IPackageFragmentRoot root= getPackageFragmentRoot(); 373 return root.getPackageFragment(getNewElementName()); 374 } 375 376 public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException { 377 return new RefactoringStatus(); 378 } 379 380 protected RefactoringStatus doCheckFinalConditions(IProgressMonitor pm, CheckConditionsContext context) throws CoreException { 381 try{ 382 pm.beginTask("", 23 + (fUpdateQualifiedNames ? 10 : 0) + (fUpdateTextualMatches ? 10 : 0)); pm.setTaskName(RefactoringCoreMessages.RenamePackageRefactoring_checking); 384 RefactoringStatus result= new RefactoringStatus(); 385 result.merge(checkNewElementName(getNewElementName())); 386 pm.worked(1); 387 result.merge(checkForMainAndNativeMethods()); 388 pm.worked(2); 389 390 if (fPackage.isReadOnly()){ 391 String message= Messages.format(RefactoringCoreMessages.RenamePackageRefactoring_Packagered_only, fPackage.getElementName()); 392 result.addFatalError(message); 393 } else if (Resources.isReadOnly(fPackage.getResource())) { 394 String message= Messages.format(RefactoringCoreMessages.RenamePackageRefactoring_resource_read_only, fPackage.getElementName()); 395 result.addError(message); 396 } 397 398 result.merge(checkPackageName(getNewElementName())); 399 if (result.hasFatalError()) 400 return result; 401 402 fChangeManager= new TextChangeManager(); 403 fImportsManager= new ImportsManager(); 404 405 SubProgressMonitor subPm= new SubProgressMonitor(pm, 16); 406 if (fRenameSubpackages) { 407 IPackageFragment[] allSubpackages= JavaElementUtil.getPackageAndSubpackages(fPackage); 408 subPm.beginTask("", allSubpackages.length); for (int i= 0; i < allSubpackages.length; i++) { 410 new PackageRenamer(allSubpackages[i], this, fChangeManager, fImportsManager).doRename(new SubProgressMonitor(subPm, 1), result); 411 } 412 subPm.done(); 413 } else { 414 new PackageRenamer(fPackage, this, fChangeManager, fImportsManager).doRename(subPm, result); 415 } 416 417 fImportsManager.rewriteImports(fChangeManager, new SubProgressMonitor(pm, 3)); 418 419 if (fUpdateTextualMatches) { 420 pm.subTask(RefactoringCoreMessages.RenamePackageRefactoring_searching_text); 421 TextMatchUpdater.perform(new SubProgressMonitor(pm, 10), RefactoringScopeFactory.create(fPackage), this, fChangeManager, new SearchResultGroup[0]); 422 } 423 424 if (fUpdateQualifiedNames) 425 computeQualifiedNameMatches(new SubProgressMonitor(pm, 10)); 426 427 return result; 428 } finally{ 429 pm.done(); 430 } 431 } 432 433 public IPackageFragment getPackage() { 434 return fPackage; 435 } 436 437 private RefactoringStatus checkForMainAndNativeMethods() throws CoreException{ 438 RefactoringStatus result= new RefactoringStatus(); 439 if (fRenameSubpackages) { 440 IPackageFragment[] allSubpackages= JavaElementUtil.getPackageAndSubpackages(fPackage); 441 for (int i= 0; i < allSubpackages.length; i++) { 442 ICompilationUnit[] cus= allSubpackages[i].getCompilationUnits(); 443 for (int c= 0; c < cus.length; c++) 444 result.merge(Checks.checkForMainAndNativeMethods(cus[c])); 445 } 446 } else { 447 ICompilationUnit[] cus= fPackage.getCompilationUnits(); 448 for (int i= 0; i < cus.length; i++) 449 result.merge(Checks.checkForMainAndNativeMethods(cus[i])); 450 } 451 return result; 452 } 453 454 459 public static boolean isPackageNameOkInRoot(String newName, IPackageFragmentRoot root) throws CoreException { 460 IPackageFragment pack= root.getPackageFragment(newName); 461 if (! pack.exists()) 462 return true; 463 else if (pack.containsJavaResources()) 464 return false; 465 else if (pack.getNonJavaResources().length != 0) 466 return false; 467 else 468 return true; 469 } 470 471 private RefactoringStatus checkPackageInCurrentRoot(String newName) throws CoreException { 472 if (fRenameSubpackages) { 473 String currentName= getCurrentElementName(); 474 if (isAncestorPackage(currentName, newName)) { 475 return null; 477 } 478 if (! isAncestorPackage(newName, currentName)) { 479 if (! isPackageNameOkInRoot(newName, getPackageFragmentRoot())) { 481 return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.RenamePackageRefactoring_package_exists); 482 } 483 } 484 IPackageFragment[] packsToRename= JavaElementUtil.getPackageAndSubpackages(fPackage); 488 for (int i = 0; i < packsToRename.length; i++) { 489 IPackageFragment pack = packsToRename[i]; 490 String newPack= newName + pack.getElementName().substring(currentName.length()); 491 if (! isAncestorPackage(currentName, newPack) && ! isPackageNameOkInRoot(newPack, getPackageFragmentRoot())) { 492 String msg= Messages.format(RefactoringCoreMessages.RenamePackageProcessor_subpackage_collides, newPack); 493 return RefactoringStatus.createFatalErrorStatus(msg); 494 } 495 } 496 return null; 497 498 } else if (! isPackageNameOkInRoot(newName, getPackageFragmentRoot())) { 499 return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.RenamePackageRefactoring_package_exists); 500 } else { 501 return null; 502 } 503 } 504 505 private boolean isAncestorPackage(String ancestor, String descendant) { 506 int a= ancestor.length(); 507 int d= descendant.length(); 508 if (a == d || (a < d && descendant.charAt(a) == '.')) 509 return descendant.startsWith(ancestor); 510 else 511 return false; 512 } 513 514 private IPackageFragmentRoot getPackageFragmentRoot() { 515 return ((IPackageFragmentRoot)fPackage.getParent()); 516 } 517 518 private RefactoringStatus checkPackageName(String newName) throws CoreException { 519 RefactoringStatus status= new RefactoringStatus(); 520 IPackageFragmentRoot[] roots= fPackage.getJavaProject().getPackageFragmentRoots(); 521 Set topLevelTypeNames= getTopLevelTypeNames(); 522 for (int i= 0; i < roots.length; i++) { 523 if (! isPackageNameOkInRoot(newName, roots[i])){ 524 String message= Messages.format(RefactoringCoreMessages.RenamePackageRefactoring_aleady_exists, new Object []{getNewElementName(), roots[i].getElementName()}); 525 status.merge(RefactoringStatus.createWarningStatus(message)); 526 status.merge(checkTypeNameConflicts(roots[i], newName, topLevelTypeNames)); 527 } 528 } 529 return status; 530 } 531 532 private Set getTopLevelTypeNames() throws CoreException { 533 ICompilationUnit[] cus= fPackage.getCompilationUnits(); 534 Set result= new HashSet (2 * cus.length); 535 for (int i= 0; i < cus.length; i++) { 536 result.addAll(getTopLevelTypeNames(cus[i])); 537 } 538 return result; 539 } 540 541 private static Collection getTopLevelTypeNames(ICompilationUnit iCompilationUnit) throws CoreException { 542 IType[] types= iCompilationUnit.getTypes(); 543 List result= new ArrayList (types.length); 544 for (int i= 0; i < types.length; i++) { 545 result.add(types[i].getElementName()); 546 } 547 return result; 548 } 549 550 private RefactoringStatus checkTypeNameConflicts(IPackageFragmentRoot root, String newName, Set topLevelTypeNames) throws CoreException { 551 IPackageFragment otherPack= root.getPackageFragment(newName); 552 if (fPackage.equals(otherPack)) 553 return null; 554 ICompilationUnit[] cus= otherPack.getCompilationUnits(); 555 RefactoringStatus result= new RefactoringStatus(); 556 for (int i= 0; i < cus.length; i++) { 557 result.merge(checkTypeNameConflicts(cus[i], topLevelTypeNames)); 558 } 559 return result; 560 } 561 562 private RefactoringStatus checkTypeNameConflicts(ICompilationUnit iCompilationUnit, Set topLevelTypeNames) throws CoreException { 563 RefactoringStatus result= new RefactoringStatus(); 564 IType[] types= iCompilationUnit.getTypes(); 565 String packageName= iCompilationUnit.getParent().getElementName(); 566 for (int i= 0; i < types.length; i++) { 567 String name= types[i].getElementName(); 568 if (topLevelTypeNames.contains(name)){ 569 String [] keys= {packageName, name}; 570 String msg= Messages.format(RefactoringCoreMessages.RenamePackageRefactoring_contains_type, keys); 571 RefactoringStatusContext context= JavaStatusContext.create(types[i]); 572 result.addError(msg, context); 573 } 574 } 575 return result; 576 } 577 578 public Change createChange(IProgressMonitor monitor) throws CoreException { 579 try { 580 monitor.beginTask(RefactoringCoreMessages.RenamePackageRefactoring_creating_change, 1); 581 final RenameJavaElementDescriptor descriptor= createRefactoringDescriptor(); 582 final DynamicValidationRefactoringChange result= new DynamicValidationRefactoringChange(descriptor, RefactoringCoreMessages.RenamePackageRefactoring_change_name); 583 result.addAll(fChangeManager.getAllChanges()); 584 fRenamePackageChange= new RenamePackageChange( fPackage, getNewElementName(), fRenameSubpackages); 585 result.add(fRenamePackageChange); 586 monitor.worked(1); 587 return result; 588 } finally { 589 fChangeManager= null; 590 fImportsManager= null; 591 monitor.done(); 592 } 593 } 594 595 private RenameJavaElementDescriptor createRefactoringDescriptor() { 596 String project= null; 597 IJavaProject javaProject= fPackage.getJavaProject(); 598 if (javaProject != null) 599 project= javaProject.getElementName(); 600 final int flags= JavaRefactoringDescriptor.JAR_MIGRATION | JavaRefactoringDescriptor.JAR_REFACTORING | RefactoringDescriptor.STRUCTURAL_CHANGE | RefactoringDescriptor.MULTI_CHANGE; 601 final String description= Messages.format(RefactoringCoreMessages.RenamePackageProcessor_descriptor_description_short, fPackage.getElementName()); 602 final String header= Messages.format(RefactoringCoreMessages.RenamePackageProcessor_descriptor_description, new String [] { fPackage.getElementName(), getNewElementName()}); 603 final JDTRefactoringDescriptorComment comment= new JDTRefactoringDescriptorComment(project, this, header); 604 if (fRenameSubpackages) 605 comment.addSetting(RefactoringCoreMessages.RenamePackageProcessor_rename_subpackages); 606 final RenameJavaElementDescriptor descriptor= new RenameJavaElementDescriptor(IJavaRefactorings.RENAME_PACKAGE); 607 descriptor.setProject(project); 608 descriptor.setDescription(description); 609 descriptor.setComment(comment.asString()); 610 descriptor.setFlags(flags); 611 descriptor.setJavaElement(fPackage); 612 descriptor.setNewName(getNewElementName()); 613 descriptor.setUpdateReferences(fUpdateReferences); 614 descriptor.setUpdateTextualOccurrences(fUpdateTextualMatches); 615 descriptor.setUpdateQualifiedNames(fUpdateQualifiedNames); 616 if (fUpdateQualifiedNames && fFilePatterns != null && !"".equals(fFilePatterns)) descriptor.setFileNamePatterns(fFilePatterns); 618 descriptor.setUpdateHierarchy(fRenameSubpackages); 619 return descriptor; 620 } 621 622 public Change postCreateChange(Change[] participantChanges, IProgressMonitor pm) throws CoreException { 623 if (fQualifiedNameSearchResult != null) { 624 CompositeChange parent= (CompositeChange) fRenamePackageChange.getParent(); 625 try { 626 631 parent.remove(fRenamePackageChange); 632 parent.add(fQualifiedNameSearchResult.getSingleChange(Changes.getModifiedFiles(participantChanges))); 633 } finally { 634 fQualifiedNameSearchResult= null; 635 parent.add(fRenamePackageChange); 636 fRenamePackageChange= null; 637 } 638 } 639 return null; 640 } 641 642 private void computeQualifiedNameMatches(IProgressMonitor pm) throws CoreException { 643 if (fQualifiedNameSearchResult == null) 644 fQualifiedNameSearchResult= new QualifiedNameSearchResult(); 645 QualifiedNameFinder.process(fQualifiedNameSearchResult, fPackage.getElementName(), getNewElementName(), 646 fFilePatterns, fPackage.getJavaProject().getProject(), pm); 647 } 648 649 public String getNewPackageName(String oldSubPackageName) { 650 String oldPackageName= getPackage().getElementName(); 651 return getNewElementName() + oldSubPackageName.substring(oldPackageName.length()); 652 } 653 654 private static class PackageRenamer { 655 private final IPackageFragment fPackage; 656 private final RenamePackageProcessor fProcessor; 657 private final TextChangeManager fTextChangeManager; 658 private final ImportsManager fImportsManager; 659 660 661 private SearchResultGroup[] fOccurrences; 662 663 670 private List fReferencesToTypesInNamesakes; 671 672 675 private List fReferencesToTypesInPackage; 676 677 public PackageRenamer(IPackageFragment pack, RenamePackageProcessor processor, TextChangeManager textChangeManager, ImportsManager importsManager) { 678 fPackage= pack; 679 fProcessor= processor; 680 fTextChangeManager= textChangeManager; 681 fImportsManager= importsManager; 682 } 683 684 void doRename(IProgressMonitor pm, RefactoringStatus result) throws CoreException { 685 pm.beginTask("", 16); if (fProcessor.getUpdateReferences()){ 687 pm.setTaskName(RefactoringCoreMessages.RenamePackageRefactoring_searching); 688 fOccurrences= getReferences(new SubProgressMonitor(pm, 4), result); 689 fReferencesToTypesInNamesakes= getReferencesToTypesInNamesakes(new SubProgressMonitor(pm, 4), result); 690 fReferencesToTypesInPackage= getReferencesToTypesInPackage(new SubProgressMonitor(pm, 4), result); 691 pm.setTaskName(RefactoringCoreMessages.RenamePackageRefactoring_checking); 692 result.merge(analyzeAffectedCompilationUnits()); 693 pm.worked(1); 694 } else { 695 fOccurrences= new SearchResultGroup[0]; 696 pm.worked(13); 697 } 698 699 if (result.hasFatalError()) 700 return; 701 702 if (fProcessor.getUpdateReferences()) 703 addReferenceUpdates(new SubProgressMonitor(pm, 3)); 704 else 705 pm.worked(3); 706 707 pm.done(); 708 } 709 710 private SearchResultGroup[] getReferences(IProgressMonitor pm, RefactoringStatus status) throws CoreException { 711 IJavaSearchScope scope= RefactoringScopeFactory.create(fPackage); 712 SearchPattern pattern= SearchPattern.createPattern(fPackage, IJavaSearchConstants.REFERENCES); 713 return RefactoringSearchEngine.search(pattern, scope, pm, status); 714 } 715 716 private void addReferenceUpdates(IProgressMonitor pm) throws CoreException { 717 pm.beginTask("", fOccurrences.length + fReferencesToTypesInPackage.size() + fReferencesToTypesInNamesakes.size()); for (int i= 0; i < fOccurrences.length; i++){ 719 ICompilationUnit cu= fOccurrences[i].getCompilationUnit(); 720 if (cu == null) 721 continue; 722 SearchMatch[] results= fOccurrences[i].getSearchResults(); 723 for (int j= 0; j < results.length; j++){ 724 SearchMatch result= results[j]; 725 IJavaElement enclosingElement= SearchUtils.getEnclosingJavaElement(result); 726 if (enclosingElement instanceof IImportDeclaration) { 727 IImportDeclaration importDeclaration= (IImportDeclaration) enclosingElement; 728 String updatedImport= getUpdatedImport(importDeclaration); 729 updateImport(cu, importDeclaration, updatedImport); 730 } else { TextChangeCompatibility.addTextEdit(fTextChangeManager.get(cu), RefactoringCoreMessages.RenamePackageRefactoring_update_reference, createTextChange(result)); 732 } 733 } 734 if (fReferencesToTypesInNamesakes.size() != 0) { 735 SearchResultGroup typeRefsRequiringOldNameImport= extractGroupFor(cu, fReferencesToTypesInNamesakes); 736 if (typeRefsRequiringOldNameImport != null) 737 addTypeImports(typeRefsRequiringOldNameImport); 738 } 739 if (fReferencesToTypesInPackage.size() != 0) { 740 SearchResultGroup typeRefsRequiringNewNameImport= extractGroupFor(cu, fReferencesToTypesInPackage); 741 if (typeRefsRequiringNewNameImport != null) 742 updateTypeImports(typeRefsRequiringNewNameImport); 743 } 744 pm.worked(1); 745 } 746 747 if (fReferencesToTypesInNamesakes.size() != 0) { 748 for (Iterator iter= fReferencesToTypesInNamesakes.iterator(); iter.hasNext();) { 749 SearchResultGroup referencesToTypesInNamesakes= (SearchResultGroup) iter.next(); 750 addTypeImports(referencesToTypesInNamesakes); 751 pm.worked(1); 752 } 753 } 754 if (fReferencesToTypesInPackage.size() != 0) { 755 for (Iterator iter= fReferencesToTypesInPackage.iterator(); iter.hasNext();) { 756 SearchResultGroup namesakeReferencesToPackage= (SearchResultGroup) iter.next(); 757 updateTypeImports(namesakeReferencesToPackage); 758 pm.worked(1); 759 } 760 } 761 pm.done(); 762 } 763 764 767 private static SearchResultGroup extractGroupFor(ICompilationUnit cu, List searchResultGroups) { 768 for (Iterator iter= searchResultGroups.iterator(); iter.hasNext();) { 769 SearchResultGroup group= (SearchResultGroup) iter.next(); 770 if (cu.equals(group.getCompilationUnit())) { 771 iter.remove(); 772 return group; 773 } 774 } 775 return null; 776 } 777 778 private TextEdit createTextChange(SearchMatch searchResult) { 779 return new ReplaceEdit(searchResult.getOffset(), searchResult.getLength(), getNewPackageName()); 780 } 781 782 private RefactoringStatus analyzeAffectedCompilationUnits() throws CoreException { 783 RefactoringStatus result= new RefactoringStatus(); 785 fOccurrences= Checks.excludeCompilationUnits(fOccurrences, result); 786 if (result.hasFatalError()) 787 return result; 788 789 result.merge(Checks.checkCompileErrorsInAffectedFiles(fOccurrences)); 790 return result; 791 } 792 793 798 private IJavaSearchScope getPackageAndOccurrencesWithoutNamesakesScope() { 799 List scopeList= new ArrayList (); 800 scopeList.add(fPackage); 801 for (int i= 0; i < fOccurrences.length; i++) { 802 ICompilationUnit cu= fOccurrences[i].getCompilationUnit(); 803 if (cu == null) 804 continue; 805 IPackageFragment pack= (IPackageFragment) cu.getParent(); 806 if (! pack.getElementName().equals(fPackage.getElementName())) 807 scopeList.add(cu); 808 } 809 return SearchEngine.createJavaSearchScope((IJavaElement[]) scopeList.toArray(new IJavaElement[scopeList.size()])); 810 } 811 812 private List getReferencesToTypesInNamesakes(IProgressMonitor pm, RefactoringStatus status) throws CoreException { 813 pm.beginTask("", 2); 817 IJavaElement[] elements= new IJavaElement[fOccurrences.length + 1]; 819 for (int i= 0; i < fOccurrences.length; i++) { 820 elements[i]= fOccurrences[i].getCompilationUnit(); 821 } 822 elements[fOccurrences.length]= fPackage; 823 IJavaSearchScope namesakePackagesScope= RefactoringScopeFactory.createReferencedScope(elements); 824 IPackageFragment[] namesakePackages= getNamesakePackages(namesakePackagesScope, new SubProgressMonitor(pm, 1)); 825 if (namesakePackages.length == 0) { 826 pm.done(); 827 return new ArrayList (0); 828 } 829 830 IType[] typesToSearch= getTypesInPackages(namesakePackages); 834 if (typesToSearch.length == 0) { 835 pm.done(); 836 return new ArrayList (0); 837 } 838 SearchPattern pattern= RefactoringSearchEngine.createOrPattern(typesToSearch, IJavaSearchConstants.REFERENCES); 839 IJavaSearchScope scope= getPackageAndOccurrencesWithoutNamesakesScope(); 840 SearchResultGroup[] results= RefactoringSearchEngine.search(pattern, scope, new SubProgressMonitor(pm, 1), status); 841 pm.done(); 842 return new ArrayList (Arrays.asList(results)); 843 } 844 845 private List getReferencesToTypesInPackage(IProgressMonitor pm, RefactoringStatus status) throws CoreException { 846 pm.beginTask("", 2); IJavaSearchScope referencedFromNamesakesScope= RefactoringScopeFactory.create(fPackage); 848 IPackageFragment[] namesakePackages= getNamesakePackages(referencedFromNamesakesScope, new SubProgressMonitor(pm, 1)); 849 if (namesakePackages.length == 0) { 850 pm.done(); 851 return new ArrayList (0); 852 } 853 854 IJavaSearchScope scope= SearchEngine.createJavaSearchScope(namesakePackages); 855 IType[] typesToSearch= getTypesInPackage(fPackage); 856 if (typesToSearch.length == 0) { 857 pm.done(); 858 return new ArrayList (0); 859 } 860 SearchPattern pattern= RefactoringSearchEngine.createOrPattern(typesToSearch, IJavaSearchConstants.REFERENCES); 861 SearchResultGroup[] results= RefactoringSearchEngine.search(pattern, scope, new SubProgressMonitor(pm, 1), status); 862 pm.done(); 863 return new ArrayList (Arrays.asList(results)); 864 } 865 866 private IType[] getTypesInPackage(IPackageFragment packageFragment) throws JavaModelException { 867 List types= new ArrayList (); 868 addContainedTypes(packageFragment, types); 869 return (IType[]) types.toArray(new IType[types.size()]); 870 } 871 872 875 private IPackageFragment[] getNamesakePackages(IJavaSearchScope scope, IProgressMonitor pm) throws CoreException { 876 SearchPattern pattern= SearchPattern.createPattern(fPackage.getElementName(), IJavaSearchConstants.PACKAGE, IJavaSearchConstants.DECLARATIONS, SearchPattern.R_EXACT_MATCH | SearchPattern.R_CASE_SENSITIVE); 877 878 final HashSet packageFragments= new HashSet (); 879 SearchRequestor requestor= new SearchRequestor() { 880 public void acceptSearchMatch(SearchMatch match) throws CoreException { 881 IJavaElement enclosingElement= SearchUtils.getEnclosingJavaElement(match); 882 if (enclosingElement instanceof IPackageFragment) { 883 IPackageFragment pack= (IPackageFragment) enclosingElement; 884 if (! fPackage.equals(pack)) 885 packageFragments.add(pack); 886 } 887 } 888 }; 889 new SearchEngine().search(pattern, SearchUtils.getDefaultSearchParticipants(), scope, requestor, pm); 890 891 return (IPackageFragment[]) packageFragments.toArray(new IPackageFragment[packageFragments.size()]); 892 } 893 894 private IType[] getTypesInPackages(IPackageFragment[] packageFragments) throws JavaModelException { 895 List types= new ArrayList (); 896 for (int i= 0; i < packageFragments.length; i++) { 897 IPackageFragment pack= packageFragments[i]; 898 addContainedTypes(pack, types); 899 } 900 return (IType[]) types.toArray(new IType[types.size()]); 901 } 902 903 private void addContainedTypes(IPackageFragment pack, List typesCollector) throws JavaModelException { 904 IJavaElement[] children= pack.getChildren(); 905 for (int c= 0; c < children.length; c++) { 906 IJavaElement child= children[c]; 907 if (child instanceof ICompilationUnit) { 908 typesCollector.addAll(Arrays.asList(((ICompilationUnit) child).getTypes())); 909 } else if (child instanceof IClassFile) { 910 typesCollector.add(((IClassFile) child).getType()); 911 } 912 } 913 } 914 915 private void updateImport(ICompilationUnit cu, IImportDeclaration importDeclaration, String updatedImport) throws JavaModelException { 916 ImportChange importChange= fImportsManager.getImportChange(cu); 917 if (Flags.isStatic(importDeclaration.getFlags())) { 918 importChange.removeStaticImport(importDeclaration.getElementName()); 919 importChange.addStaticImport(Signature.getQualifier(updatedImport), Signature.getSimpleName(updatedImport)); 920 } else { 921 importChange.removeImport(importDeclaration.getElementName()); 922 importChange.addImport(updatedImport); 923 } 924 } 925 926 929 private void addTypeImports(SearchResultGroup typeReferences) throws CoreException { 930 SearchMatch[] searchResults= typeReferences.getSearchResults(); 931 for (int i= 0; i < searchResults.length; i++) { 932 SearchMatch result= searchResults[i]; 933 IJavaElement enclosingElement= SearchUtils.getEnclosingJavaElement(result); 934 if (! (enclosingElement instanceof IImportDeclaration)) { 935 String reference= getNormalizedTypeReference(result); 936 if (! reference.startsWith(fPackage.getElementName())) { 937 reference= cutOffInnerTypes(reference); 939 ImportChange importChange= fImportsManager.getImportChange(typeReferences.getCompilationUnit()); 940 importChange.addImport(fPackage.getElementName() + '.' + reference); 941 } 942 } 943 } 944 } 945 946 950 private void updateTypeImports(SearchResultGroup typeReferences) throws CoreException { 951 SearchMatch[] searchResults= typeReferences.getSearchResults(); 952 for (int i= 0; i < searchResults.length; i++) { 953 SearchMatch result= searchResults[i]; 954 IJavaElement enclosingElement= SearchUtils.getEnclosingJavaElement(result); 955 if (enclosingElement instanceof IImportDeclaration) { 956 IImportDeclaration importDeclaration= (IImportDeclaration) enclosingElement; 957 updateImport(typeReferences.getCompilationUnit(), importDeclaration, getUpdatedImport(importDeclaration)); 958 } else { 959 String reference= getNormalizedTypeReference(result); 960 if (! reference.startsWith(fPackage.getElementName())) { 961 reference= cutOffInnerTypes(reference); 962 ImportChange importChange= fImportsManager.getImportChange(typeReferences.getCompilationUnit()); 963 importChange.removeImport(fPackage.getElementName() + '.' + reference); 964 importChange.addImport(getNewPackageName() + '.' + reference); 965 } } 967 } 968 } 969 970 private static String getNormalizedTypeReference(SearchMatch searchResult) throws JavaModelException { 971 ICompilationUnit cu= SearchUtils.getCompilationUnit(searchResult); 972 String reference= cu.getBuffer().getText(searchResult.getOffset(), searchResult.getLength()); 973 return CommentAnalyzer.normalizeReference(reference); 975 } 976 977 private static String cutOffInnerTypes(String reference) { 978 int dotPos= reference.indexOf('.'); if (dotPos != -1) 980 reference= reference.substring(0, dotPos); 981 return reference; 982 } 983 984 private String getUpdatedImport(IImportDeclaration importDeclaration) { 985 String fullyQualifiedImportType= importDeclaration.getElementName(); 986 int offsetOfDotBeforeTypeName= fPackage.getElementName().length(); 987 String result= getNewPackageName() + fullyQualifiedImportType.substring(offsetOfDotBeforeTypeName); 988 return result; 989 } 990 991 private String getNewPackageName() { 992 return fProcessor.getNewPackageName(fPackage.getElementName()); 993 } 994 } 995 996 1000 static class ImportsManager { 1001 public static class ImportChange { 1002 private ArrayList fStaticToRemove= new ArrayList (); 1003 private ArrayList fStaticToAdd= new ArrayList (); 1004 private ArrayList fToRemove= new ArrayList (); 1005 private ArrayList fToAdd= new ArrayList (); 1006 1007 public void removeStaticImport(String elementName) { 1008 fStaticToRemove.add(elementName); 1009 } 1010 1011 public void addStaticImport(String declaringType, String memberName) { 1012 fStaticToAdd.add(new String [] {declaringType, memberName}); 1013 } 1014 1015 public void removeImport(String elementName) { 1016 fToRemove.add(elementName); 1017 } 1018 1019 public void addImport(String elementName) { 1020 fToAdd.add(elementName); 1021 } 1022 } 1023 1024 private HashMap fImportChanges= new HashMap (); 1025 1026 public ImportChange getImportChange(ICompilationUnit cu) { 1027 ImportChange importChange= (ImportChange) fImportChanges.get(cu); 1028 if (importChange == null) { 1029 importChange= new ImportChange(); 1030 fImportChanges.put(cu, importChange); 1031 } 1032 return importChange; 1033 } 1034 1035 public void rewriteImports(TextChangeManager changeManager, IProgressMonitor pm) throws CoreException { 1036 for (Iterator iter= fImportChanges.entrySet().iterator(); iter.hasNext();) { 1037 Entry entry= (Entry) iter.next(); 1038 ICompilationUnit cu= (ICompilationUnit) entry.getKey(); 1039 ImportChange importChange= (ImportChange) entry.getValue(); 1040 1041 ImportRewrite importRewrite= StubUtility.createImportRewrite(cu, true); 1042 importRewrite.setFilterImplicitImports(false); 1043 for (Iterator iterator= importChange.fStaticToRemove.iterator(); iterator.hasNext();) { 1044 importRewrite.removeStaticImport((String ) iterator.next()); 1045 } 1046 for (Iterator iterator= importChange.fToRemove.iterator(); iterator.hasNext();) { 1047 importRewrite.removeImport((String ) iterator.next()); 1048 } 1049 for (Iterator iterator= importChange.fStaticToAdd.iterator(); iterator.hasNext();) { 1050 String [] toAdd= (String []) iterator.next(); 1051 importRewrite.addStaticImport(toAdd[0], toAdd[1], true); 1052 } 1053 for (Iterator iterator= importChange.fToAdd.iterator(); iterator.hasNext();) { 1054 importRewrite.addImport((String ) iterator.next()); 1055 } 1056 1057 if (importRewrite.hasRecordedChanges()) { 1058 TextEdit importEdit= importRewrite.rewriteImports(pm); 1059 String name= RefactoringCoreMessages.RenamePackageRefactoring_update_imports; 1060 try { 1061 TextChangeCompatibility.addTextEdit(changeManager.get(cu), name, importEdit); 1062 } catch (MalformedTreeException e) { 1063 JavaPlugin.logErrorMessage("MalformedTreeException while processing cu " + cu); throw e; 1065 } 1066 } 1067 } 1068 } 1069 } 1070 1071 public RefactoringStatus initialize(RefactoringArguments arguments) { 1072 if (arguments instanceof JavaRefactoringArguments) { 1073 final JavaRefactoringArguments extended= (JavaRefactoringArguments) arguments; 1074 final String handle= extended.getAttribute(JDTRefactoringDescriptor.ATTRIBUTE_INPUT); 1075 if (handle != null) { 1076 final IJavaElement element= JDTRefactoringDescriptor.handleToElement(extended.getProject(), handle, false); 1077 if (element == null || !element.exists() || element.getElementType() != IJavaElement.PACKAGE_FRAGMENT) 1078 return ScriptableRefactoring.createInputFatalStatus(element, getRefactoring().getName(), IJavaRefactorings.RENAME_PACKAGE); 1079 else 1080 fPackage= (IPackageFragment) element; 1081 } else 1082 return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JDTRefactoringDescriptor.ATTRIBUTE_INPUT)); 1083 final String name= extended.getAttribute(JDTRefactoringDescriptor.ATTRIBUTE_NAME); 1084 if (name != null && !"".equals(name)) setNewElementName(name); 1086 else 1087 return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JDTRefactoringDescriptor.ATTRIBUTE_NAME)); 1088 final String patterns= extended.getAttribute(ATTRIBUTE_PATTERNS); 1089 if (patterns != null && !"".equals(patterns)) fFilePatterns= patterns; 1091 else 1092 fFilePatterns= ""; final String references= extended.getAttribute(JDTRefactoringDescriptor.ATTRIBUTE_REFERENCES); 1094 if (references != null) { 1095 fUpdateReferences= Boolean.valueOf(references).booleanValue(); 1096 } else 1097 return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JDTRefactoringDescriptor.ATTRIBUTE_REFERENCES)); 1098 final String matches= extended.getAttribute(ATTRIBUTE_TEXTUAL_MATCHES); 1099 if (matches != null) { 1100 fUpdateTextualMatches= Boolean.valueOf(matches).booleanValue(); 1101 } else 1102 return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_TEXTUAL_MATCHES)); 1103 final String qualified= extended.getAttribute(ATTRIBUTE_QUALIFIED); 1104 if (qualified != null) { 1105 fUpdateQualifiedNames= Boolean.valueOf(qualified).booleanValue(); 1106 } else 1107 return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_QUALIFIED)); 1108 final String hierarchical= extended.getAttribute(ATTRIBUTE_HIERARCHICAL); 1109 if (hierarchical != null) { 1110 fRenameSubpackages= Boolean.valueOf(hierarchical).booleanValue(); 1111 } else 1112 return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_HIERARCHICAL)); 1113 } else 1114 return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.InitializableRefactoring_inacceptable_arguments); 1115 return new RefactoringStatus(); 1116 } 1117} 1118 | Popular Tags |