Refactoring-Tools in Eclipse intern ansprechen

Aus Eclipse
Version vom 19. Mai 2011, 16:12 Uhr von Thies (Diskussion | Beiträge)

(Unterschied) ← Nächstältere Version | Aktuelle Version (Unterschied) | Nächstjüngere Version → (Unterschied)
Wechseln zu: Navigation, Suche

◄== zurück zur Übersichtsseite


Anhand des Refactoring-Tools Change Method Signature wird nachfolgend gezeigt, wie ein fremdes Tool aus der eigenen Anwendung aufgerufen wird:

  1. Zunächst wird ein (zum konkreten Refactoring passendes) RefactoringProcessor Objekt erzeugt (meist eine Erweiterung von org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor). Für das Beispiel-Refactoring-Tool Change Method Signature ist das die Klasse org.eclipse.jdt.internal.corext.refactoring.structure.ChangeSignatureProcessor:
     ChangeSignatureProcessor processor = new ChangeSignatureProcessor(IMethod);
    Die abstrakte Oberklasse org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor definiert das Protokoll zwischen der Refactoring-Klasse und dem speziellen RefactoringProcessor. Der RefactoringProcessor wird für die Validierung der Vor- und Nachbedingungen und für das Durchführen der eigentlichen Änderungen (Erzeugen des Change Objekts) verwendet. Außerdem lädt er die Teilhaber des Refactorings (refactoring participants).

  2. Die gewünschte Änderung wird durch den Aufruf einer entsprechenden Methode beim RefactoringProcessor angemeldet. Um z.B. den Access Modifier der Methode auf public zu ändern schreiben wir:
    processor.setVisibility(JdtFlags.getVisibilityCode("public"));

  3. Ein org.eclipse.ltk.core.refactoring.participants.ProcessorBasedRefactoring Objekt (eine Erweiterung von org.eclipse.ltk.core.refactoring.Refactoring) wird auf Basis des RefactoringProcessors erzeugt:
    Refactoring refactoring = new ProcessorBasedRefactoring(processor);
    ProcessorBasedRefactoring besteht aus einem RefactoringProcessor und 0..n refactoring participants.

  4. Ein org.eclipse.ltk.core.refactoring.CheckConditionsOperation Objekt wird für das gegebene Refactoring erzeugt:
    CheckConditionsOperation checkCondOp = new CheckConditionsOperation(refactoring, CheckConditionsOperation.ALL_CONDITIONS);
    CheckConditionsOperation validiert die Vor- und Nachbedingungen des Refactorings. Der Parameter CheckConditionsOperation.ALL_CONDITIONS gibt an, ob die Vor- als auch Nachbedingungen überprüft werden sollen (mit den Methoden Refactoring.checkInitialConditions() und Refactoring.checkFinalConditions()). Das Erzeugen des CheckConditionsOperation Objekts löst noch keine Validierungen aus, sondern vermerkt dies vorerst nur beim Framework. Das Validieren der Bedingungen sowie die eigentlichen Änderungen werden in diesem Beispiel erst im letzten Schritt mit der Methode run() ausgeführt. Unabhängig davon, ob die Änderungen erfolgt sind oder nicht (weil die Validierungen gescheitert sind), kann der RefactoringStatus mit der Methode CheckConditionsOperation.getStatus() angefragt werden.

  5. Ein org.eclipse.ltk.core.refactoring.CreateChangeOperation Objekt erzeugen:
    CreateChangeOperation createChangeOp = new CreateChangeOperation(checkCondOp, RefactoringStatus.WARNING);
    Mit dem Constructor CreateChangeOperation(CheckConditionsOperation, int) erzwingt man die Validierung der Bedingungen vor der eigentlichen Änderungen. Der zweite int-Parameter gibt die maximale Fehlerstufe des RefactoringStatus Objekts an, ab welcher die Validierungen als gescheitert gelten und das Refactoring abgebrochen werden muss.
    CreateChangeOperation ist für das Erzeugen des Change Objekts zuständig. Zuerst werden die gewünschten Änderungen des Refactorings wieder beim Framework nur angemeldet. Nach dem Ausführen von run() im letzten Schritt kann das Change Objekt mit CreateChangeOperation.getChange() abgefragt werden. Bei negativer Validierung der Bedingungen aus CheckConditionsOperation wird kein Change Objekt erzeugt.

  6. Ein org.eclipse.ltk.core.refactoring.PerformChangeOperation Objekt erzeugen:
    PerformChangeOperation performChangeOp = new PerformChangeOperation(createChangeOp);
    PerformChangeOperation bereitet die Ausführung der im Change Objekt anmeldeten Änderungen vor. Es ist möglich, PerformChangeOperation ein fertiges anderweitig erzeugtes Change Objekt mitzugeben, welches es dann ausführt.

  7. Abschließend wird das Refactoring mit
    IWorkspace.run(performChangeOp, new NullProgressMonitor());
    ausgeführt. Dieser Aufruf ist dem Aufruf von PerformChangeOperation.run() vorzuziehen, da er ein besseres delta batching erzeugt. Der Aufruf stößt folgenden Prozess an:
    • das Change Objekt wird mit CreateChangeOperation.run() erzeugt und mit CreateChangeOperation.getChange() ausgelesen; CreateChangeOperation führt ihrerseits zuerst die Validierungen aus CheckConditionsOperation mit CheckConditionsOperation.run() durch
    • dann wird das erzeugte Change Objekt mit PerformChangeOperation.executeChange() ausgeführt


