-
Notifications
You must be signed in to change notification settings - Fork 252
Add copy refactoring infrastructure to LTK #4047
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,156 @@ | ||
| /******************************************************************************* | ||
| * Copyright (c) 2026 Felix Schmid | ||
| * | ||
| * This program and the accompanying materials | ||
| * are made available under the terms of the Eclipse Public License 2.0 | ||
| * which accompanies this distribution, and is available at | ||
| * https://www.eclipse.org/legal/epl-2.0/ | ||
| * | ||
| * SPDX-License-Identifier: EPL-2.0 | ||
| * | ||
| * Contributors: | ||
| * Felix Schmid - initial API and implementation and/or initial documentation | ||
| *******************************************************************************/ | ||
| package org.eclipse.ltk.core.refactoring.resource; | ||
|
|
||
| import java.net.URI; | ||
| import java.text.MessageFormat; | ||
|
|
||
| import org.eclipse.core.runtime.Assert; | ||
| 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.core.runtime.SubMonitor; | ||
|
|
||
| import org.eclipse.core.resources.IContainer; | ||
| import org.eclipse.core.resources.IFile; | ||
| import org.eclipse.core.resources.IFolder; | ||
| import org.eclipse.core.resources.IResource; | ||
|
|
||
| import org.eclipse.ltk.core.refactoring.Change; | ||
| import org.eclipse.ltk.core.refactoring.ChangeDescriptor; | ||
| import org.eclipse.ltk.core.refactoring.CompositeChange; | ||
| import org.eclipse.ltk.core.refactoring.NullChange; | ||
| import org.eclipse.ltk.core.refactoring.RefactoringStatus; | ||
| import org.eclipse.ltk.core.refactoring.participants.ReorgExecutionLog; | ||
| import org.eclipse.ltk.internal.core.refactoring.BasicElementLabels; | ||
| import org.eclipse.ltk.internal.core.refactoring.RefactoringCoreMessages; | ||
|
|
||
| /** | ||
| * {@link Change} that copies a resource. | ||
| * | ||
| * @since 3.16 | ||
| */ | ||
| public class CopyResourceChange extends ResourceChange { | ||
|
|
||
| private ChangeDescriptor descriptor; | ||
|
|
||
| private final IResource origin; | ||
|
|
||
| private final ReorgExecutionLog log; | ||
|
|
||
| private final IContainer destination; | ||
|
|
||
| public CopyResourceChange(final IResource origin, final ReorgExecutionLog log, final IContainer destination) { | ||
| Assert.isTrue(origin instanceof IFile || origin instanceof IFolder); | ||
| this.origin= origin; | ||
| this.log= log; | ||
| this.destination= destination; | ||
| setValidationMethod(VALIDATE_NOT_DIRTY); | ||
| } | ||
|
|
||
| @Override | ||
| public String getName() { | ||
| return MessageFormat.format(RefactoringCoreMessages.CopyResourceChange_name, | ||
| BasicElementLabels.getPathLabel(origin.getFullPath(), false), | ||
| BasicElementLabels.getResourceName(destination)); | ||
| } | ||
|
|
||
| @Override | ||
| public final Change perform(final IProgressMonitor pm) throws CoreException, OperationCanceledException { | ||
| pm.beginTask(getName(), 2); | ||
| try { | ||
| String newName= log.getNewName(origin); | ||
| if (newName == null) { | ||
| newName= origin.getName(); | ||
| } | ||
|
|
||
| final IResource resAtDest= destination.findMember(newName); | ||
| if (resAtDest != null && resAtDest.exists() && areEqualInWorkspaceOrOnDisk(origin, resAtDest)) { | ||
| return new NullChange(); | ||
| } | ||
|
|
||
| final Change undoOverwrite= deleteIfAlreadyExists(resAtDest, SubMonitor.convert(pm, 1)); | ||
|
|
||
| final IPath copyTo= destination.getFullPath().append(newName); | ||
| origin.copy(copyTo, getReorgFlags(), SubMonitor.convert(pm, 1)); | ||
| log.markAsProcessed(origin); | ||
|
|
||
| if (undoOverwrite != null) { | ||
| return new CompositeChange(RefactoringCoreMessages.CopyResourceChange_undo_composite_name, | ||
| new Change[] { new DeleteResourceChange(copyTo, false), undoOverwrite }); | ||
| } | ||
| return new DeleteResourceChange(copyTo, false); | ||
| } finally { | ||
| pm.done(); | ||
| } | ||
| } | ||
|
|
||
| @Override | ||
| protected IResource getModifiedResource() { | ||
| return origin; | ||
| } | ||
|
|
||
| /** | ||
| * deletes a resource if it exists and returns a <code>Change</code> to undo the deletion | ||
| * | ||
| * @param resource the resource to delete | ||
| * @param pm the progress monitor | ||
| * @return returns an undo <code>Change</code> or <code>null</code> if nothing was deleted | ||
| * @throws CoreException thrown when the resource cannot be accessed | ||
| */ | ||
| private Change deleteIfAlreadyExists(final IResource resource, final IProgressMonitor pm) throws CoreException { | ||
| if (resource == null || !resource.exists()) { | ||
| pm.done(); | ||
| return null; | ||
| } | ||
| SubMonitor subMonitor= SubMonitor.convert(pm, | ||
| RefactoringCoreMessages.MoveResourceChange_progress_delete_destination, 3); | ||
| DeleteResourceChange deleteChange= new DeleteResourceChange(resource.getFullPath(), true); | ||
| deleteChange.initializeValidationData(subMonitor.newChild(1)); | ||
| RefactoringStatus deleteStatus= deleteChange.isValid(subMonitor.newChild(1)); | ||
| if (!deleteStatus.hasFatalError()) { | ||
| return deleteChange.perform(subMonitor.newChild(1)); | ||
| } | ||
| return null; | ||
| } | ||
|
|
||
| private static boolean areEqualInWorkspaceOrOnDisk(final IResource r1, final IResource r2) { | ||
| if (r1 == null || r2 == null) { | ||
| return false; | ||
| } | ||
| if (r1.equals(r2)) { | ||
| return true; | ||
| } | ||
| final URI r1Location= r1.getLocationURI(); | ||
| final URI r2Location= r2.getLocationURI(); | ||
| if (r1Location == null || r2Location == null) { | ||
| return false; | ||
| } | ||
| return r1Location.equals(r2Location); | ||
| } | ||
|
|
||
| private static int getReorgFlags() { | ||
| return IResource.KEEP_HISTORY | IResource.SHALLOW; | ||
| } | ||
|
|
||
| @Override | ||
| public ChangeDescriptor getDescriptor() { | ||
| return descriptor; | ||
| } | ||
|
|
||
| public void setDescriptor(ChangeDescriptor descriptor) { | ||
| this.descriptor= descriptor; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
| @@ -0,0 +1,124 @@ | ||||
| /******************************************************************************* | ||||
| * Copyright (c) 2007, 2026 IBM Corporation and others. | ||||
| * | ||||
| * This program and the accompanying materials | ||||
| * are made available under the terms of the Eclipse Public License 2.0 | ||||
| * which accompanies this distribution, and is available at | ||||
| * https://www.eclipse.org/legal/epl-2.0/ | ||||
| * | ||||
| * SPDX-License-Identifier: EPL-2.0 | ||||
| * | ||||
| * Contributors: | ||||
| * IBM Corporation - initial API and implementation | ||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
I assume the header is copied from somewhere. I did you put IBM in here by intention?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This class needed to be very similar to the
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That’s okay. Increase the upper year to the current year.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||||
| * Felix Schmid - adapted for copy resource descriptor | ||||
| *******************************************************************************/ | ||||
| package org.eclipse.ltk.core.refactoring.resource; | ||||
|
|
||||
| import java.text.MessageFormat; | ||||
| import java.util.Objects; | ||||
|
|
||||
| import org.eclipse.core.runtime.CoreException; | ||||
| import org.eclipse.core.runtime.IPath; | ||||
|
|
||||
| import org.eclipse.core.resources.IResource; | ||||
| import org.eclipse.core.resources.IWorkspaceRoot; | ||||
| import org.eclipse.core.resources.ResourcesPlugin; | ||||
|
|
||||
| import org.eclipse.ltk.core.refactoring.Refactoring; | ||||
| import org.eclipse.ltk.core.refactoring.RefactoringContribution; | ||||
| import org.eclipse.ltk.core.refactoring.RefactoringCore; | ||||
| import org.eclipse.ltk.core.refactoring.RefactoringDescriptor; | ||||
| import org.eclipse.ltk.core.refactoring.RefactoringStatus; | ||||
| import org.eclipse.ltk.core.refactoring.participants.CopyRefactoring; | ||||
| import org.eclipse.ltk.internal.core.refactoring.BasicElementLabels; | ||||
| import org.eclipse.ltk.internal.core.refactoring.RefactoringCoreMessages; | ||||
| import org.eclipse.ltk.internal.core.refactoring.resource.CopyResourcesProcessor; | ||||
|
|
||||
| /** | ||||
| * Refactoring descriptor for the copy resource refactoring. | ||||
| * <p> | ||||
| * An instance of this refactoring descriptor may be obtained by calling | ||||
| * {@link RefactoringContribution#createDescriptor()} on a refactoring contribution requested by | ||||
| * invoking {@link RefactoringCore#getRefactoringContribution(String)} with the refactoring id | ||||
| * ({@link #ID}). | ||||
| * </p> | ||||
| * <p> | ||||
| * Note: this class is not intended to be subclassed or instantiated by clients. | ||||
| * </p> | ||||
| * | ||||
| * @since 3.16 | ||||
| * | ||||
| * @noinstantiate This class is not intended to be instantiated by clients. | ||||
| */ | ||||
| public final class CopyResourcesDescriptor extends RefactoringDescriptor { | ||||
| /** | ||||
| * Refactoring id of the 'Copy Resource' refactoring (value: | ||||
| * <code>org.eclipse.ltk.core.refactoring.copy.resources</code>). | ||||
| * <p> | ||||
| * Clients may safely cast the obtained refactoring descriptor to | ||||
| * {@link CopyResourcesDescriptor}. | ||||
| * </p> | ||||
| */ | ||||
| public static final String ID= "org.eclipse.ltk.core.refactoring.copy.resources"; //$NON-NLS-1$ | ||||
|
|
||||
| private IPath[] resourcePaths; | ||||
|
|
||||
| private IPath[] destinationPaths; | ||||
|
|
||||
| /** | ||||
| * Creates a new refactoring descriptor. | ||||
| * <p> | ||||
| * Clients should not instantiated this class but use | ||||
| * {@link RefactoringCore#getRefactoringContribution(String)} with {@link #ID} to get the | ||||
| * contribution that can create the descriptor. | ||||
| * </p> | ||||
| */ | ||||
| public CopyResourcesDescriptor() { | ||||
| super(ID, null, RefactoringCoreMessages.RenameResourceDescriptor_unnamed_descriptor, null, | ||||
| RefactoringDescriptor.STRUCTURAL_CHANGE | RefactoringDescriptor.MULTI_CHANGE); | ||||
| } | ||||
|
|
||||
| public IPath[] getResourcePaths() { | ||||
| return resourcePaths; | ||||
| } | ||||
|
|
||||
| public IPath[] getDestinationPaths() { | ||||
| return destinationPaths; | ||||
| } | ||||
|
|
||||
| @Override | ||||
| public Refactoring createRefactoring(RefactoringStatus status) throws CoreException { | ||||
| IWorkspaceRoot wsRoot= ResourcesPlugin.getWorkspace().getRoot(); | ||||
| IResource[] resources= new IResource[resourcePaths.length]; | ||||
| for (int i= 0; i < resourcePaths.length; i++) { | ||||
| IResource resource= wsRoot.findMember(resourcePaths[i]); | ||||
| if (resource == null || !resource.exists()) { | ||||
| status.addFatalError(MessageFormat.format( | ||||
| RefactoringCoreMessages.CopyResourcesDescriptor_error_resource_not_exists, | ||||
| BasicElementLabels.getPathLabel(resourcePaths[i], false))); | ||||
| return null; | ||||
| } | ||||
| resources[i]= resource; | ||||
| } | ||||
| return new CopyRefactoring(new CopyResourcesProcessor(resources, destinationPaths)); | ||||
| } | ||||
|
|
||||
| public void setResourcePaths(IPath[] resourcePaths) { | ||||
| Objects.requireNonNull(resourcePaths); | ||||
| this.resourcePaths= resourcePaths; | ||||
| } | ||||
|
|
||||
| public void setResources(IResource[] resources) { | ||||
| Objects.requireNonNull(resources); | ||||
| IPath[] paths= new IPath[resources.length]; | ||||
| for (int i= 0; i < paths.length; i++) { | ||||
| paths[i]= resources[i].getFullPath(); | ||||
| } | ||||
| setResourcePaths(paths); | ||||
| } | ||||
|
|
||||
| public void setDestinationPaths(IPath[] destinationPaths) { | ||||
| Objects.requireNonNull(destinationPaths); | ||||
| this.destinationPaths= destinationPaths; | ||||
| } | ||||
| } | ||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this is supposed to be a public API, you must bump bundle version to
3.16.0and addsince 3.16javadoc tags on every new API typeThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the heads-up, done.
Everything that was made public was done to align in with how the move/delete classes do it, a discussion about this was also had here #2262 (comment).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
API tooling complains, and I see now the package is not exported as API. Is this intentional?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be fixed now.