Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ Automatic-Module-Name: org.eclipse.ltk.core.refactoring
Bundle-ManifestVersion: 2
Bundle-Name: %pluginName
Bundle-SymbolicName: org.eclipse.ltk.core.refactoring; singleton:=true
Bundle-Version: 3.15.200.qualifier
Bundle-Version: 3.16.0.qualifier
Bundle-Activator: org.eclipse.ltk.internal.core.refactoring.RefactoringCorePlugin
Bundle-ActivationPolicy: lazy
Bundle-Vendor: %providerName
Expand Down
3 changes: 3 additions & 0 deletions bundles/org.eclipse.ltk.core.refactoring/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -52,5 +52,8 @@
class="org.eclipse.ltk.internal.core.refactoring.resource.CopyProjectRefactoringContribution"
id="org.eclipse.ltk.core.refactoring.copyproject.resource">
</contribution>
<contribution
class="org.eclipse.ltk.internal.core.refactoring.resource.CopyResourcesRefactoringContribution"
id="org.eclipse.ltk.core.refactoring.copy.resources"/>
</extension>
</plugin>
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 {
Copy link
Copy Markdown
Member

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.0 and add since 3.16 javadoc tags on every new API type

Copy link
Copy Markdown
Author

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).

Copy link
Copy Markdown
Member

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?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be fixed now.


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
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* IBM Corporation - initial API and implementation

I assume the header is copied from somewhere. I did you put IBM in here by intention?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This class needed to be very similar to the DeleteResourcesDescriptor, so I copied the file and made the necessary changes. I kept the original header to show that this was not all my creation. The same principle also applies to CopyResourcesHandler & CopyResourcesRefactoringContribution.
I can change the headers if this was incorrect or if there is a better way to do it.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That’s okay. Increase the upper year to the current year.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The 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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,24 @@ public final class RefactoringCoreMessages extends NLS {

public static String CopyProjectProcessor_name;

public static String CopyResourceChange_name;

public static String CopyResourceChange_undo_composite_name;

public static String CopyResourcesDescriptor_error_resource_not_exists;

public static String CopyResourcesProcessor_create_task;

public static String CopyResourcesProcessor_description_multiple;

public static String CopyResourcesProcessor_description_single;

public static String CopyResourcesProcessor_destination_inside_moved;

public static String CopyResourcesProcessor_error_multiple_destinatinos;

public static String CopyResourcesProcessor_name;

public static String CreateChangeOperation_unknown_Refactoring;

public static String DefaultRefactoringDescriptor_cannot_create_refactoring;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,3 +173,13 @@ MoveResourceProcessor_destination_same_as_moved=The destination contains a resou
MoveResourceProcessor_error_destination_not_exists=Destination does not exist
MoveResourceProcessor_error_invalid_destination=Invalid parent
MoveResourceProcessor_processor_name=Move Resources

CopyResourceChange_name=Copy resource ''{0}'' to ''{1}''
CopyResourceChange_undo_composite_name=Delete copy and restore overwritten resource.
CopyResourcesDescriptor_error_resource_not_exists=The resource ''{0}'' to copy does not exist.
CopyResourcesProcessor_create_task=Creating pending copies...
CopyResourcesProcessor_description_multiple=Copy {0} resources to ''{1}''
CopyResourcesProcessor_description_single=Copy ''{0}'' to ''{1}''
CopyResourcesProcessor_destination_inside_moved=Destination is inside copied resource ''{0}''
CopyResourcesProcessor_error_multiple_destinatinos=Can only copy to a single destination.
CopyResourcesProcessor_name=Copy Resources
Loading