001: /*******************************************************************************
002: * Copyright (c) 2000, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.jdt.internal.corext.refactoring.structure;
011:
012: import java.util.Collection;
013: import java.util.HashSet;
014: import java.util.Iterator;
015: import java.util.Set;
016:
017: import org.eclipse.core.runtime.Assert;
018: import org.eclipse.core.runtime.CoreException;
019: import org.eclipse.core.runtime.IProgressMonitor;
020: import org.eclipse.core.runtime.NullProgressMonitor;
021: import org.eclipse.core.runtime.OperationCanceledException;
022: import org.eclipse.core.runtime.SubProgressMonitor;
023:
024: import org.eclipse.ltk.core.refactoring.Change;
025: import org.eclipse.ltk.core.refactoring.RefactoringDescriptor;
026: import org.eclipse.ltk.core.refactoring.RefactoringStatus;
027: import org.eclipse.ltk.core.refactoring.TextChange;
028: import org.eclipse.ltk.core.refactoring.TextEditBasedChange;
029: import org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext;
030: import org.eclipse.ltk.core.refactoring.participants.RefactoringArguments;
031: import org.eclipse.ltk.core.refactoring.participants.RefactoringParticipant;
032: import org.eclipse.ltk.core.refactoring.participants.SharableParticipants;
033:
034: import org.eclipse.jdt.core.ICompilationUnit;
035: import org.eclipse.jdt.core.IJavaElement;
036: import org.eclipse.jdt.core.IJavaProject;
037: import org.eclipse.jdt.core.IType;
038: import org.eclipse.jdt.core.JavaModelException;
039: import org.eclipse.jdt.core.dom.AST;
040: import org.eclipse.jdt.core.dom.ASTNode;
041: import org.eclipse.jdt.core.dom.ASTParser;
042: import org.eclipse.jdt.core.dom.ASTRequestor;
043: import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
044: import org.eclipse.jdt.core.dom.CompilationUnit;
045: import org.eclipse.jdt.core.dom.IBinding;
046: import org.eclipse.jdt.core.dom.ITypeBinding;
047: import org.eclipse.jdt.core.refactoring.IJavaRefactorings;
048: import org.eclipse.jdt.core.refactoring.descriptors.JavaRefactoringDescriptor;
049: import org.eclipse.jdt.core.refactoring.descriptors.UseSupertypeDescriptor;
050:
051: import org.eclipse.jdt.internal.corext.dom.NodeFinder;
052: import org.eclipse.jdt.internal.corext.refactoring.Checks;
053: import org.eclipse.jdt.internal.corext.refactoring.JDTRefactoringDescriptorComment;
054: import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringArguments;
055: import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringDescriptorUtil;
056: import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages;
057: import org.eclipse.jdt.internal.corext.refactoring.changes.DynamicValidationRefactoringChange;
058: import org.eclipse.jdt.internal.corext.refactoring.code.ScriptableRefactoring;
059: import org.eclipse.jdt.internal.corext.refactoring.structure.constraints.SuperTypeConstraintsModel;
060: import org.eclipse.jdt.internal.corext.refactoring.structure.constraints.SuperTypeConstraintsSolver;
061: import org.eclipse.jdt.internal.corext.refactoring.structure.constraints.SuperTypeRefactoringProcessor;
062: import org.eclipse.jdt.internal.corext.refactoring.typeconstraints.types.TType;
063: import org.eclipse.jdt.internal.corext.refactoring.typeconstraints2.ISourceConstraintVariable;
064: import org.eclipse.jdt.internal.corext.refactoring.typeconstraints2.ITypeConstraintVariable;
065: import org.eclipse.jdt.internal.corext.refactoring.util.RefactoringASTParser;
066: import org.eclipse.jdt.internal.corext.refactoring.util.ResourceUtil;
067: import org.eclipse.jdt.internal.corext.refactoring.util.TextEditBasedChangeManager;
068: import org.eclipse.jdt.internal.corext.util.Messages;
069:
070: import org.eclipse.jdt.ui.JavaElementLabels;
071:
072: import org.eclipse.jdt.internal.ui.JavaPlugin;
073:
074: /**
075: * Refactoring processor to replace type occurrences by a super type.
076: */
077: public final class UseSuperTypeProcessor extends
078: SuperTypeRefactoringProcessor {
079:
080: private static final String IDENTIFIER = "org.eclipse.jdt.ui.useSuperTypeProcessor"; //$NON-NLS-1$
081:
082: /**
083: * Finds the type with the given fully qualified name (generic type
084: * parameters included) in the hierarchy.
085: *
086: * @param type
087: * The hierarchy type to find the super type in
088: * @param name
089: * The fully qualified name of the super type
090: * @return The found super type, or <code>null</code>
091: */
092: protected static ITypeBinding findTypeInHierarchy(
093: final ITypeBinding type, final String name) {
094: if (type.isArray() || type.isPrimitive())
095: return null;
096: if (name.equals(type.getTypeDeclaration().getQualifiedName()))
097: return type;
098: final ITypeBinding binding = type.getSuperclass();
099: if (binding != null) {
100: final ITypeBinding result = findTypeInHierarchy(binding,
101: name);
102: if (result != null)
103: return result;
104: }
105: final ITypeBinding[] bindings = type.getInterfaces();
106: for (int index = 0; index < bindings.length; index++) {
107: final ITypeBinding result = findTypeInHierarchy(
108: bindings[index], name);
109: if (result != null)
110: return result;
111: }
112: return null;
113: }
114:
115: /** The text change manager */
116: private TextEditBasedChangeManager fChangeManager = null;
117:
118: /** The number of files affected by the last change generation */
119: private int fChanges = 0;
120:
121: /** The subtype to replace */
122: private IType fSubType;
123:
124: /** The supertype as replacement */
125: private IType fSuperType = null;
126:
127: /**
128: * Creates a new super type processor.
129: *
130: * @param subType
131: * the subtype to replace its occurrences, or <code>null</code>
132: * if invoked by scripting
133: */
134: public UseSuperTypeProcessor(final IType subType) {
135: super (null);
136: fReplace = true;
137: fSubType = subType;
138: }
139:
140: /**
141: * Creates a new super type processor.
142: *
143: * @param subType
144: * the subtype to replace its occurrences, or <code>null</code>
145: * if invoked by scripting
146: * @param superType
147: * the supertype as replacement, or <code>null</code> if
148: * invoked by scripting
149: */
150: public UseSuperTypeProcessor(final IType subType,
151: final IType super Type) {
152: super (null);
153: fReplace = true;
154: fSubType = subType;
155: fSuperType = super Type;
156: }
157:
158: /*
159: * @see org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor#checkFinalConditions(org.eclipse.core.runtime.IProgressMonitor,org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext)
160: */
161: public final RefactoringStatus checkFinalConditions(
162: final IProgressMonitor monitor,
163: final CheckConditionsContext context) throws CoreException,
164: OperationCanceledException {
165: Assert.isNotNull(monitor);
166: Assert.isNotNull(context);
167: final RefactoringStatus status = new RefactoringStatus();
168: fChangeManager = new TextEditBasedChangeManager();
169: try {
170: monitor.beginTask("", 200); //$NON-NLS-1$
171: monitor
172: .setTaskName(RefactoringCoreMessages.UseSuperTypeProcessor_checking);
173: fChangeManager = createChangeManager(
174: new SubProgressMonitor(monitor, 200), status);
175: if (!status.hasFatalError()) {
176: final RefactoringStatus validation = Checks
177: .validateModifiesFiles(ResourceUtil
178: .getFiles(fChangeManager
179: .getAllCompilationUnits()),
180: getRefactoring().getValidationContext());
181: if (!validation.isOK())
182: return validation;
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: public final RefactoringStatus checkInitialConditions(
194: final IProgressMonitor monitor) throws CoreException,
195: OperationCanceledException {
196: Assert.isNotNull(monitor);
197: final RefactoringStatus status = new RefactoringStatus();
198: try {
199: monitor.beginTask("", 1); //$NON-NLS-1$
200: monitor
201: .setTaskName(RefactoringCoreMessages.UseSuperTypeProcessor_checking);
202: // No checks
203: monitor.worked(1);
204: } finally {
205: monitor.done();
206: }
207: return status;
208: }
209:
210: /*
211: * @see org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor#createChange(org.eclipse.core.runtime.IProgressMonitor)
212: */
213: public final Change createChange(final IProgressMonitor monitor)
214: throws CoreException, OperationCanceledException {
215: Assert.isNotNull(monitor);
216: try {
217: fChanges = 0;
218: monitor.beginTask("", 1); //$NON-NLS-1$
219: monitor
220: .setTaskName(RefactoringCoreMessages.ExtractInterfaceProcessor_creating);
221: final TextEditBasedChange[] changes = fChangeManager
222: .getAllChanges();
223: if (changes != null && changes.length != 0) {
224: fChanges = changes.length;
225: IJavaProject project = null;
226: if (!fSubType.isBinary())
227: project = fSubType.getJavaProject();
228: int flags = JavaRefactoringDescriptor.JAR_MIGRATION
229: | JavaRefactoringDescriptor.JAR_REFACTORING
230: | RefactoringDescriptor.STRUCTURAL_CHANGE
231: | RefactoringDescriptor.MULTI_CHANGE;
232: try {
233: if (fSubType.isLocal() || fSubType.isAnonymous())
234: flags |= JavaRefactoringDescriptor.JAR_SOURCE_ATTACHMENT;
235: } catch (JavaModelException exception) {
236: JavaPlugin.log(exception);
237: }
238: final String name = project != null ? project
239: .getElementName() : null;
240: final String description = Messages
241: .format(
242: RefactoringCoreMessages.UseSuperTypeProcessor_descriptor_description_short,
243: fSuperType.getElementName());
244: final String header = Messages
245: .format(
246: RefactoringCoreMessages.UseSuperTypeProcessor_descriptor_description,
247: new String[] {
248: JavaElementLabels
249: .getElementLabel(
250: fSuperType,
251: JavaElementLabels.ALL_FULLY_QUALIFIED),
252: JavaElementLabels
253: .getElementLabel(
254: fSubType,
255: JavaElementLabels.ALL_FULLY_QUALIFIED) });
256: final JDTRefactoringDescriptorComment comment = new JDTRefactoringDescriptorComment(
257: name, this , header);
258: comment
259: .addSetting(Messages
260: .format(
261: RefactoringCoreMessages.UseSuperTypeProcessor_refactored_element_pattern,
262: JavaElementLabels
263: .getElementLabel(
264: fSuperType,
265: JavaElementLabels.ALL_FULLY_QUALIFIED)));
266: addSuperTypeSettings(comment, false);
267: final UseSupertypeDescriptor descriptor = new UseSupertypeDescriptor();
268: descriptor.setProject(name);
269: descriptor.setDescription(description);
270: descriptor.setComment(comment.asString());
271: descriptor.setFlags(flags);
272: descriptor.setSubtype(getSubType());
273: descriptor.setSupertype(getSuperType());
274: descriptor.setReplaceInstanceof(fInstanceOf);
275: return new DynamicValidationRefactoringChange(
276: descriptor,
277: RefactoringCoreMessages.UseSupertypeWherePossibleRefactoring_name,
278: fChangeManager.getAllChanges());
279: }
280: monitor.worked(1);
281: } finally {
282: monitor.done();
283: }
284: return null;
285: }
286:
287: /**
288: * Creates the text change manager for this processor.
289: *
290: * @param monitor
291: * the progress monitor to display progress
292: * @param status
293: * the refactoring status
294: * @return the created text change manager
295: * @throws JavaModelException
296: * if the method declaration could not be found
297: * @throws CoreException
298: * if the changes could not be generated
299: */
300: protected final TextEditBasedChangeManager createChangeManager(
301: final IProgressMonitor monitor,
302: final RefactoringStatus status) throws JavaModelException,
303: CoreException {
304: Assert.isNotNull(status);
305: Assert.isNotNull(monitor);
306: try {
307: monitor.beginTask("", 300); //$NON-NLS-1$
308: monitor
309: .setTaskName(RefactoringCoreMessages.UseSuperTypeProcessor_creating);
310: final TextEditBasedChangeManager manager = new TextEditBasedChangeManager();
311: final IJavaProject project = fSubType.getJavaProject();
312: final ASTParser parser = ASTParser.newParser(AST.JLS3);
313: parser.setWorkingCopyOwner(fOwner);
314: parser.setResolveBindings(true);
315: parser.setProject(project);
316: parser.setCompilerOptions(RefactoringASTParser
317: .getCompilerOptions(project));
318: if (fSubType.isBinary() || fSubType.isReadOnly()) {
319: final IBinding[] bindings = parser.createBindings(
320: new IJavaElement[] { fSubType, fSuperType },
321: new SubProgressMonitor(monitor, 50));
322: if (bindings != null && bindings.length == 2
323: && bindings[0] instanceof ITypeBinding
324: && bindings[1] instanceof ITypeBinding) {
325: solveSuperTypeConstraints(null, null, fSubType,
326: (ITypeBinding) bindings[0],
327: (ITypeBinding) bindings[1],
328: new SubProgressMonitor(monitor, 100),
329: status);
330: if (!status.hasFatalError())
331: rewriteTypeOccurrences(manager, null, null,
332: null, null, new HashSet(), status,
333: new SubProgressMonitor(monitor, 150));
334: }
335: } else {
336: parser.createASTs(new ICompilationUnit[] { fSubType
337: .getCompilationUnit() }, new String[0],
338: new ASTRequestor() {
339:
340: public final void acceptAST(
341: final ICompilationUnit unit,
342: final CompilationUnit node) {
343: try {
344: final CompilationUnitRewrite subRewrite = new CompilationUnitRewrite(
345: fOwner, unit, node);
346: final AbstractTypeDeclaration subDeclaration = ASTNodeSearchUtil
347: .getAbstractTypeDeclarationNode(
348: fSubType,
349: subRewrite
350: .getRoot());
351: if (subDeclaration != null) {
352: final ITypeBinding subBinding = subDeclaration
353: .resolveBinding();
354: if (subBinding != null) {
355: final ITypeBinding super Binding = findTypeInHierarchy(
356: subBinding,
357: fSuperType
358: .getFullyQualifiedName('.'));
359: if (super Binding != null) {
360: solveSuperTypeConstraints(
361: subRewrite
362: .getCu(),
363: subRewrite
364: .getRoot(),
365: fSubType,
366: subBinding,
367: super Binding,
368: new SubProgressMonitor(
369: monitor,
370: 100),
371: status);
372: if (!status
373: .hasFatalError()) {
374: rewriteTypeOccurrences(
375: manager,
376: this ,
377: subRewrite,
378: subRewrite
379: .getCu(),
380: subRewrite
381: .getRoot(),
382: new HashSet(),
383: status,
384: new SubProgressMonitor(
385: monitor,
386: 200));
387: final TextChange change = subRewrite
388: .createChange();
389: if (change != null)
390: manager
391: .manage(
392: subRewrite
393: .getCu(),
394: change);
395: }
396: }
397: }
398: }
399: } catch (CoreException exception) {
400: JavaPlugin.log(exception);
401: status
402: .merge(RefactoringStatus
403: .createFatalErrorStatus(RefactoringCoreMessages.UseSuperTypeProcessor_internal_error));
404: }
405: }
406:
407: public final void acceptBinding(
408: final String key,
409: final IBinding binding) {
410: // Do nothing
411: }
412: }, new NullProgressMonitor());
413: }
414: return manager;
415: } finally {
416: monitor.done();
417: }
418: }
419:
420: /*
421: * @see org.eclipse.jdt.internal.corext.refactoring.structure.constraints.SuperTypeRefactoringProcessor#createContraintSolver(org.eclipse.jdt.internal.corext.refactoring.structure.constraints.SuperTypeConstraintsModel)
422: */
423: protected final SuperTypeConstraintsSolver createContraintSolver(
424: final SuperTypeConstraintsModel model) {
425: return new SuperTypeConstraintsSolver(model);
426: }
427:
428: /**
429: * Returns the number of files that are affected from the last change
430: * generation.
431: *
432: * @return The number of files which are affected
433: */
434: public final int getChanges() {
435: return fChanges;
436: }
437:
438: /*
439: * @see org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor#getElements()
440: */
441: public final Object[] getElements() {
442: return new Object[] { fSubType };
443: }
444:
445: /*
446: * @see org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor#getIdentifier()
447: */
448: public final String getIdentifier() {
449: return IDENTIFIER;
450: }
451:
452: /*
453: * @see org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor#getProcessorName()
454: */
455: public final String getProcessorName() {
456: return RefactoringCoreMessages.UseSuperTypeProcessor_name;
457: }
458:
459: /**
460: * Returns the subtype to be replaced.
461: *
462: * @return The subtype to be replaced
463: */
464: public final IType getSubType() {
465: return fSubType;
466: }
467:
468: /**
469: * Returns the supertype as replacement.
470: *
471: * @return The supertype as replacement
472: */
473: public final IType getSuperType() {
474: return fSuperType;
475: }
476:
477: /**
478: * {@inheritDoc}
479: */
480: public final RefactoringStatus initialize(
481: final RefactoringArguments arguments) {
482: if (arguments instanceof JavaRefactoringArguments) {
483: final JavaRefactoringArguments extended = (JavaRefactoringArguments) arguments;
484: String handle = extended
485: .getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT);
486: if (handle != null) {
487: final IJavaElement element = JavaRefactoringDescriptorUtil
488: .handleToElement(extended.getProject(), handle,
489: false);
490: if (element == null
491: || !element.exists()
492: || element.getElementType() != IJavaElement.TYPE)
493: return ScriptableRefactoring
494: .createInputFatalStatus(element,
495: getRefactoring().getName(),
496: IJavaRefactorings.USE_SUPER_TYPE);
497: else
498: fSubType = (IType) element;
499: } else
500: return RefactoringStatus
501: .createFatalErrorStatus(Messages
502: .format(
503: RefactoringCoreMessages.InitializableRefactoring_argument_not_exist,
504: JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT));
505: handle = extended
506: .getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_ELEMENT + 1);
507: if (handle != null) {
508: final IJavaElement element = JavaRefactoringDescriptorUtil
509: .handleToElement(extended.getProject(), handle,
510: false);
511: if (element == null
512: || !element.exists()
513: || element.getElementType() != IJavaElement.TYPE)
514: return ScriptableRefactoring
515: .createInputFatalStatus(element,
516: getRefactoring().getName(),
517: IJavaRefactorings.USE_SUPER_TYPE);
518: else
519: fSuperType = (IType) element;
520: } else
521: return RefactoringStatus
522: .createFatalErrorStatus(Messages
523: .format(
524: RefactoringCoreMessages.InitializableRefactoring_argument_not_exist,
525: JavaRefactoringDescriptorUtil.ATTRIBUTE_ELEMENT + 1));
526: final String instance = extended
527: .getAttribute(ATTRIBUTE_INSTANCEOF);
528: if (instance != null) {
529: fInstanceOf = Boolean.valueOf(instance).booleanValue();
530: } else
531: return RefactoringStatus
532: .createFatalErrorStatus(Messages
533: .format(
534: RefactoringCoreMessages.InitializableRefactoring_argument_not_exist,
535: ATTRIBUTE_INSTANCEOF));
536: } else
537: return RefactoringStatus
538: .createFatalErrorStatus(RefactoringCoreMessages.InitializableRefactoring_inacceptable_arguments);
539: return new RefactoringStatus();
540: }
541:
542: /*
543: * @see org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor#isApplicable()
544: */
545: public final boolean isApplicable() throws CoreException {
546: return Checks.isAvailable(fSubType)
547: && Checks.isAvailable(fSuperType)
548: && !fSubType.isAnonymous() && !fSubType.isAnnotation()
549: && !fSuperType.isAnonymous()
550: && !fSuperType.isAnnotation() && !fSuperType.isEnum();
551: }
552:
553: /*
554: * @see org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor#loadParticipants(org.eclipse.ltk.core.refactoring.RefactoringStatus,org.eclipse.ltk.core.refactoring.participants.SharableParticipants)
555: */
556: public final RefactoringParticipant[] loadParticipants(
557: final RefactoringStatus status,
558: final SharableParticipants sharedParticipants)
559: throws CoreException {
560: return new RefactoringParticipant[0];
561: }
562:
563: /**
564: * {@inheritDoc}
565: */
566: protected final void rewriteTypeOccurrences(
567: final TextEditBasedChangeManager manager,
568: final ASTRequestor requestor,
569: final CompilationUnitRewrite rewrite,
570: final ICompilationUnit unit, final CompilationUnit node,
571: final Set replacements, final IProgressMonitor monitor)
572: throws CoreException {
573: try {
574: monitor.beginTask("", 100); //$NON-NLS-1$
575: monitor
576: .setTaskName(RefactoringCoreMessages.ExtractInterfaceProcessor_creating);
577: final Collection collection = (Collection) fTypeOccurrences
578: .get(unit);
579: if (collection != null && !collection.isEmpty()) {
580: final IProgressMonitor subMonitor = new SubProgressMonitor(
581: monitor, 100);
582: try {
583: subMonitor.beginTask("", collection.size() * 10); //$NON-NLS-1$
584: subMonitor
585: .setTaskName(RefactoringCoreMessages.ExtractInterfaceProcessor_creating);
586: TType estimate = null;
587: ISourceConstraintVariable variable = null;
588: CompilationUnitRewrite currentRewrite = null;
589: final ICompilationUnit sourceUnit = rewrite.getCu();
590: if (sourceUnit.equals(unit))
591: currentRewrite = rewrite;
592: else
593: currentRewrite = new CompilationUnitRewrite(
594: fOwner, unit, node);
595: for (final Iterator iterator = collection
596: .iterator(); iterator.hasNext();) {
597: variable = (ISourceConstraintVariable) iterator
598: .next();
599: estimate = (TType) variable
600: .getData(SuperTypeConstraintsSolver.DATA_TYPE_ESTIMATE);
601: if (estimate != null
602: && variable instanceof ITypeConstraintVariable) {
603: final ASTNode result = NodeFinder
604: .perform(
605: node,
606: ((ITypeConstraintVariable) variable)
607: .getRange()
608: .getSourceRange());
609: if (result != null)
610: rewriteTypeOccurrence(
611: estimate,
612: currentRewrite,
613: result,
614: currentRewrite
615: .createCategorizedGroupDescription(
616: RefactoringCoreMessages.SuperTypeRefactoringProcessor_update_type_occurrence,
617: SET_SUPER_TYPE));
618: }
619: subMonitor.worked(10);
620: }
621: if (!sourceUnit.equals(unit)) {
622: final TextChange change = currentRewrite
623: .createChange();
624: if (change != null)
625: manager.manage(unit, change);
626: }
627: } finally {
628: subMonitor.done();
629: }
630: }
631: } finally {
632: monitor.done();
633: }
634: }
635:
636: /**
637: * Sets the supertype as replacement.
638: *
639: * @param type
640: * The supertype to set
641: */
642: public final void setSuperType(final IType type) {
643: Assert.isNotNull(type);
644:
645: fSuperType = type;
646: }
647: }
|