First, there is the rename processor "RenameProcessor.java":
package mysomething.refactoring.rename; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IWorkspace; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.jface.text.ITextSelection; import org.eclipse.ltk.core.refactoring.Change; import org.eclipse.ltk.core.refactoring.CompositeChange; import org.eclipse.ltk.core.refactoring.RefactoringStatus; import org.eclipse.ltk.core.refactoring.TextFileChange; import org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext; import org.eclipse.ltk.core.refactoring.participants.ProcessorBasedRefactoring; import org.eclipse.ltk.core.refactoring.participants.RefactoringParticipant; import org.eclipse.ltk.core.refactoring.participants.SharableParticipants; import org.eclipse.text.edits.MultiTextEdit; import org.eclipse.text.edits.ReplaceEdit; import org.eclipse.text.edits.TextEditGroup; import org.eclipse.xtext.parsetree.CompositeNode; import org.eclipse.xtext.parsetree.NodeAdapter; import org.eclipse.xtext.parsetree.NodeUtil; import org.eclipse.xtext.resource.EObjectAtOffsetHelper; import org.eclipse.xtext.resource.IEObjectDescription; import org.eclipse.xtext.resource.IReferenceDescription; import org.eclipse.xtext.resource.IResourceDescription; import org.eclipse.xtext.resource.IResourceDescriptions; import org.eclipse.xtext.resource.XtextResource; import org.eclipse.xtext.ui.core.editor.XtextEditor; import org.eclipse.xtext.util.concurrent.IUnitOfWork; import mysomething.Rule; import mysomething.RuleReference; import mysomething.utils.EObjectResolver; import mysomething.utils.URIFragmentResolver; import com.google.common.base.Predicate; import com.google.common.collect.Iterables; public class RenameProcessor extends org.eclipse.ltk.core.refactoring.participants.RenameProcessor { private XtextEditor editor; private IResourceDescriptions resourceDescriptions; private List<CompositeNode> references = null; private String currentName; private CompositeNode declaration; private IFile file; public RenameProcessor(XtextEditor editor, IResourceDescriptions resourceDescriptions) { this.editor = editor; this.resourceDescriptions = resourceDescriptions; final ITextSelection selection = (ITextSelection) editor .getSelectionProvider().getSelection(); final IEObjectDescription eObjectDescription = editor.getDocument() .readOnly(new EObjectResolver(selection, resourceDescriptions)); declaration = editor.getDocument().readOnly( new URIFragmentResolver(eObjectDescription.getEObjectURI() .fragment())); if (declaration.getElement() instanceof Rule) { Rule r = (Rule) declaration.getElement(); currentName = r.getName(); } file = (IFile) editor.getEditorInput().getAdapter(IFile.class); if (eObjectDescription != null) { references = findReferenceDescriptions(eObjectDescription); } } @Override public Object[] getElements() { return null; } @Override public String getIdentifier() { return "Rename Processor Identifier"; } @Override public String getProcessorName() { return "Rename Processor"; } @Override public boolean isApplicable() throws CoreException { return (references != null); } @Override public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException, OperationCanceledException { RefactoringStatus status = new RefactoringStatus(); if (references == null) status.addFatalError("Could not obtain references!"); return status; } @Override public RefactoringStatus checkFinalConditions(IProgressMonitor pm, CheckConditionsContext context) throws CoreException, OperationCanceledException { RefactoringStatus status = new RefactoringStatus(); RenameRefactoring refactoring = (RenameRefactoring) getRefactoring(); if (currentName.equals(refactoring.getRenameText())) { status.addFatalError("Name unchanged!"); } else if (refactoring.getRenameText().length() <= 0) { status.addFatalError("name must not be empty!"); } return status; } @Override public Change createChange(IProgressMonitor pm) throws CoreException, OperationCanceledException { CompositeChange compositeChange = new CompositeChange("Rename"); pm.beginTask("Rename Refactoring", references.size()); MultiTextEdit multiEdit = new MultiTextEdit(); TextFileChange fileChange = new TextFileChange("Declaraction Renaming", file); fileChange.setEdit(multiEdit); fileChange.setTextType("bnf"); compositeChange.add(fileChange); RenameRefactoring refactoring = (RenameRefactoring) getRefactoring(); String replaceText = refactoring.getRenameText(); ReplaceEdit replaceEdit = new ReplaceEdit(declaration.getOffset(), ((Rule)declaration.getElement()).getName().length(), replaceText); multiEdit.addChild(replaceEdit); TextEditGroup editGroup = new TextEditGroup("declaration update", replaceEdit); fileChange.addTextEditGroup(editGroup); pm.worked(1); for (int i=0; i < references.size(); i++) { CompositeNode reference = references.get(i); replaceEdit = new ReplaceEdit(reference.getOffset(), ((Rule)declaration.getElement()).getName().length(), replaceText); multiEdit.addChild(replaceEdit); editGroup = new TextEditGroup("reference update", replaceEdit); fileChange.addTextEditGroup(editGroup); pm.worked(1); } return compositeChange; } @Override public RefactoringParticipant[] loadParticipants(RefactoringStatus status, SharableParticipants sharedParticipants) throws CoreException { return null; } private List<CompositeNode> findReferenceDescriptions( final IEObjectDescription eObjectDescription) { List<CompositeNode> references = new ArrayList<CompositeNode>(); for (IResourceDescription resourceDescription : resourceDescriptions .getAllResourceDescriptions()) { Iterable<IReferenceDescription> matchingReferenceDescriptors = Iterables .filter(resourceDescription.getReferenceDescriptions(), new Predicate<IReferenceDescription>() { public boolean apply(IReferenceDescription input) { return eObjectDescription .getEObjectURI() .equals(input.getTargetEObjectUri()); } }); for (IReferenceDescription matchingReferenceDescription : matchingReferenceDescriptors) { CompositeNode node = editor.getDocument().readOnly( new URIFragmentResolver(matchingReferenceDescription .getSourceEObjectUri().fragment())); references.add(node); } } return references; } private IFile getProjectFile(String filename) { IWorkspace ws = ResourcesPlugin.getWorkspace(); IWorkspaceRoot root = ws.getRoot(); IProject project = root.getProject(); IFile file = null; if (project == null) { IPath path = ws.getRoot().findMember(filename).getFullPath(); file = ws.getRoot().getFile(path); } else { file = project.getFile(filename); } return file; } }
The handler called from the command "RenameRefactoringHandler.java":
package mysomething.refactoring.rename; import org.eclipse.core.commands.AbstractHandler; import org.eclipse.core.commands.ExecutionEvent; import org.eclipse.core.commands.ExecutionException; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.text.ITextSelection; import org.eclipse.ltk.ui.refactoring.RefactoringWizard; import org.eclipse.ltk.ui.refactoring.RefactoringWizardOpenOperation; import org.eclipse.swt.widgets.Display; import org.eclipse.ui.handlers.HandlerUtil; import org.eclipse.xtext.parsetree.CompositeNode; import org.eclipse.xtext.resource.IEObjectDescription; import org.eclipse.xtext.resource.IResourceDescriptions; import org.eclipse.xtext.ui.core.editor.XtextEditor; import mysomething.Rule; import mysomething.utils.EObjectResolver; import mysomething.utils.URIFragmentResolver; import com.google.inject.Inject; public class RenameRefactoringHandler extends AbstractHandler { @Inject private IResourceDescriptions resourceDescriptions; @Override public Object execute(ExecutionEvent event) throws ExecutionException { try { String currentName = ""; XtextEditor editor = (XtextEditor) HandlerUtil .getActiveEditor(event); final ITextSelection selection = (ITextSelection) editor .getSelectionProvider().getSelection(); final IEObjectDescription eObjectDescription = editor .getDocument() .readOnly( new EObjectResolver(selection, resourceDescriptions)); CompositeNode o = editor.getDocument().readOnly( new URIFragmentResolver(eObjectDescription.getEObjectURI() .fragment())); if (o.getElement() instanceof Rule) { Rule r = (Rule) o.getElement(); currentName = r.getName(); } RenameProcessor processor = new RenameProcessor(editor, resourceDescriptions); RenameRefactoring refactoring = new RenameRefactoring(processor); RenameRefactoringWizard wizard = new RenameRefactoringWizard( refactoring, RefactoringWizard.WIZARD_BASED_USER_INTERFACE); wizard.setRenameText(currentName); RefactoringWizardOpenOperation openOperation = new RefactoringWizardOpenOperation( wizard); openOperation.run(Display.getCurrent().getActiveShell(), "Refactoring not possible!"); } catch (Exception e) { MessageDialog.openInformation( Display.getDefault().getActiveShell(), "Rename Refactoring", "Error while applying refactoring to workbench/wizard: " + e.getMessage()); e.printStackTrace(); } return null; } }
and we have the two gui classes "RenameRefactoringWizard.java" and "RenamePageComposite.java":
package mysomething.refactoring.rename; import org.eclipse.jface.wizard.IWizardPage; import org.eclipse.ltk.core.refactoring.Refactoring; import org.eclipse.ltk.ui.refactoring.RefactoringWizard; import org.eclipse.ltk.ui.refactoring.UserInputWizardPage; import org.eclipse.swt.widgets.Composite; public class RenameRefactoringWizard extends RefactoringWizard { private RenameInputWizardPage wizardPage; private String renameText = ""; public RenameRefactoringWizard(Refactoring refactoring, int flags) { super(refactoring, flags); } @Override protected void addUserInputPages() { wizardPage = new RenameInputWizardPage("Refactoring"); wizardPage.setRenameText(renameText); addPage(wizardPage); } public void setRenameText(String s) { renameText = s; if (wizardPage != null) wizardPage.setRenameText(s); } static class RenameInputWizardPage extends UserInputWizardPage { private RenamePageComposite composite; private String renameText; public RenameInputWizardPage(String name) { super(name); setTitle("Rename Refactoring"); setDescription("Automated Refactoring to rename rule names, rule references etc." + " with the possibility to automatically update references"); } public void createControl(Composite parent) { composite = new RenamePageComposite(parent, 0); composite.getRenameText().setText(renameText); composite.getRenameText().selectAll(); setControl(composite); } public String getRenameText() { return composite.getRenameText().getText(); } public void setRenameText(String s) { renameText = s; if (composite != null) composite.getRenameText().setText(renameText); } @Override public IWizardPage getNextPage() { RenameRefactoring refactoring = (RenameRefactoring) getRefactoring(); if (refactoring != null) { refactoring.setRenameText(getRenameText()); } return super.getNextPage(); } @Override protected boolean performFinish() { RenameRefactoring refactoring = (RenameRefactoring) getRefactoring(); if (refactoring != null) { refactoring.setRenameText(getRenameText()); } return super.performFinish(); } } }
package mysomething.refactoring.rename; import org.eclipse.swt.SWT; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Text; public class RenamePageComposite extends Composite { private Text renameText; public RenamePageComposite(Composite parent, int style) { super(parent, style); final GridLayout gridLayout = new GridLayout(); setLayout(gridLayout); final Composite composite = new Composite(this, SWT.NONE); composite.setLayoutData(new GridData(GridData.FILL, GridData.BEGINNING, true, false)); final GridLayout gridLayout_1 = new GridLayout(); gridLayout_1.numColumns = 2; composite.setLayout(gridLayout_1); final Label renameLabel = new Label(composite, SWT.NONE); renameLabel.setLayoutData(new GridData(GridData.BEGINNING, GridData.CENTER, false, true)); renameLabel.setText("New Name:"); renameText = new Text(composite, SWT.BORDER); renameText.setLayoutData(new GridData(GridData.FILL, GridData.CENTER, true, true)); } @Override public void dispose() { super.dispose(); } public Text getRenameText() { return renameText; } }
Finally, two helper classes that deal with the EObject stuff:
package mysomething.utils; import java.util.Iterator; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.jface.text.ITextSelection; import org.eclipse.xtext.resource.EObjectAtOffsetHelper; import org.eclipse.xtext.resource.IEObjectDescription; import org.eclipse.xtext.resource.IResourceDescription; import org.eclipse.xtext.resource.IResourceDescriptions; import org.eclipse.xtext.resource.XtextResource; import org.eclipse.xtext.util.concurrent.IUnitOfWork; import com.google.common.base.Predicate; import com.google.common.collect.Iterables; public class EObjectResolver implements IUnitOfWork<IEObjectDescription, XtextResource> { private final ITextSelection selection; private IResourceDescriptions resourceDescriptions; public EObjectResolver(ITextSelection selection, IResourceDescriptions resourceDescriptions) { this.selection = selection; this.resourceDescriptions = resourceDescriptions; } public IEObjectDescription exec(XtextResource state) throws Exception { EObject element = EObjectAtOffsetHelper.resolveElementAt(state, selection.getOffset(), null); if (element != null) { final URI eObjectURI = EcoreUtil.getURI(element); IResourceDescription resourceDescription = resourceDescriptions .getResourceDescription(eObjectURI.trimFragment()); if (resourceDescription != null) { Iterator<IEObjectDescription> eObjectDescriptions = Iterables .filter(resourceDescription.getExportedObjects(), new Predicate<IEObjectDescription>() { public boolean apply( IEObjectDescription input) { return input.getEObjectURI().equals( eObjectURI); } }).iterator(); if (eObjectDescriptions.hasNext()) { return eObjectDescriptions.next(); } } } return null; } }
package mysomething.utils; import org.eclipse.emf.ecore.EObject; import org.eclipse.xtext.parsetree.CompositeNode; import org.eclipse.xtext.parsetree.NodeAdapter; import org.eclipse.xtext.parsetree.NodeUtil; import org.eclipse.xtext.resource.XtextResource; import org.eclipse.xtext.util.concurrent.IUnitOfWork; public class URIFragmentResolver implements IUnitOfWork<CompositeNode, XtextResource> { private String uriFragment; public URIFragmentResolver(String uriFragment) { this.uriFragment = uriFragment; } @Override public CompositeNode exec(XtextResource state) throws Exception { EObject o = state.getEObject(uriFragment); NodeAdapter node = NodeUtil.getNodeAdapter(o); return node.getParserNode(); } }