Skip to content

Commit e77ea7b

Browse files
committed
Add copy refactoring infrastructure to LTK
Version bump(s) for 4.41 stream Add "since" to javadoc of new APIs Fix copyright header year Fix bundle version Improve entry location for LTK copy refactoring Add tests for LTK copy refactoring Fix
1 parent bacf493 commit e77ea7b

19 files changed

Lines changed: 1257 additions & 3 deletions

File tree

bundles/org.eclipse.ltk.core.refactoring/META-INF/MANIFEST.MF

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ Automatic-Module-Name: org.eclipse.ltk.core.refactoring
33
Bundle-ManifestVersion: 2
44
Bundle-Name: %pluginName
55
Bundle-SymbolicName: org.eclipse.ltk.core.refactoring; singleton:=true
6-
Bundle-Version: 3.15.200.qualifier
6+
Bundle-Version: 3.16.0.qualifier
77
Bundle-Activator: org.eclipse.ltk.internal.core.refactoring.RefactoringCorePlugin
88
Bundle-ActivationPolicy: lazy
99
Bundle-Vendor: %providerName

bundles/org.eclipse.ltk.core.refactoring/plugin.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,5 +52,8 @@
5252
class="org.eclipse.ltk.internal.core.refactoring.resource.CopyProjectRefactoringContribution"
5353
id="org.eclipse.ltk.core.refactoring.copyproject.resource">
5454
</contribution>
55+
<contribution
56+
class="org.eclipse.ltk.internal.core.refactoring.resource.CopyResourcesRefactoringContribution"
57+
id="org.eclipse.ltk.core.refactoring.copy.resources"/>
5558
</extension>
5659
</plugin>
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2026 Felix Schmid
3+
*
4+
* This program and the accompanying materials
5+
* are made available under the terms of the Eclipse Public License 2.0
6+
* which accompanies this distribution, and is available at
7+
* https://www.eclipse.org/legal/epl-2.0/
8+
*
9+
* SPDX-License-Identifier: EPL-2.0
10+
*
11+
* Contributors:
12+
* Felix Schmid - initial API and implementation and/or initial documentation
13+
*******************************************************************************/
14+
package org.eclipse.ltk.core.refactoring.resource;
15+
16+
import java.net.URI;
17+
import java.text.MessageFormat;
18+
19+
import org.eclipse.core.runtime.Assert;
20+
import org.eclipse.core.runtime.CoreException;
21+
import org.eclipse.core.runtime.IPath;
22+
import org.eclipse.core.runtime.IProgressMonitor;
23+
import org.eclipse.core.runtime.OperationCanceledException;
24+
import org.eclipse.core.runtime.SubMonitor;
25+
26+
import org.eclipse.core.resources.IContainer;
27+
import org.eclipse.core.resources.IFile;
28+
import org.eclipse.core.resources.IFolder;
29+
import org.eclipse.core.resources.IResource;
30+
31+
import org.eclipse.ltk.core.refactoring.Change;
32+
import org.eclipse.ltk.core.refactoring.ChangeDescriptor;
33+
import org.eclipse.ltk.core.refactoring.CompositeChange;
34+
import org.eclipse.ltk.core.refactoring.NullChange;
35+
import org.eclipse.ltk.core.refactoring.RefactoringStatus;
36+
import org.eclipse.ltk.core.refactoring.participants.ReorgExecutionLog;
37+
import org.eclipse.ltk.internal.core.refactoring.BasicElementLabels;
38+
import org.eclipse.ltk.internal.core.refactoring.RefactoringCoreMessages;
39+
40+
/**
41+
* {@link Change} that copies a resource.
42+
*
43+
* @since 3.16
44+
*/
45+
public class CopyResourceChange extends ResourceChange {
46+
47+
private ChangeDescriptor descriptor;
48+
49+
private final IResource origin;
50+
51+
private final ReorgExecutionLog log;
52+
53+
private final IContainer destination;
54+
55+
public CopyResourceChange(final IResource origin, final ReorgExecutionLog log, final IContainer destination) {
56+
Assert.isTrue(origin instanceof IFile || origin instanceof IFolder);
57+
this.origin= origin;
58+
this.log= log;
59+
this.destination= destination;
60+
setValidationMethod(VALIDATE_NOT_DIRTY);
61+
}
62+
63+
@Override
64+
public String getName() {
65+
return MessageFormat.format(RefactoringCoreMessages.CopyResourceChange_name,
66+
BasicElementLabels.getPathLabel(origin.getFullPath(), false),
67+
BasicElementLabels.getResourceName(destination));
68+
}
69+
70+
@Override
71+
public final Change perform(final IProgressMonitor pm) throws CoreException, OperationCanceledException {
72+
pm.beginTask(getName(), 2);
73+
try {
74+
String newName= log.getNewName(origin);
75+
if (newName == null) {
76+
newName= origin.getName();
77+
}
78+
79+
final IResource resAtDest= destination.findMember(newName);
80+
if (resAtDest != null && resAtDest.exists() && areEqualInWorkspaceOrOnDisk(origin, resAtDest)) {
81+
return new NullChange();
82+
}
83+
84+
final Change undoOverwrite= deleteIfAlreadyExists(resAtDest, SubMonitor.convert(pm, 1));
85+
86+
final IPath copyTo= destination.getFullPath().append(newName);
87+
origin.copy(copyTo, getReorgFlags(), SubMonitor.convert(pm, 1));
88+
log.markAsProcessed(origin);
89+
90+
if (undoOverwrite != null) {
91+
return new CompositeChange(RefactoringCoreMessages.CopyResourceChange_undo_composite_name,
92+
new Change[] { new DeleteResourceChange(copyTo, false), undoOverwrite });
93+
}
94+
return new DeleteResourceChange(copyTo, false);
95+
} finally {
96+
pm.done();
97+
}
98+
}
99+
100+
@Override
101+
protected IResource getModifiedResource() {
102+
return origin;
103+
}
104+
105+
/**
106+
* deletes a resource if it exists and returns a <code>Change</code> to undo the deletion
107+
*
108+
* @param resource the resource to delete
109+
* @param pm the progress monitor
110+
* @return returns an undo <code>Change</code> or <code>null</code> if nothing was deleted
111+
* @throws CoreException thrown when the resource cannot be accessed
112+
*/
113+
private Change deleteIfAlreadyExists(final IResource resource, final IProgressMonitor pm) throws CoreException {
114+
if (resource == null || !resource.exists()) {
115+
pm.done();
116+
return null;
117+
}
118+
SubMonitor subMonitor= SubMonitor.convert(pm,
119+
RefactoringCoreMessages.MoveResourceChange_progress_delete_destination, 3);
120+
DeleteResourceChange deleteChange= new DeleteResourceChange(resource.getFullPath(), true);
121+
deleteChange.initializeValidationData(subMonitor.newChild(1));
122+
RefactoringStatus deleteStatus= deleteChange.isValid(subMonitor.newChild(1));
123+
if (!deleteStatus.hasFatalError()) {
124+
return deleteChange.perform(subMonitor.newChild(1));
125+
}
126+
return null;
127+
}
128+
129+
private static boolean areEqualInWorkspaceOrOnDisk(final IResource r1, final IResource r2) {
130+
if (r1 == null || r2 == null) {
131+
return false;
132+
}
133+
if (r1.equals(r2)) {
134+
return true;
135+
}
136+
final URI r1Location= r1.getLocationURI();
137+
final URI r2Location= r2.getLocationURI();
138+
if (r1Location == null || r2Location == null) {
139+
return false;
140+
}
141+
return r1Location.equals(r2Location);
142+
}
143+
144+
private static int getReorgFlags() {
145+
return IResource.KEEP_HISTORY | IResource.SHALLOW;
146+
}
147+
148+
@Override
149+
public ChangeDescriptor getDescriptor() {
150+
return descriptor;
151+
}
152+
153+
public void setDescriptor(ChangeDescriptor descriptor) {
154+
this.descriptor= descriptor;
155+
}
156+
}
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2007, 2026 IBM Corporation and others.
3+
*
4+
* This program and the accompanying materials
5+
* are made available under the terms of the Eclipse Public License 2.0
6+
* which accompanies this distribution, and is available at
7+
* https://www.eclipse.org/legal/epl-2.0/
8+
*
9+
* SPDX-License-Identifier: EPL-2.0
10+
*
11+
* Contributors:
12+
* IBM Corporation - initial API and implementation
13+
* Felix Schmid - adapted for copy resource descriptor
14+
*******************************************************************************/
15+
package org.eclipse.ltk.core.refactoring.resource;
16+
17+
import java.text.MessageFormat;
18+
import java.util.Objects;
19+
20+
import org.eclipse.core.runtime.CoreException;
21+
import org.eclipse.core.runtime.IPath;
22+
23+
import org.eclipse.core.resources.IResource;
24+
import org.eclipse.core.resources.IWorkspaceRoot;
25+
import org.eclipse.core.resources.ResourcesPlugin;
26+
27+
import org.eclipse.ltk.core.refactoring.Refactoring;
28+
import org.eclipse.ltk.core.refactoring.RefactoringContribution;
29+
import org.eclipse.ltk.core.refactoring.RefactoringCore;
30+
import org.eclipse.ltk.core.refactoring.RefactoringDescriptor;
31+
import org.eclipse.ltk.core.refactoring.RefactoringStatus;
32+
import org.eclipse.ltk.core.refactoring.participants.CopyRefactoring;
33+
import org.eclipse.ltk.internal.core.refactoring.BasicElementLabels;
34+
import org.eclipse.ltk.internal.core.refactoring.RefactoringCoreMessages;
35+
import org.eclipse.ltk.internal.core.refactoring.resource.CopyResourcesProcessor;
36+
37+
/**
38+
* Refactoring descriptor for the copy resource refactoring.
39+
* <p>
40+
* An instance of this refactoring descriptor may be obtained by calling
41+
* {@link RefactoringContribution#createDescriptor()} on a refactoring contribution requested by
42+
* invoking {@link RefactoringCore#getRefactoringContribution(String)} with the refactoring id
43+
* ({@link #ID}).
44+
* </p>
45+
* <p>
46+
* Note: this class is not intended to be subclassed or instantiated by clients.
47+
* </p>
48+
*
49+
* @since 3.16
50+
*
51+
* @noinstantiate This class is not intended to be instantiated by clients.
52+
*/
53+
public final class CopyResourcesDescriptor extends RefactoringDescriptor {
54+
/**
55+
* Refactoring id of the 'Copy Resource' refactoring (value:
56+
* <code>org.eclipse.ltk.core.refactoring.copy.resources</code>).
57+
* <p>
58+
* Clients may safely cast the obtained refactoring descriptor to
59+
* {@link CopyResourcesDescriptor}.
60+
* </p>
61+
*/
62+
public static final String ID= "org.eclipse.ltk.core.refactoring.copy.resources"; //$NON-NLS-1$
63+
64+
private IPath[] resourcePaths;
65+
66+
private IPath[] destinationPaths;
67+
68+
/**
69+
* Creates a new refactoring descriptor.
70+
* <p>
71+
* Clients should not instantiated this class but use
72+
* {@link RefactoringCore#getRefactoringContribution(String)} with {@link #ID} to get the
73+
* contribution that can create the descriptor.
74+
* </p>
75+
*/
76+
public CopyResourcesDescriptor() {
77+
super(ID, null, RefactoringCoreMessages.RenameResourceDescriptor_unnamed_descriptor, null,
78+
RefactoringDescriptor.STRUCTURAL_CHANGE | RefactoringDescriptor.MULTI_CHANGE);
79+
}
80+
81+
public IPath[] getResourcePaths() {
82+
return resourcePaths;
83+
}
84+
85+
public IPath[] getDestinationPaths() {
86+
return destinationPaths;
87+
}
88+
89+
@Override
90+
public Refactoring createRefactoring(RefactoringStatus status) throws CoreException {
91+
IWorkspaceRoot wsRoot= ResourcesPlugin.getWorkspace().getRoot();
92+
IResource[] resources= new IResource[resourcePaths.length];
93+
for (int i= 0; i < resourcePaths.length; i++) {
94+
IResource resource= wsRoot.findMember(resourcePaths[i]);
95+
if (resource == null || !resource.exists()) {
96+
status.addFatalError(MessageFormat.format(
97+
RefactoringCoreMessages.CopyResourcesDescriptor_error_resource_not_exists,
98+
BasicElementLabels.getPathLabel(resourcePaths[i], false)));
99+
return null;
100+
}
101+
resources[i]= resource;
102+
}
103+
return new CopyRefactoring(new CopyResourcesProcessor(resources, destinationPaths));
104+
}
105+
106+
public void setResourcePaths(IPath[] resourcePaths) {
107+
Objects.requireNonNull(resourcePaths);
108+
this.resourcePaths= resourcePaths;
109+
}
110+
111+
public void setResources(IResource[] resources) {
112+
Objects.requireNonNull(resources);
113+
IPath[] paths= new IPath[resources.length];
114+
for (int i= 0; i < paths.length; i++) {
115+
paths[i]= resources[i].getFullPath();
116+
}
117+
setResourcePaths(paths);
118+
}
119+
120+
public void setDestinationPaths(IPath[] destinationPaths) {
121+
Objects.requireNonNull(destinationPaths);
122+
this.destinationPaths= destinationPaths;
123+
}
124+
}

