/******************************************************************************* * Copyright (c) 2010 itemis AG (http://www.itemis.eu) and others. * This program and the accompanying materials are made available under the * terms of the Eclipse Public License 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0. * * SPDX-License-Identifier: EPL-2.0 *******************************************************************************/ package org.eclipse.xtext.ui.refactoring.impl; import static com.google.common.collect.Iterables.*; import static com.google.common.collect.Lists.*; import static org.eclipse.ltk.core.refactoring.RefactoringStatus.*; import java.util.List; import java.util.Map; import org.apache.log4j.Logger; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.SubMonitor; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.common.util.WrappedException; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.ltk.core.refactoring.Change; import org.eclipse.ltk.core.refactoring.RefactoringStatus; import org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext; import org.eclipse.ltk.core.refactoring.participants.ParticipantManager; import org.eclipse.ltk.core.refactoring.participants.RefactoringParticipant; import org.eclipse.ltk.core.refactoring.participants.RenameArguments; import org.eclipse.ltk.core.refactoring.participants.RenameParticipant; import org.eclipse.ltk.core.refactoring.participants.SharableParticipants; import org.eclipse.xtext.Constants; import org.eclipse.xtext.ui.XtextProjectHelper; import org.eclipse.xtext.ui.refactoring.ElementRenameArguments; import org.eclipse.xtext.ui.refactoring.IChangeRedirector; import org.eclipse.xtext.ui.refactoring.IDependentElementsCalculator; import org.eclipse.xtext.ui.refactoring.IRefactoringUpdateAcceptor; import org.eclipse.xtext.ui.refactoring.IRenameStrategy; import org.eclipse.xtext.ui.refactoring.IRenameStrategy.Provider.NoSuchStrategyException; import org.eclipse.xtext.ui.refactoring.IRenamedElementTracker; import org.eclipse.xtext.ui.refactoring.ui.IRenameElementContext; import com.google.inject.Inject; import com.google.inject.Provider; import com.google.inject.name.Named; /** * LTK {@link org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor} for an Xtext element rename * refactoring. * * @author Jan Koehnlein - Initial contribution and API * @author Holger Schill */ public class RenameElementProcessor extends AbstractRenameProcessor { protected static final Logger LOG = Logger.getLogger(RenameElementProcessor.class); @Inject private RefactoringResourceSetProvider resourceSetProvider; @Inject private IDependentElementsCalculator dependentElementsCalculator; @Inject private IRenameStrategy.Provider strategyProvider; @Inject private ProjectUtil projectUtil; @Inject private ReferenceUpdaterDispatcher referenceUpdaterDispatcher; @Inject private IRenamedElementTracker renameElementTracker; @Inject @Named(Constants.LANGUAGE_NAME) private String languageName; @Inject private Provider<StatusWrapper> statusProvider; private StatusWrapper status; private IRenameElementContext renameElementContext; private RefactoringResourceSetProvider resourceSets; private ResourceSet resourceSet; private URI targetElementURI; private EObject targetElement; private IRenameStrategy renameStrategy; private String newName; @Inject private Provider<IRefactoringUpdateAcceptor> updateAcceptorProvider; private ElementRenameArguments renameArguments; private IRefactoringUpdateAcceptor currentUpdateAcceptor; @Override public boolean initialize(final IRenameElementContext renameElementContext) { try { status = statusProvider.get(); resourceSets = new CachingResourceSetProvider(resourceSetProvider); this.renameElementContext = renameElementContext; this.targetElementURI = renameElementContext.getTargetElementURI(); resourceSet = getResourceSet(renameElementContext); // we may fail to obtain a resourceSet - in that case, a fatal problem was logged already // so we just stop the initialization process if (resourceSet != null) { targetElement = resourceSet.getEObject(targetElementURI, true); if (targetElement == null) { status.add(FATAL, "Rename target element {0} can not be resolved", targetElementURI); } else { this.renameStrategy = createRenameElementStrategy(targetElement, renameElementContext); if (this.renameStrategy == null) return false; } } } catch (NoSuchStrategyException e) { status.add(FATAL, e.getMessage()); } catch (Exception e) { handleException(e, status); throw (e instanceof RuntimeException) ? (RuntimeException) e : new WrappedException(e); } return true; } protected ResourceSet getResourceSet(IRenameElementContext renameElementContext) { if (resourceSet == null) resourceSet = createResourceSet(renameElementContext); return resourceSet; } protected ResourceSet createResourceSet(IRenameElementContext renameElementContext) { IProject project = projectUtil.getProject(renameElementContext.getTargetElementURI()); if (project == null) { status.add(FATAL, "Could not find project for ", renameElementContext.getTargetElementURI()); return null; } return resourceSet = resourceSets.get(project); } protected boolean isValidTargetFile(Resource resource, StatusWrapper status) { IFile targetFile = projectUtil.findFileStorage(resource.getURI(), true); if (targetFile != null) return true; String path = (resource.getURI().isPlatformResource()) ? resource.getURI().toPlatformString(true) : resource.getURI().toString(); status.add(FATAL, "Rename target file '" + path + "' cannot be accessed", resource.getURI()); return false; } protected IRenameStrategy createRenameElementStrategy(EObject targetElement, IRenameElementContext renameElementContext) throws NoSuchStrategyException { IRenameStrategy result = strategyProvider.get(targetElement, renameElementContext); return result; } public IRenameStrategy getRenameElementStrategy() { return renameStrategy; } @Override public Object[] getElements() { return new Object[] { targetElementURI }; } @Override public String getOriginalName() { return renameStrategy.getOriginalName(); } @Override public RefactoringStatus validateNewName(String newName) { return renameStrategy.validateNewName(newName); } protected String getLanguageName() { return languageName; } @Override public String getIdentifier() { return getLanguageName() + ".ui.refactoring.Processor"; } @Override public String getProcessorName() { return "Rename element"; } @Override public boolean isApplicable() throws CoreException { return true; } @Override public void setNewName(String newName) { this.newName = newName; } @Override public String getNewName() { return newName; } @Override public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException, OperationCanceledException { isValidTargetFile(targetElement.eResource(), status); if(!status.getRefactoringStatus().hasFatalError()) status.merge(validateNewName(newName)); return status.getRefactoringStatus(); } protected Iterable<URI> getElementURIs() { List<URI> elementURIs = newArrayList(); for(Object element: getElements()) { if(element instanceof URI) elementURIs.add((URI) element); } return elementURIs; } @Override public RefactoringStatus checkFinalConditions(IProgressMonitor monitor, CheckConditionsContext context) throws CoreException, OperationCanceledException { SubMonitor progress = SubMonitor.convert(monitor, 100); status = statusProvider.get(); try { currentUpdateAcceptor = updateAcceptorProvider.get(); transferChangeRedirector(currentUpdateAcceptor); Iterable<URI> dependentElementURIs = dependentElementsCalculator.getDependentElementURIs(targetElement, progress.newChild(1)); Map<URI, URI> original2newElementURIs = renameElementTracker.renameAndTrack( concat(getElementURIs(), dependentElementURIs), newName, resourceSet, renameStrategy, progress.newChild(1)); renameStrategy.createDeclarationUpdates(newName, resourceSet, currentUpdateAcceptor); if (progress.isCanceled()) { throw new OperationCanceledException(); } renameArguments = new ElementRenameArguments( targetElementURI, newName, renameStrategy, original2newElementURIs, resourceSets); if (progress.isCanceled()) { throw new OperationCanceledException(); } referenceUpdaterDispatcher.createReferenceUpdates( renameArguments, resourceSet, currentUpdateAcceptor, progress.newChild(98)); status.merge(currentUpdateAcceptor.getRefactoringStatus()); } catch (OperationCanceledException e) { throw e; } catch (Exception exc) { handleException(exc, status); } return status.getRefactoringStatus(); } protected void transferChangeRedirector(IRefactoringUpdateAcceptor currentUpdateAcceptor2) { if(currentUpdateAcceptor instanceof IChangeRedirector.Aware && renameElementContext instanceof IChangeRedirector.Aware) ((IChangeRedirector.Aware) currentUpdateAcceptor).setChangeRedirector( ((IChangeRedirector.Aware)getRenameElementContext()).getChangeRedirector()); } @Override public Change createChange(IProgressMonitor monitor) throws CoreException, OperationCanceledException { return currentUpdateAcceptor.createCompositeChange("Rename " + renameStrategy.getOriginalName() + " to " + newName, monitor); } @Override public RefactoringParticipant[] loadParticipants(RefactoringStatus status, SharableParticipants sharedParticipants) throws CoreException { RenameParticipant[] renameParticipants = ParticipantManager.loadRenameParticipants(status, this, renameElementContext, new RenameArguments(newName, true), new String[] { XtextProjectHelper.NATURE_ID }, sharedParticipants); return renameParticipants; } protected void handleException(Exception exc, StatusWrapper status) { status.add(FATAL, "Error during refactoring: {0}", exc, LOG); } public IRenameElementContext getRenameElementContext() { return renameElementContext; } protected RefactoringResourceSetProvider getResourceSetProvider() { return resourceSetProvider; } protected ElementRenameArguments getRenameArguments() { return renameArguments; } protected EObject getTargetElement() { return targetElement; } public Provider<StatusWrapper> getStatusProvider() { return statusProvider; } }