1 /******************************************************************************* 2 * Copyright (c) 2000, 2016 IBM Corporation and others. 3 * 4 * This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * which accompanies this distribution, and is available at 7 * https://www.eclipse.org/legal/epl-2.0/ 8 * 9 * SPDX-License-Identifier: EPL-2.0 10 * 11 * Contributors: 12 * IBM Corporation - initial API and implementation 13 *******************************************************************************/ 14 package org.eclipse.jdt.internal.corext.refactoring.structure; 15 16 import java.util.Collection; 17 import java.util.HashSet; 18 import java.util.Set; 19 20 import org.eclipse.core.runtime.Assert; 21 import org.eclipse.core.runtime.CoreException; 22 import org.eclipse.core.runtime.IProgressMonitor; 23 import org.eclipse.core.runtime.NullProgressMonitor; 24 import org.eclipse.core.runtime.OperationCanceledException; 25 import org.eclipse.core.runtime.SubProgressMonitor; 26 27 import org.eclipse.ltk.core.refactoring.Change; 28 import org.eclipse.ltk.core.refactoring.RefactoringDescriptor; 29 import org.eclipse.ltk.core.refactoring.RefactoringStatus; 30 import org.eclipse.ltk.core.refactoring.TextChange; 31 import org.eclipse.ltk.core.refactoring.TextEditBasedChange; 32 import org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext; 33 import org.eclipse.ltk.core.refactoring.participants.RefactoringParticipant; 34 import org.eclipse.ltk.core.refactoring.participants.SharableParticipants; 35 36 import org.eclipse.jdt.core.ICompilationUnit; 37 import org.eclipse.jdt.core.IJavaElement; 38 import org.eclipse.jdt.core.IJavaProject; 39 import org.eclipse.jdt.core.IType; 40 import org.eclipse.jdt.core.JavaModelException; 41 import org.eclipse.jdt.core.dom.ASTNode; 42 import org.eclipse.jdt.core.dom.ASTParser; 43 import org.eclipse.jdt.core.dom.ASTRequestor; 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.NodeFinder; 49 import org.eclipse.jdt.core.refactoring.IJavaRefactorings; 50 import org.eclipse.jdt.core.refactoring.descriptors.JavaRefactoringDescriptor; 51 import org.eclipse.jdt.core.refactoring.descriptors.UseSupertypeDescriptor; 52 53 import org.eclipse.jdt.internal.core.refactoring.descriptors.RefactoringSignatureDescriptorFactory; 54 import org.eclipse.jdt.internal.corext.refactoring.Checks; 55 import org.eclipse.jdt.internal.corext.refactoring.JDTRefactoringDescriptorComment; 56 import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringArguments; 57 import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringDescriptorUtil; 58 import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages; 59 import org.eclipse.jdt.internal.corext.refactoring.changes.DynamicValidationRefactoringChange; 60 import org.eclipse.jdt.internal.corext.refactoring.structure.constraints.SuperTypeConstraintsModel; 61 import org.eclipse.jdt.internal.corext.refactoring.structure.constraints.SuperTypeConstraintsSolver; 62 import org.eclipse.jdt.internal.corext.refactoring.structure.constraints.SuperTypeRefactoringProcessor; 63 import org.eclipse.jdt.internal.corext.refactoring.typeconstraints.types.TType; 64 import org.eclipse.jdt.internal.corext.refactoring.typeconstraints2.ISourceConstraintVariable; 65 import org.eclipse.jdt.internal.corext.refactoring.typeconstraints2.ITypeConstraintVariable; 66 import org.eclipse.jdt.internal.corext.refactoring.util.RefactoringASTParser; 67 import org.eclipse.jdt.internal.corext.refactoring.util.ResourceUtil; 68 import org.eclipse.jdt.internal.corext.refactoring.util.TextEditBasedChangeManager; 69 import org.eclipse.jdt.internal.corext.util.Messages; 70 71 import org.eclipse.jdt.ui.JavaElementLabels; 72 73 import org.eclipse.jdt.internal.ui.JavaPlugin; 74 import org.eclipse.jdt.internal.corext.dom.IASTSharedValues; 75 import org.eclipse.jdt.internal.core.manipulation.util.BasicElementLabels; 76 77 78 /** 79 * Refactoring processor to replace type occurrences by a super type. 80 */ 81 public final class UseSuperTypeProcessor extends SuperTypeRefactoringProcessor { 82 83 private static final String IDENTIFIER= "org.eclipse.jdt.ui.useSuperTypeProcessor"; //$NON-NLS-1$ 84 85 /** 86 * Finds the type with the given fully qualified name (generic type 87 * parameters included) in the hierarchy. 88 * 89 * @param type 90 * The hierarchy type to find the super type in 91 * @param name 92 * The fully qualified name of the super type 93 * @return The found super type, or <code>null</code> 94 */ findTypeInHierarchy(final ITypeBinding type, final String name)95 protected static ITypeBinding findTypeInHierarchy(final ITypeBinding type, final String name) { 96 if (type.isArray() || type.isPrimitive()) 97 return null; 98 if (name.equals(type.getTypeDeclaration().getQualifiedName())) 99 return type; 100 final ITypeBinding binding= type.getSuperclass(); 101 if (binding != null) { 102 final ITypeBinding result= findTypeInHierarchy(binding, name); 103 if (result != null) 104 return result; 105 } 106 for (ITypeBinding b : type.getInterfaces()) { 107 final ITypeBinding result= findTypeInHierarchy(b, name); 108 if (result != null) 109 return result; 110 } 111 return null; 112 } 113 114 /** The text change manager */ 115 private TextEditBasedChangeManager fChangeManager= null; 116 117 /** The number of files affected by the last change generation */ 118 private int fChanges= 0; 119 120 /** The subtype to replace */ 121 private IType fSubType; 122 123 /** The supertype as replacement */ 124 private IType fSuperType= null; 125 126 /** 127 * Creates a new super type processor. 128 * 129 * @param subType 130 * the subtype to replace its occurrences 131 */ UseSuperTypeProcessor(final IType subType)132 public UseSuperTypeProcessor(final IType subType) { 133 super(null); 134 fReplace= true; 135 fSubType= subType; 136 } 137 138 /** 139 * Creates a new super type processor. 140 * 141 * @param subType 142 * the subtype to replace its occurrences 143 * @param superType 144 * the supertype as replacement 145 */ UseSuperTypeProcessor(final IType subType, final IType superType)146 public UseSuperTypeProcessor(final IType subType, final IType superType) { 147 super(null); 148 fReplace= true; 149 fSubType= subType; 150 fSuperType= superType; 151 } 152 153 /** 154 * Creates a new super type processor from refactoring arguments. 155 * 156 * @param arguments 157 * the refactoring arguments 158 * @param status 159 * the resulting status 160 */ UseSuperTypeProcessor(JavaRefactoringArguments arguments, RefactoringStatus status)161 public UseSuperTypeProcessor(JavaRefactoringArguments arguments, RefactoringStatus status) { 162 super(null); 163 fReplace= true; 164 RefactoringStatus initializeStatus= initialize(arguments); 165 status.merge(initializeStatus); 166 } 167 168 /* 169 * @see org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor#checkFinalConditions(org.eclipse.core.runtime.IProgressMonitor,org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext) 170 */ 171 @Override checkFinalConditions(final IProgressMonitor monitor, final CheckConditionsContext context)172 public final RefactoringStatus checkFinalConditions(final IProgressMonitor monitor, final CheckConditionsContext context) throws CoreException, OperationCanceledException { 173 Assert.isNotNull(monitor); 174 Assert.isNotNull(context); 175 final RefactoringStatus status= new RefactoringStatus(); 176 fChangeManager= new TextEditBasedChangeManager(); 177 try { 178 monitor.beginTask("", 200); //$NON-NLS-1$ 179 monitor.setTaskName(RefactoringCoreMessages.UseSuperTypeProcessor_checking); 180 fChangeManager= createChangeManager(new SubProgressMonitor(monitor, 200), status); 181 if (!status.hasFatalError()) { 182 Checks.addModifiedFilesToChecker(ResourceUtil.getFiles(fChangeManager.getAllCompilationUnits()), context); 183 } 184 } finally { 185 monitor.done(); 186 } 187 return status; 188 } 189 190 /* 191 * @see org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor#checkInitialConditions(org.eclipse.core.runtime.IProgressMonitor) 192 */ 193 @Override checkInitialConditions(final IProgressMonitor monitor)194 public final RefactoringStatus checkInitialConditions(final IProgressMonitor monitor) throws CoreException, OperationCanceledException { 195 Assert.isNotNull(monitor); 196 final RefactoringStatus status= new RefactoringStatus(); 197 try { 198 monitor.beginTask("", 1); //$NON-NLS-1$ 199 monitor.setTaskName(RefactoringCoreMessages.UseSuperTypeProcessor_checking); 200 // No checks 201 monitor.worked(1); 202 } finally { 203 monitor.done(); 204 } 205 return status; 206 } 207 208 /* 209 * @see org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor#createChange(org.eclipse.core.runtime.IProgressMonitor) 210 */ 211 @Override createChange(final IProgressMonitor monitor)212 public final Change createChange(final IProgressMonitor monitor) throws CoreException, OperationCanceledException { 213 Assert.isNotNull(monitor); 214 try { 215 fChanges= 0; 216 monitor.beginTask("", 1); //$NON-NLS-1$ 217 monitor.setTaskName(RefactoringCoreMessages.ExtractInterfaceProcessor_creating); 218 final TextEditBasedChange[] changes= fChangeManager.getAllChanges(); 219 if (changes != null && changes.length != 0) { 220 fChanges= changes.length; 221 IJavaProject project= null; 222 if (!fSubType.isBinary()) 223 project= fSubType.getJavaProject(); 224 int flags= JavaRefactoringDescriptor.JAR_MIGRATION | JavaRefactoringDescriptor.JAR_REFACTORING | RefactoringDescriptor.STRUCTURAL_CHANGE | RefactoringDescriptor.MULTI_CHANGE; 225 try { 226 if (fSubType.isLocal() || fSubType.isAnonymous()) 227 flags|= JavaRefactoringDescriptor.JAR_SOURCE_ATTACHMENT; 228 } catch (JavaModelException exception) { 229 JavaPlugin.log(exception); 230 } 231 final String name= project != null ? project.getElementName() : null; 232 final String description= Messages.format(RefactoringCoreMessages.UseSuperTypeProcessor_descriptor_description_short, BasicElementLabels.getJavaElementName(fSuperType.getElementName())); 233 final String header= Messages.format(RefactoringCoreMessages.UseSuperTypeProcessor_descriptor_description, new String[] { JavaElementLabels.getElementLabel(fSuperType, JavaElementLabels.ALL_FULLY_QUALIFIED), JavaElementLabels.getElementLabel(fSubType, JavaElementLabels.ALL_FULLY_QUALIFIED) }); 234 final JDTRefactoringDescriptorComment comment= new JDTRefactoringDescriptorComment(name, this, header); 235 comment.addSetting(Messages.format(RefactoringCoreMessages.UseSuperTypeProcessor_refactored_element_pattern, JavaElementLabels.getElementLabel(fSuperType, JavaElementLabels.ALL_FULLY_QUALIFIED))); 236 addSuperTypeSettings(comment, false); 237 final UseSupertypeDescriptor descriptor= RefactoringSignatureDescriptorFactory.createUseSupertypeDescriptor(); 238 descriptor.setProject(name); 239 descriptor.setDescription(description); 240 descriptor.setComment(comment.asString()); 241 descriptor.setFlags(flags); 242 descriptor.setSubtype(getSubType()); 243 descriptor.setSupertype(getSuperType()); 244 descriptor.setReplaceInstanceof(fInstanceOf); 245 return new DynamicValidationRefactoringChange(descriptor, RefactoringCoreMessages.UseSupertypeWherePossibleRefactoring_name, fChangeManager.getAllChanges()); 246 } 247 monitor.worked(1); 248 } finally { 249 monitor.done(); 250 } 251 return null; 252 } 253 254 /** 255 * Creates the text change manager for this processor. 256 * 257 * @param monitor 258 * the progress monitor to display progress 259 * @param status 260 * the refactoring status 261 * @return the created text change manager 262 * @throws JavaModelException 263 * if the method declaration could not be found 264 * @throws CoreException 265 * if the changes could not be generated 266 */ createChangeManager(final IProgressMonitor monitor, final RefactoringStatus status)267 protected final TextEditBasedChangeManager createChangeManager(final IProgressMonitor monitor, final RefactoringStatus status) throws JavaModelException, CoreException { 268 Assert.isNotNull(status); 269 Assert.isNotNull(monitor); 270 try { 271 monitor.beginTask("", 300); //$NON-NLS-1$ 272 monitor.setTaskName(RefactoringCoreMessages.UseSuperTypeProcessor_creating); 273 final TextEditBasedChangeManager manager= new TextEditBasedChangeManager(); 274 final IJavaProject project= fSubType.getJavaProject(); 275 final ASTParser parser= ASTParser.newParser(IASTSharedValues.SHARED_AST_LEVEL); 276 parser.setWorkingCopyOwner(fOwner); 277 parser.setResolveBindings(true); 278 parser.setProject(project); 279 parser.setCompilerOptions(RefactoringASTParser.getCompilerOptions(project)); 280 if (fSubType.isBinary() || fSubType.isReadOnly()) { 281 final IBinding[] bindings= parser.createBindings(new IJavaElement[] { fSubType, fSuperType }, new SubProgressMonitor(monitor, 50)); 282 if (bindings != null && bindings.length == 2 && bindings[0] instanceof ITypeBinding && bindings[1] instanceof ITypeBinding) { 283 solveSuperTypeConstraints(null, null, fSubType, (ITypeBinding) bindings[0], (ITypeBinding) bindings[1], new SubProgressMonitor(monitor, 100), status); 284 if (!status.hasFatalError()) 285 rewriteTypeOccurrences(manager, null, null, null, null, new HashSet<String>(), status, new SubProgressMonitor(monitor, 150)); 286 } 287 } else { 288 parser.createASTs(new ICompilationUnit[] { fSubType.getCompilationUnit() }, new String[0], new ASTRequestor() { 289 290 @Override 291 public final void acceptAST(final ICompilationUnit unit, final CompilationUnit node) { 292 try { 293 final CompilationUnitRewrite subRewrite= new CompilationUnitRewrite(fOwner, unit, node); 294 final AbstractTypeDeclaration subDeclaration= ASTNodeSearchUtil.getAbstractTypeDeclarationNode(fSubType, subRewrite.getRoot()); 295 if (subDeclaration != null) { 296 final ITypeBinding subBinding= subDeclaration.resolveBinding(); 297 if (subBinding != null) { 298 final ITypeBinding superBinding= findTypeInHierarchy(subBinding, fSuperType.getFullyQualifiedName('.')); 299 if (superBinding != null) { 300 solveSuperTypeConstraints(subRewrite.getCu(), subRewrite.getRoot(), fSubType, subBinding, superBinding, new SubProgressMonitor(monitor, 100), status); 301 if (!status.hasFatalError()) { 302 rewriteTypeOccurrences(manager, this, subRewrite, subRewrite.getCu(), subRewrite.getRoot(), new HashSet<String>(), status, new SubProgressMonitor(monitor, 200)); 303 final TextChange change= subRewrite.createChange(true); 304 if (change != null) 305 manager.manage(subRewrite.getCu(), change); 306 } 307 } 308 } 309 } 310 } catch (CoreException exception) { 311 JavaPlugin.log(exception); 312 status.merge(RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.UseSuperTypeProcessor_internal_error)); 313 } 314 } 315 316 @Override 317 public final void acceptBinding(final String key, final IBinding binding) { 318 // Do nothing 319 } 320 }, new NullProgressMonitor()); 321 } 322 return manager; 323 } finally { 324 monitor.done(); 325 } 326 } 327 328 /* 329 * @see org.eclipse.jdt.internal.corext.refactoring.structure.constraints.SuperTypeRefactoringProcessor#createContraintSolver(org.eclipse.jdt.internal.corext.refactoring.structure.constraints.SuperTypeConstraintsModel) 330 */ 331 @Override createContraintSolver(final SuperTypeConstraintsModel model)332 protected final SuperTypeConstraintsSolver createContraintSolver(final SuperTypeConstraintsModel model) { 333 return new SuperTypeConstraintsSolver(model); 334 } 335 336 /** 337 * Returns the number of files that are affected from the last change 338 * generation. 339 * 340 * @return The number of files which are affected 341 */ getChanges()342 public final int getChanges() { 343 return fChanges; 344 } 345 346 /* 347 * @see org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor#getElements() 348 */ 349 @Override getElements()350 public final Object[] getElements() { 351 return new Object[] { fSubType }; 352 } 353 354 /* 355 * @see org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor#getIdentifier() 356 */ 357 @Override getIdentifier()358 public final String getIdentifier() { 359 return IDENTIFIER; 360 } 361 362 /* 363 * @see org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor#getProcessorName() 364 */ 365 @Override getProcessorName()366 public final String getProcessorName() { 367 return RefactoringCoreMessages.UseSuperTypeProcessor_name; 368 } 369 370 /** 371 * Returns the subtype to be replaced. 372 * 373 * @return The subtype to be replaced 374 */ getSubType()375 public final IType getSubType() { 376 return fSubType; 377 } 378 379 /** 380 * Returns the supertype as replacement. 381 * 382 * @return The supertype as replacement 383 */ getSuperType()384 public final IType getSuperType() { 385 return fSuperType; 386 } 387 initialize(JavaRefactoringArguments extended)388 private final RefactoringStatus initialize(JavaRefactoringArguments extended) { 389 String handle= extended.getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT); 390 if (handle != null) { 391 final IJavaElement element= JavaRefactoringDescriptorUtil.handleToElement(extended.getProject(), handle, false); 392 if (element == null || !element.exists() || element.getElementType() != IJavaElement.TYPE) 393 return JavaRefactoringDescriptorUtil.createInputFatalStatus(element, getProcessorName(), IJavaRefactorings.USE_SUPER_TYPE); 394 else 395 fSubType= (IType) element; 396 } else 397 return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT)); 398 handle= extended.getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_ELEMENT + 1); 399 if (handle != null) { 400 final IJavaElement element= JavaRefactoringDescriptorUtil.handleToElement(extended.getProject(), handle, false); 401 if (element == null || !element.exists() || element.getElementType() != IJavaElement.TYPE) 402 return JavaRefactoringDescriptorUtil.createInputFatalStatus(element, getProcessorName(), IJavaRefactorings.USE_SUPER_TYPE); 403 else 404 fSuperType= (IType) element; 405 } else 406 return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JavaRefactoringDescriptorUtil.ATTRIBUTE_ELEMENT + 1)); 407 final String instance= extended.getAttribute(ATTRIBUTE_INSTANCEOF); 408 if (instance != null) { 409 fInstanceOf= Boolean.parseBoolean(instance); 410 } else 411 return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_INSTANCEOF)); 412 return new RefactoringStatus(); 413 } 414 415 /* 416 * @see org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor#isApplicable() 417 */ 418 @Override isApplicable()419 public final boolean isApplicable() throws CoreException { 420 return Checks.isAvailable(fSubType) && Checks.isAvailable(fSuperType) && !fSubType.isAnonymous() && !fSubType.isAnnotation() && !fSuperType.isAnonymous() && !fSuperType.isAnnotation() && !fSuperType.isEnum(); 421 } 422 423 /* 424 * @see org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor#loadParticipants(org.eclipse.ltk.core.refactoring.RefactoringStatus,org.eclipse.ltk.core.refactoring.participants.SharableParticipants) 425 */ 426 @Override loadParticipants(final RefactoringStatus status, final SharableParticipants sharedParticipants)427 public final RefactoringParticipant[] loadParticipants(final RefactoringStatus status, final SharableParticipants sharedParticipants) throws CoreException { 428 return new RefactoringParticipant[0]; 429 } 430 431 @Override rewriteTypeOccurrences(final TextEditBasedChangeManager manager, final ASTRequestor requestor, final CompilationUnitRewrite rewrite, final ICompilationUnit unit, final CompilationUnit node, final Set<String> replacements, final IProgressMonitor monitor)432 protected final void rewriteTypeOccurrences(final TextEditBasedChangeManager manager, final ASTRequestor requestor, final CompilationUnitRewrite rewrite, final ICompilationUnit unit, final CompilationUnit node, final Set<String> replacements, final IProgressMonitor monitor) throws CoreException { 433 try { 434 monitor.beginTask("", 100); //$NON-NLS-1$ 435 monitor.setTaskName(RefactoringCoreMessages.ExtractInterfaceProcessor_creating); 436 final Collection<ITypeConstraintVariable> collection= fTypeOccurrences.get(unit); 437 if (collection != null && !collection.isEmpty()) { 438 final IProgressMonitor subMonitor= new SubProgressMonitor(monitor, 100); 439 try { 440 subMonitor.beginTask("", collection.size() * 10); //$NON-NLS-1$ 441 subMonitor.setTaskName(RefactoringCoreMessages.ExtractInterfaceProcessor_creating); 442 TType estimate= null; 443 ISourceConstraintVariable variable= null; 444 CompilationUnitRewrite currentRewrite= null; 445 final ICompilationUnit sourceUnit= rewrite.getCu(); 446 if (sourceUnit.equals(unit)) 447 currentRewrite= rewrite; 448 else 449 currentRewrite= new CompilationUnitRewrite(fOwner, unit, node); 450 for (ITypeConstraintVariable iTypeConstraintVariable : collection) { 451 variable= iTypeConstraintVariable; 452 estimate= (TType) variable.getData(SuperTypeConstraintsSolver.DATA_TYPE_ESTIMATE); 453 if (estimate != null && variable instanceof ITypeConstraintVariable) { 454 final ASTNode result= NodeFinder.perform(node, ((ITypeConstraintVariable) variable).getRange().getSourceRange()); 455 if (result != null) 456 rewriteTypeOccurrence(estimate, currentRewrite, result, currentRewrite.createCategorizedGroupDescription(RefactoringCoreMessages.SuperTypeRefactoringProcessor_update_type_occurrence, SET_SUPER_TYPE)); 457 } 458 subMonitor.worked(10); 459 } 460 if (!sourceUnit.equals(unit)) { 461 final TextChange change= currentRewrite.createChange(true); 462 if (change != null) 463 manager.manage(unit, change); 464 } 465 } finally { 466 subMonitor.done(); 467 } 468 } 469 } finally { 470 monitor.done(); 471 } 472 } 473 474 /** 475 * Sets the supertype as replacement. 476 * 477 * @param type 478 * The supertype to set 479 */ setSuperType(final IType type)480 public final void setSuperType(final IType type) { 481 Assert.isNotNull(type); 482 483 fSuperType= type; 484 } 485 } 486