Bug 508260 - A lot of unnecessary file I/O when deleting a linked folder
pointing to a deleted file system directory.
Change-Id: I2ba4b13274e97ad26a7bc92d7c0301f8dbecd924
Signed-off-by: Sergey Prigogin <eclipse.sprigogin@gmail.com>
diff --git a/org.eclipse.ltk.core.refactoring.tests/src/org/eclipse/ltk/core/refactoring/tests/resource/ResourceRefactoringUndoTests.java b/org.eclipse.ltk.core.refactoring.tests/src/org/eclipse/ltk/core/refactoring/tests/resource/ResourceRefactoringUndoTests.java
index 47c3e07..736f357 100644
--- a/org.eclipse.ltk.core.refactoring.tests/src/org/eclipse/ltk/core/refactoring/tests/resource/ResourceRefactoringUndoTests.java
+++ b/org.eclipse.ltk.core.refactoring.tests/src/org/eclipse/ltk/core/refactoring/tests/resource/ResourceRefactoringUndoTests.java
@@ -4,17 +4,19 @@
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
- *
+ *
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.ltk.core.refactoring.tests.resource;
import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URI;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
@@ -63,7 +65,6 @@
import junit.framework.TestSuite;
public class ResourceRefactoringUndoTests extends TestCase {
-
private static final String TEST_NEWPROJECT_NAME= "projectTestNew";
private static final String TEST_FOLDER_NAME= "test";
private static final String TEST_NEWFOLDER_NAME= "testNew";
@@ -73,6 +74,7 @@
private static final String TEST_LINKEDFILE_NAME= "linkedFile.txt";
private static final String TEST_SUBFOLDER_NAME= "subFolder";
private static List<String> fileNameExcludes= new ArrayList<>();
+
static {
fileNameExcludes.add(".project");
}
@@ -190,7 +192,7 @@
desc.setResourcePath(fProject.getProject().getFullPath());
desc.setNewName(TEST_NEWPROJECT_NAME);
PerformRefactoringOperation op= new PerformRefactoringOperation(desc.createRefactoringContext(new RefactoringStatus()), CheckConditionsOperation.ALL_CONDITIONS);
-
+
ProjectSnapshot snap= new ProjectSnapshot(fProject.getProject());
execute(op);
renamedProject= getWorkspaceRoot().getProject(TEST_NEWPROJECT_NAME);
@@ -283,6 +285,36 @@
assertFalse("Redo delete failed", testLinkedFolder.exists());
}
+ public void testFolderDeleteLinkedDeletedOnFilesystemUndoRedoLTK() throws ExecutionException, CoreException {
+ RefactoringContribution deleteContribution= RefactoringCore.getRefactoringContribution(DeleteResourcesDescriptor.ID);
+ DeleteResourcesDescriptor desc= (DeleteResourcesDescriptor) deleteContribution.createDescriptor();
+ desc.setResourcePaths(new IPath[] { testLinkedFolder.getFullPath() });
+
+ PerformRefactoringOperation op= new PerformRefactoringOperation(
+ desc.createRefactoringContext(new RefactoringStatus()),
+ CheckConditionsOperation.ALL_CONDITIONS);
+
+ FolderSnapshot snap= new FolderSnapshot(testLinkedFolder);
+
+ // Create a subfolder containing a file under the linked folder.
+ IFolder subfolder= testLinkedFolder.getFolder("A");
+ subfolder.create(true, true, getMonitor());
+ IFile file= subfolder.getFile("test.txt");
+ file.create(new ByteArrayInputStream("test contents".getBytes(StandardCharsets.UTF_8)), true, getMonitor());
+ // Delete the target of the linked folder on the file system making the linked folder out of sync
+ // with the file system.
+ IFileStore folderStore= EFS.getStore(testLinkedFolder.getLocationURI());
+ folderStore.delete(EFS.NONE, getMonitor()); // Delete the target folder on the file system.
+
+ execute(op);
+ assertFalse("Folder delete failed", testLinkedFolder.exists());
+ undo();
+ assertTrue("Folder recreation failed", testLinkedFolder.exists());
+ assertTrue("Folder CONTENT was altered on undo", snap.isValid(testLinkedFolder.getParent()));
+ redo();
+ assertFalse("Redo delete failed", testLinkedFolder.exists());
+ }
+
public void testProjectDeleteUndoRedoLTK() throws ExecutionException, CoreException {
RefactoringContribution renameContribution= RefactoringCore.getRefactoringContribution(DeleteResourcesDescriptor.ID);
DeleteResourcesDescriptor desc= (DeleteResourcesDescriptor) renameContribution.createDescriptor();
@@ -430,10 +462,10 @@
}
/**
- * Returns a FileStore instance backed by storage in a temporary location.
- * The returned store will not exist, but will belong to an existing parent.
- * The tearDown method in this class will ensure the location is deleted
- * after the test is completed.
+ * Returns a FileStore instance backed by storage in a temporary location. The returned store
+ * will not exist, but will belong to an existing parent. The tearDown method in this class will
+ * ensure the location is deleted after the test is completed.
+ *
* @return The temp filestore to use
*/
private IFileStore getTempStore() {
diff --git a/org.eclipse.ltk.core.refactoring/src/org/eclipse/ltk/internal/core/refactoring/resource/undostates/AbstractResourceUndoState.java b/org.eclipse.ltk.core.refactoring/src/org/eclipse/ltk/internal/core/refactoring/resource/undostates/AbstractResourceUndoState.java
index 1face25..20ab87e 100644
--- a/org.eclipse.ltk.core.refactoring/src/org/eclipse/ltk/internal/core/refactoring/resource/undostates/AbstractResourceUndoState.java
+++ b/org.eclipse.ltk.core.refactoring/src/org/eclipse/ltk/internal/core/refactoring/resource/undostates/AbstractResourceUndoState.java
@@ -57,9 +57,13 @@
protected AbstractResourceUndoState(IResource resource) {
parent= resource.getParent();
if (resource.isAccessible()) {
- modificationStamp= resource.getModificationStamp();
- localTimeStamp= resource.getLocalTimeStamp();
resourceAttributes= resource.getResourceAttributes();
+ // If resourceAttruibutes is null, the resource doesn't exist on disk, so leave
+ // modificationStamp and localTimeStamp set to IResource.NULL_STAMP.
+ if (resourceAttributes != null) {
+ modificationStamp= resource.getModificationStamp();
+ localTimeStamp= resource.getLocalTimeStamp();
+ }
try {
IMarker[] markers= resource.findMarkers(null, true, IResource.DEPTH_INFINITE);
markerDescriptions= new MarkerUndoState[markers.length];
diff --git a/org.eclipse.ltk.core.refactoring/src/org/eclipse/ltk/internal/core/refactoring/resource/undostates/ContainerUndoState.java b/org.eclipse.ltk.core.refactoring/src/org/eclipse/ltk/internal/core/refactoring/resource/undostates/ContainerUndoState.java
index 42fa0b0..b8e1ea0 100644
--- a/org.eclipse.ltk.core.refactoring/src/org/eclipse/ltk/internal/core/refactoring/resource/undostates/ContainerUndoState.java
+++ b/org.eclipse.ltk.core.refactoring/src/org/eclipse/ltk/internal/core/refactoring/resource/undostates/ContainerUndoState.java
@@ -12,6 +12,8 @@
package org.eclipse.ltk.internal.core.refactoring.resource.undostates;
import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
@@ -44,7 +46,7 @@
protected URI location;
private String defaultCharSet;
- private AbstractResourceUndoState[] members;
+ private List<AbstractResourceUndoState> members;
/**
* Create a container description from the specified container handle that
@@ -132,10 +134,13 @@
if (container.isAccessible()) {
defaultCharSet = container.getDefaultCharset(false);
IResource[] resourceMembers = container.members();
- members = new AbstractResourceUndoState[resourceMembers.length];
- for (int i = 0; i < resourceMembers.length; i++) {
- members[i] = (AbstractResourceUndoState) ResourceUndoState
- .fromResource(resourceMembers[i]);
+ members = new ArrayList<>(resourceMembers.length);
+ for (IResource resourceMember : resourceMembers) {
+ // Add a member only if its container exists on disk or if the member is a linked resource.
+ // Otherwise avoid wasting time. See http://bugs.eclipse.org/508260
+ if (localTimeStamp != IResource.NULL_STAMP || resourceMember.isLinked()) {
+ members.add((AbstractResourceUndoState) ResourceUndoState.fromResource(resourceMember));
+ }
}
}
} catch (CoreException e) {
@@ -161,11 +166,10 @@
IProgressMonitor monitor, int ticks) throws CoreException {
// restore any children
- if (members != null && members.length > 0) {
- for (int i = 0; i < members.length; i++) {
- members[i].parent = parentHandle;
- members[i].createResource(new SubProgressMonitor(monitor, ticks
- / members.length));
+ if (members != null) {
+ for (AbstractResourceUndoState member : members) {
+ member.parent = parentHandle;
+ member.createResource(new SubProgressMonitor(monitor, ticks / members.size()));
}
}
}
@@ -176,21 +180,17 @@
monitor.beginTask(
RefactoringCoreMessages.FolderDescription_SavingUndoInfoProgress, 100);
if (members != null) {
- for (int i = 0; i < members.length; i++) {
- if (members[i] instanceof FileUndoState) {
- IPath path = resource.getFullPath().append(
- ((FileUndoState) members[i]).name);
- IFile fileHandle = resource.getWorkspace().getRoot().getFile(
- path);
- members[i].recordStateFromHistory(fileHandle,
- new SubProgressMonitor(monitor, 100 / members.length));
- } else if (members[i] instanceof FolderUndoState) {
- IPath path = resource.getFullPath().append(
- ((FolderUndoState) members[i]).name);
- IFolder folderHandle = resource.getWorkspace().getRoot()
- .getFolder(path);
- members[i].recordStateFromHistory(folderHandle,
- new SubProgressMonitor(monitor, 100 / members.length));
+ for (AbstractResourceUndoState member : members) {
+ if (member instanceof FileUndoState) {
+ IPath path = resource.getFullPath().append(((FileUndoState) member).name);
+ IFile fileHandle = resource.getWorkspace().getRoot().getFile(path);
+ member.recordStateFromHistory(fileHandle,
+ new SubProgressMonitor(monitor, 100 / members.size()));
+ } else if (member instanceof FolderUndoState) {
+ IPath path = resource.getFullPath().append(((FolderUndoState) member).name);
+ IFolder folderHandle = resource.getWorkspace().getRoot().getFolder(path);
+ member.recordStateFromHistory(folderHandle,
+ new SubProgressMonitor(monitor, 100 / members.size()));
}
}
}
@@ -215,13 +215,13 @@
*/
public ContainerUndoState getFirstLeafFolder() {
// If there are no members, this is a leaf
- if (members == null || members.length == 0) {
+ if (members == null || members.isEmpty()) {
return this;
}
// Traverse the members and find the first potential leaf
- for (int i = 0; i < members.length; i++) {
- if (members[i] instanceof ContainerUndoState) {
- return ((ContainerUndoState) members[i]).getFirstLeafFolder();
+ for (AbstractResourceUndoState member : members) {
+ if (member instanceof ContainerUndoState) {
+ return ((ContainerUndoState) member).getFirstLeafFolder();
}
}
// No child folders were found, this is a leaf
@@ -238,13 +238,9 @@
*/
public void addMember(AbstractResourceUndoState member) {
if (members == null) {
- members = new AbstractResourceUndoState[] { member };
- } else {
- AbstractResourceUndoState[] expandedMembers = new AbstractResourceUndoState[members.length + 1];
- System.arraycopy(members, 0, expandedMembers, 0, members.length);
- expandedMembers[members.length] = member;
- members = expandedMembers;
+ members = new ArrayList<>();
}
+ members.add(member);
}
@Override
@@ -274,9 +270,9 @@
if (existence) {
if (checkMembers) {
// restore any children
- if (members != null && members.length > 0) {
- for (int i = 0; i < members.length; i++) {
- if (!members[i].verifyExistence(checkMembers)) {
+ if (members != null) {
+ for (AbstractResourceUndoState member : members) {
+ if (!member.verifyExistence(checkMembers)) {
return false;
}
}