Um die durchgeführten Änderungen rückgängig zu machen, genügt folgender Dreizeiler:

Change undoChange = performChangeOp.getUndoChange();
performChangeOp = new PerformChangeOperation(undoChange);
workspace.run(performChangeOp, new NullProgressMonitor());


Dasselbe funktioniert für andere Refactoring Tools ganz analog, wie hier für rename field:

import org.eclipse.ltk.core.refactoring.participants.RenameRefactoring;
import org.eclipse.jdt.internal.corext.refactoring.rename.RenameFieldProcessor;
 
public boolean performRename(IField field, String newname) throws CoreException {
 
  RenameFieldProcessor processor = new RenameFieldProcessor(field);
  RenameRefactoring refactoring = new RenameRefactoring(processor);
  processor.setNewElementName(newname);
 
  CheckConditionsOperation conditionChecker = new CheckConditionsOperation(refactoring, CheckConditionsOperation.ALL_CONDITIONS);
 
  CreateChangeOperation create = new CreateChangeOperation(conditionChecker, RefactoringStatus.FATAL);
  PerformChangeOperation change = new PerformChangeOperation(create);
 
  IWorkspace workspace = ResourcesPlugin.getWorkspace();
  workspace.run(change, new NullProgressMonitor());
 
  return change.changeExecuted();
}

Eine weitere, recht elegante Methode aus [1]

RefactoringContribution contribution =
RefactoringCore . getRefactoringContribution (
IJavaRefactorings . RENAME_METHOD );
RenameJavaElementDescriptor descriptor =
( RenameJavaElementDescriptor ) contribution . createDescriptor ();
// set the Java element to refactor
descriptor . setJavaElement ( methodList .get (0));
// new method name from user input
descriptor . setNewName ( newMethodName );
// refactor also the references
descriptor . setUpdateReferences ( true );
RefactoringStatus status = new RefactoringStatus ();
state = descriptor . validateDescriptor ();
try {
Refactoring renameMethod = descriptor . createRefactoring ( state );
state . merge ( renameMethod . checkInitialConditions (pm ));
state . merge ( renameMethod . checkFinalConditions (pm ));
return renameMethod . createChange (pm );
} catch ( CoreException e) {
e. printStackTrace ();
}

Soll eine Rename-Refaktorisierung nicht direkt ausgeführt, sondern zunächst ein Dialog angezeigt werden, bietet sich die Klasse org.eclipse.jdt.ui.refactoring.RenameSupport an, die sich der JDT-Dialoge bedient.

private void rename(IMethod method, String name) throws CoreException {
  RefactoringContribution contribution = RefactoringCore.getRefactoringContribution(IJavaRefactorings.RENAME_METHOD);
  RenameJavaElementDescriptor descriptor = (RenameJavaElementDescriptor) contribution.createDescriptor();
  descriptor.setJavaElement(method);
  descriptor.setNewName(name);
  descriptor.setUpdateReferences(true);
  RenameSupport.create(descriptor).openDialog(getShell());
}

◄== zurück zur Übersichtsseite