bundles/org.eclipse.ltk.core.refactoring/src/org/eclipse/ltk/internal/core/refactoring/RefactoringCoreMessages.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,24 @@ public final class RefactoringCoreMessages extends NLS {
4747

4848
public static String CopyProjectProcessor_name;
4949

50+
public static String CopyResourceChange_name;
51+
52+
public static String CopyResourceChange_undo_composite_name;
53+
54+
public static String CopyResourcesDescriptor_error_resource_not_exists;
55+
56+
public static String CopyResourcesProcessor_create_task;
57+
58+
public static String CopyResourcesProcessor_description_multiple;
59+
60+
public static String CopyResourcesProcessor_description_single;
61+
62+
public static String CopyResourcesProcessor_destination_inside_moved;
63+
64+
public static String CopyResourcesProcessor_error_multiple_destinatinos;
65+
66+
public static String CopyResourcesProcessor_name;
67+
5068
public static String CreateChangeOperation_unknown_Refactoring;
5169

5270
public static String DefaultRefactoringDescriptor_cannot_create_refactoring;

bundles/org.eclipse.ltk.core.refactoring/src/org/eclipse/ltk/internal/core/refactoring/RefactoringCoreMessages.properties

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,3 +173,13 @@ MoveResourceProcessor_destination_same_as_moved=The destination contains a resou
173173
MoveResourceProcessor_error_destination_not_exists=Destination does not exist
174174
MoveResourceProcessor_error_invalid_destination=Invalid parent
175175
MoveResourceProcessor_processor_name=Move Resources
176+
177+
CopyResourceChange_name=Copy resource ''{0}'' to ''{1}''
178+
CopyResourceChange_undo_composite_name=Delete copy and restore overwritten resource.
179+
CopyResourcesDescriptor_error_resource_not_exists=The resource ''{0}'' to copy does not exist.
180+
CopyResourcesProcessor_create_task=Creating pending copies...
181+
CopyResourcesProcessor_description_multiple=Copy {0} resources to ''{1}''
182+
CopyResourcesProcessor_description_single=Copy ''{0}'' to ''{1}''
183+
CopyResourcesProcessor_destination_inside_moved=Destination is inside copied resource ''{0}''
184+
CopyResourcesProcessor_error_multiple_destinatinos=Can only copy to a single destination.
185+
CopyResourcesProcessor_name=Copy Resources

0 commit comments

Comments
 (0)