diff --git a/org.eclipse.ltk.core.refactoring.tests/src/org/eclipse/ltk/core/refactoring/tests/resource/ResourceRefactoringTests.java b/org.eclipse.ltk.core.refactoring.tests/src/org/eclipse/ltk/core/refactoring/tests/resource/ResourceRefactoringTests.java index c71b6f9..07f3a3e 100644 --- a/org.eclipse.ltk.core.refactoring.tests/src/org/eclipse/ltk/core/refactoring/tests/resource/ResourceRefactoringTests.java +++ b/org.eclipse.ltk.core.refactoring.tests/src/org/eclipse/ltk/core/refactoring/tests/resource/ResourceRefactoringTests.java @@ -11,7 +11,9 @@ *******************************************************************************/ package org.eclipse.ltk.core.refactoring.tests.resource; +import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.InputStream; import junit.framework.Test; import junit.framework.TestCase; @@ -41,6 +43,7 @@ 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.resource.CreateFileChange; import org.eclipse.ltk.core.refactoring.resource.DeleteResourcesDescriptor; import org.eclipse.ltk.core.refactoring.resource.MoveResourceChange; import org.eclipse.ltk.core.refactoring.resource.MoveResourcesDescriptor; @@ -95,7 +98,7 @@ Change undoChange= perform(new MoveResourceChange(testFolder, destination)); - IFolder movedResource= (IFolder) assertMove(testFolder, destination, null); + IFolder movedResource= (IFolder)assertMove(testFolder, destination, null); assertTrue(movedResource.getFile("myFile.txt").exists()); perform(undoChange); @@ -137,7 +140,7 @@ IFolder destination= fProject.createFolder("dest"); RefactoringContribution contribution= RefactoringCore.getRefactoringContribution(MoveResourcesDescriptor.ID); - MoveResourcesDescriptor descriptor= (MoveResourcesDescriptor) contribution.createDescriptor(); + MoveResourcesDescriptor descriptor= (MoveResourcesDescriptor)contribution.createDescriptor(); descriptor.setResourcesToMove(new IResource[] { file }); descriptor.setDestination(destination); @@ -161,14 +164,14 @@ IFolder destination= fProject.createFolder("dest"); RefactoringContribution contribution= RefactoringCore.getRefactoringContribution(MoveResourcesDescriptor.ID); - MoveResourcesDescriptor descriptor= (MoveResourcesDescriptor) contribution.createDescriptor(); + MoveResourcesDescriptor descriptor= (MoveResourcesDescriptor)contribution.createDescriptor(); descriptor.setResourcesToMove(new IResource[] { testFolder }); descriptor.setDestination(destination); Change undoChange= perform(descriptor); - IFolder movedResource= (IFolder) assertMove(testFolder, destination, null); + IFolder movedResource= (IFolder)assertMove(testFolder, destination, null); assertTrue(movedResource.getFile("myFile.txt").exists()); perform(undoChange); @@ -190,7 +193,7 @@ IFile file2= fProject.createFile(destination, "myFile.txt", content2); RefactoringContribution contribution= RefactoringCore.getRefactoringContribution(MoveResourcesDescriptor.ID); - MoveResourcesDescriptor descriptor= (MoveResourcesDescriptor) contribution.createDescriptor(); + MoveResourcesDescriptor descriptor= (MoveResourcesDescriptor)contribution.createDescriptor(); descriptor.setResourcesToMove(new IResource[] { file1 }); descriptor.setDestination(destination); @@ -208,7 +211,7 @@ public void testDeleteRefactoring1_bug343584() throws Exception { IFolder testFolder= fProject.createFolder("test"); fProject.createFile(testFolder, "myFile.txt", "hello"); - + IProject testProject2= ResourcesPlugin.getWorkspace().getRoot().getProject(SimpleTestProject.TEST_PROJECT_NAME + "2"); try { testProject2.create(null); @@ -233,7 +236,7 @@ IPath location= fProject.getProject().getLocation(); IFolder testFolder= fProject.createFolder("test"); fProject.createFile(testFolder, "myFile.txt", "hello"); - + IWorkspace workspace= ResourcesPlugin.getWorkspace(); String p2Name= "p2"; IProjectDescription p2Description= workspace.newProjectDescription(p2Name); @@ -242,28 +245,28 @@ p2.create(p2Description, null); p2.open(null); IPath p2Location= p2.getLocation(); - + RefactoringContribution contribution= RefactoringCore.getRefactoringContribution(DeleteResourcesDescriptor.ID); - DeleteResourcesDescriptor descriptor= (DeleteResourcesDescriptor) contribution.createDescriptor(); - + DeleteResourcesDescriptor descriptor= (DeleteResourcesDescriptor)contribution.createDescriptor(); + descriptor.setDeleteContents(true); descriptor.setResources(new IResource[] { fProject.getProject(), p2 }); - + perform(descriptor); - + assertFalse(fProject.getProject().exists()); assertFalse(p2.exists()); - + assertFalse(location.toFile().exists()); assertFalse(p2Location.toFile().exists()); } - + public void testDeleteRefactoring3_bug343584() throws Exception { IPath location= fProject.getProject().getLocation(); IFolder testFolder= fProject.createFolder("test"); IFile file= fProject.createFile(testFolder, "myFile.txt", "hello"); IPath fileLocation= file.getLocation(); - + IWorkspace workspace= ResourcesPlugin.getWorkspace(); String p2Name= "p2"; IProjectDescription p2Description= workspace.newProjectDescription(p2Name); @@ -272,29 +275,127 @@ p2.create(p2Description, null); p2.open(null); IPath p2Location= p2.getLocation(); - + try { RefactoringContribution contribution= RefactoringCore.getRefactoringContribution(DeleteResourcesDescriptor.ID); - DeleteResourcesDescriptor descriptor= (DeleteResourcesDescriptor) contribution.createDescriptor(); - + DeleteResourcesDescriptor descriptor= (DeleteResourcesDescriptor)contribution.createDescriptor(); + descriptor.setDeleteContents(false); descriptor.setResources(new IResource[] { fProject.getProject(), p2 }); - + perform(descriptor); - + assertFalse(fProject.getProject().exists()); assertFalse(p2.exists()); - + assertTrue(location.toFile().exists()); assertTrue(fileLocation.toFile().exists()); assertTrue(p2Location.toFile().exists()); - + } finally { EFS.getLocalFileSystem().getStore(location).delete(EFS.NONE, null); EFS.getLocalFileSystem().getStore(p2Location).delete(EFS.NONE, null); } } - + + /* + * Creates a file in the project's root. + */ + public void testCreateFileChange1() throws Exception { + String content= "hello"; + + IFile file= fProject.getProject().getFile("myFile.txt"); + InputStream inputStream= new ByteArrayInputStream(content.getBytes()); + + Change undoChange= perform(new CreateFileChange(file, inputStream)); + + assertTrue(file.exists()); + assertEquals(content, fProject.getContent(file)); + + perform(undoChange); + + assertFalse(file.exists()); + } + + /* + * Updates an existing file in the project's root. + */ + public void testCreateFileChange2() throws Exception { + String oldContent= "hi"; + String newContent= "hello"; + + IFile file= fProject.createFile(fProject.getProject(), "myFile.txt", oldContent); + InputStream inputStream= new ByteArrayInputStream(newContent.getBytes()); + + Change undoChange= perform(new CreateFileChange(file, inputStream)); + + assertTrue(file.exists()); + assertEquals(newContent, fProject.getContent(file)); + + perform(undoChange); + + assertTrue(file.exists()); + assertEquals(oldContent, fProject.getContent(file)); + } + + /* + * Updates an existing file in the project's root, but the overwrite flag + * is set to false. + */ + public void testCreateFileChange3() throws Exception { + String oldContent= "hi"; + String newContent= "hello"; + + IFile file= fProject.createFile(fProject.getProject(), "myFile.txt", oldContent); + InputStream inputStream= new ByteArrayInputStream(newContent.getBytes()); + + Change change= new CreateFileChange(file, inputStream, false); + assertTrue(change.isValid(null).hasFatalError()); + } + + /* + * Creates a file in a deep folder structure where some folders do not + * exist yet. + */ + public void testCreateFileChange4() throws Exception { + String content= "hello"; + + IFolder existingFolder= fProject.createFolder("test"); + IFolder newFolder1= existingFolder.getFolder("foo"); + IFolder newFolder2= newFolder1.getFolder("bar"); + IFile file= newFolder2.getFile("myFile.txt"); + InputStream inputStream= new ByteArrayInputStream(content.getBytes()); + + Change undoChange= perform(new CreateFileChange(file, inputStream)); + + assertTrue(existingFolder.exists()); + assertTrue(newFolder1.exists()); + assertTrue(newFolder2.exists()); + assertTrue(file.exists()); + assertEquals(content, fProject.getContent(file)); + + perform(undoChange); + + assertFalse(file.exists()); + assertFalse(newFolder2.exists()); + assertFalse(newFolder1.exists()); + assertTrue(existingFolder.exists()); + } + + /* + * Creates a file in a project that does not exist. + */ + public void testCreateFileChange5() throws Exception { + String content= "hello"; + + IProject project= ResourcesPlugin.getWorkspace().getRoot().getProject("foo"); + IFile file= project.getFile("myFile.txt"); + InputStream inputStream= new ByteArrayInputStream(content.getBytes()); + + Change change= new CreateFileChange(file, inputStream, false); + assertTrue(change.isValid(null).hasFatalError()); + } + private Change perform(Change change) throws CoreException { PerformChangeOperation op= new PerformChangeOperation(change); op.run(null); @@ -327,10 +428,9 @@ assertTrue(res.getType() == source.getType()); if (res instanceof IFile) { - assertTrue(content.equals(fProject.getContent((IFile) res))); + assertTrue(content.equals(fProject.getContent((IFile)res))); } return res; } - } diff --git a/org.eclipse.ltk.core.refactoring/src/org/eclipse/ltk/core/refactoring/resource/CreateFileChange.java b/org.eclipse.ltk.core.refactoring/src/org/eclipse/ltk/core/refactoring/resource/CreateFileChange.java new file mode 100644 index 0000000..69d9204 --- /dev/null +++ b/org.eclipse.ltk.core.refactoring/src/org/eclipse/ltk/core/refactoring/resource/CreateFileChange.java @@ -0,0 +1,207 @@ +/******************************************************************************* + * Copyright (c) 2013 Zend Technologies Ltd and others. + * All rights reserved. This program and the accompanying materials + * 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: + * Zend Technologies Ltd - initial API and implementation + *******************************************************************************/ +package org.eclipse.ltk.core.refactoring.resource; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; + +import org.eclipse.core.filesystem.EFS; +import org.eclipse.core.filesystem.IFileInfo; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.SubProgressMonitor; + +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.CompositeChange; +import org.eclipse.ltk.core.refactoring.RefactoringCore; +import org.eclipse.ltk.core.refactoring.RefactoringStatus; +import org.eclipse.ltk.internal.core.refactoring.BasicElementLabels; +import org.eclipse.ltk.internal.core.refactoring.Messages; +import org.eclipse.ltk.internal.core.refactoring.RefactoringCoreMessages; + +/** + * {@link Change} that creates a file. + * + * @since 3.7 + */ +public class CreateFileChange extends ResourceChange { + + private IFile fFile; + + private InputStream fInputStream; + + private boolean fOverwrite; + + /** + * Create new file. + * + *

+ * The file's project must exist in the workspace. Otherwise a fatal error will be returned by + * the {@link #isValid(IProgressMonitor)} method. + *

+ * + *

+ * If there is an existing file on the specified path then it will be replaced with the new + * file. Calling this constructor is the same as calling + * CreateFileChange(path, inputStream, true). + *

+ * + * @param file handle to for the new file + * @param inputStream content for the new file + */ + public CreateFileChange(IFile file, InputStream inputStream) { + this(file, inputStream, true); + } + + /** + * Create new file. + * + *

+ * The file's project must exist in the workspace. Otherwise a fatal error will be returned by + * the {@link #isValid(IProgressMonitor)} method. + *

+ * + * @param file handle to for the new file + * @param inputStream content for the new file + * @param overwrite whether an existing file on the specified path should be replaced, or the + * change should fail + */ + + public CreateFileChange(IFile file, InputStream inputStream, boolean overwrite) { + Assert.isNotNull(file, "file"); //$NON-NLS-1$ + Assert.isNotNull(inputStream, "inputStream"); //$NON-NLS-1$ + fFile= file; + fInputStream= inputStream; + fOverwrite= overwrite; + } + + /* (non-Javadoc) + * @see org.eclipse.ltk.core.refactoring.resource.ResourceChange#getModifiedResource() + */ + protected IResource getModifiedResource() { + return fFile; + } + + /* (non-Javadoc) + * @see org.eclipse.ltk.core.refactoring.Change#getName() + */ + public String getName() { + String message= (fFile.exists()) ? RefactoringCoreMessages.CreateFileChange_update_file + : RefactoringCoreMessages.CreateFileChange_create_file; + return Messages.format(message, BasicElementLabels.getPathLabel(fFile.getFullPath(), false)); + } + + /* + * (non-Javadoc) + * @see org.eclipse.ltk.core.refactoring.resource.ResourceChange#isValid(org.eclipse.core.runtime.IProgressMonitor) + */ + public RefactoringStatus isValid(IProgressMonitor pm) throws CoreException { + RefactoringStatus result= new RefactoringStatus(); + + // among other things, checks that the file's project exists + URI location= fFile.getLocationURI(); + if (location == null) { + result.addFatalError(Messages.format( + RefactoringCoreMessages.CreateFileChange_error_unknownLocation, + BasicElementLabels.getPathLabel(fFile.getFullPath(), false))); + return result; + } + + // checks that if the file already exist then the fOverwrite flag is on too + if (!fOverwrite) { + IFileInfo jFile= EFS.getStore(location).fetchInfo(); + if (jFile.exists()) { + result.addFatalError(Messages.format( + RefactoringCoreMessages.CreateFileChange_error_exists, + BasicElementLabels.getPathLabel(fFile.getFullPath(), false))); + return result; + } + } + + return result; + } + + /* (non-Javadoc) + * @see org.eclipse.ltk.core.refactoring.Change#perform(org.eclipse.core.runtime.IProgressMonitor) + */ + public Change perform(IProgressMonitor pm) throws CoreException { + if (pm == null) + pm= new NullProgressMonitor(); + + try { + pm.beginTask(RefactoringCoreMessages.CreateFileChange_creating_resource, 2); + + if (fFile.exists()) { + // assuming fOverwrite is on, checked in isValid() + CompositeChange composite= new CompositeChange(getName()); + composite.add(new DeleteResourceChange(fFile.getFullPath(), true)); + composite.add(new CreateFileChange(fFile, fInputStream)); + return composite.perform(new SubProgressMonitor(pm, 1)); + } else { + // create the parent folder if does not exist yet + Change undo= createParentFolderIfNeeded(fFile, pm); + + // create the file + fFile.create(fInputStream, false, new SubProgressMonitor(pm, 1)); + + // the undo change is the delete change for the parent folder if one is created + // otherwise - it is the delete change for the created file + return (undo != null) ? undo : + new DeleteResourceChange(fFile.getFullPath(), false); + } + } finally { + try { + fInputStream.close(); + } catch (IOException e) { + throw new CoreException(new Status(IStatus.ERROR, + RefactoringCore.ID_PLUGIN, e.getMessage(), e)); + } finally { + pm.done(); + } + } + } + + private Change createParentFolderIfNeeded(IResource resource, IProgressMonitor pm) throws CoreException { + IContainer parent= resource.getParent(); + Assert.isNotNull(parent); + + if (parent.getType() == IResource.PROJECT) + // the parent is a project - assuming it exists, checked in isValid() + return null; + + IFolder folder= (IFolder)parent; + if (folder.exists()) + // the parent folder exists - nothing to do + return null; + + // check if the parent of the parent also needs to be created + Change undo= createParentFolderIfNeeded(folder, pm); + + // create the parent folder + folder.create(false, true, pm); + + // the undo change is the delete change for the top-most created folder + return (undo != null) ? undo : + new DeleteResourceChange(folder.getFullPath(), false); + } + +} diff --git a/org.eclipse.ltk.core.refactoring/src/org/eclipse/ltk/internal/core/refactoring/RefactoringCoreMessages.java b/org.eclipse.ltk.core.refactoring/src/org/eclipse/ltk/internal/core/refactoring/RefactoringCoreMessages.java index d733c3b..f218ae2 100644 --- a/org.eclipse.ltk.core.refactoring/src/org/eclipse/ltk/internal/core/refactoring/RefactoringCoreMessages.java +++ b/org.eclipse.ltk.core.refactoring/src/org/eclipse/ltk/internal/core/refactoring/RefactoringCoreMessages.java @@ -266,6 +266,16 @@ public static String ValidateEditChecker_failed; + public static String CreateFileChange_creating_resource; + + public static String CreateFileChange_create_file; + + public static String CreateFileChange_update_file; + + public static String CreateFileChange_error_exists; + + public static String CreateFileChange_error_unknownLocation; + static { NLS.initializeMessages(BUNDLE_NAME, RefactoringCoreMessages.class); } diff --git a/org.eclipse.ltk.core.refactoring/src/org/eclipse/ltk/internal/core/refactoring/RefactoringCoreMessages.properties b/org.eclipse.ltk.core.refactoring/src/org/eclipse/ltk/internal/core/refactoring/RefactoringCoreMessages.properties index 7be94b0..48c1357 100644 --- a/org.eclipse.ltk.core.refactoring/src/org/eclipse/ltk/internal/core/refactoring/RefactoringCoreMessages.properties +++ b/org.eclipse.ltk.core.refactoring/src/org/eclipse/ltk/internal/core/refactoring/RefactoringCoreMessages.properties @@ -156,3 +156,9 @@ MoveResourceProcessor_error_destination_not_exists=Destination does not exist MoveResourceProcessor_error_invalid_destination=Invalid parent MoveResourceProcessor_processor_name=Move Resources + +CreateFileChange_creating_resource=Creating file... +CreateFileChange_create_file=Create file ''{0}'' +CreateFileChange_update_file=Update file ''{0}'' +CreateFileChange_error_exists=File ''{0}'' already exists +CreateFileChange_error_unknownLocation=The location for file ''{0}'' is unknown