diff --git a/resources/bundles/org.eclipse.core.resources/META-INF/MANIFEST.MF b/resources/bundles/org.eclipse.core.resources/META-INF/MANIFEST.MF index 998c1b969ba..57bff4adfd3 100644 --- a/resources/bundles/org.eclipse.core.resources/META-INF/MANIFEST.MF +++ b/resources/bundles/org.eclipse.core.resources/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.core.resources; singleton:=true -Bundle-Version: 3.19.200.qualifier +Bundle-Version: 3.20.0.qualifier Bundle-Activator: org.eclipse.core.resources.ResourcesPlugin Bundle-Vendor: %providerName Bundle-Localization: plugin @@ -23,6 +23,7 @@ Export-Package: org.eclipse.core.internal.dtree;x-internal:=true, org.eclipse.core.resources.mapping, org.eclipse.core.resources.refresh, org.eclipse.core.resources.team, + org.eclipse.core.resources.undo.snapshot, org.eclipse.core.resources.variableresolvers Require-Bundle: org.eclipse.ant.core;bundle-version="[3.1.0,4.0.0)";resolution:=optional, org.eclipse.core.expressions;bundle-version="[3.2.0,4.0.0)", diff --git a/resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/undo/snapshot/AbstractResourceSnapshot.java b/resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/undo/snapshot/AbstractResourceSnapshot.java new file mode 100644 index 00000000000..1e6a4f171c1 --- /dev/null +++ b/resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/undo/snapshot/AbstractResourceSnapshot.java @@ -0,0 +1,141 @@ +/******************************************************************************* + * Copyright (c) 2006, 2023 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 + * Red Hat Inc - Adapted from classes in org.eclipse.ui.ide.undo and org.eclipse.ui.internal.ide.undo + *******************************************************************************/ + +package org.eclipse.core.internal.resources.undo.snapshot; + +import org.eclipse.core.resources.IContainer; +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IWorkspace; +import org.eclipse.core.resources.ResourceAttributes; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.resources.undo.snapshot.IResourceSnapshot; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; + +/** + * Base implementation of ResourceSnapshot that describes the common attributes + * of a resource to be created. + * + * This class is not intended to be instantiated or used by clients. + * + * @since 3.20 + * + */ +abstract class AbstractResourceSnapshot implements IResourceSnapshot { + IContainer parent; + + long modificationStamp = IResource.NULL_STAMP; + + long localTimeStamp = IResource.NULL_STAMP; + + ResourceAttributes resourceAttributes; + + MarkerSnapshot[] markerDescriptions; + + /** + * Create a resource snapshot with no initial attributes + */ + protected AbstractResourceSnapshot() { + super(); + } + + /** + * Create a resource snapshot from the specified resource. + * + * @param resource the resource to be described + */ + protected AbstractResourceSnapshot(IResource resource) { + super(); + parent = resource.getParent(); + if (resource.isAccessible()) { + modificationStamp = resource.getModificationStamp(); + localTimeStamp = resource.getLocalTimeStamp(); + resourceAttributes = resource.getResourceAttributes(); + try { + IMarker[] markers = resource.findMarkers(null, true, + IResource.DEPTH_INFINITE); + markerDescriptions = new MarkerSnapshot[markers.length]; + for (int i = 0; i < markers.length; i++) { + markerDescriptions[i] = new MarkerSnapshot(markers[i]); + } + } catch (CoreException e) { + // Eat this exception because it only occurs when the resource + // does not exist and we have already checked this. + // We do not want to throw exceptions on the simple constructor, + // as no one has actually tried to do anything yet. + } + } + } + + @Override + public IResource createResource(IProgressMonitor monitor) + throws CoreException { + IResource resource = createResourceHandle(); + createExistentResourceFromHandle(resource, monitor); + restoreResourceAttributes(resource); + return resource; + } + + @Override + public boolean isValid() { + return parent == null || parent.exists(); + } + + /** + * Restore any saved attributed of the specified resource. This method is + * called after the existent resource represented by the receiver has been + * created. + * + * @param resource + * the newly created resource + * @throws CoreException + */ + protected void restoreResourceAttributes(IResource resource) + throws CoreException { + if (modificationStamp != IResource.NULL_STAMP) { + resource.revertModificationStamp(modificationStamp); + } + if (localTimeStamp != IResource.NULL_STAMP) { + resource.setLocalTimeStamp(localTimeStamp); + } + if (resourceAttributes != null) { + resource.setResourceAttributes(resourceAttributes); + } + if (markerDescriptions != null) { + for (MarkerSnapshot markerDescription : markerDescriptions) { + if (markerDescription.resource.exists()) + markerDescription.createMarker(); + } + } + } + + /* + * Return the workspace. + */ + IWorkspace getWorkspace() { + return ResourcesPlugin.getWorkspace(); + } + + @Override + public boolean verifyExistence(boolean checkMembers) { + IContainer p = parent; + if (p == null) { + p = getWorkspace().getRoot(); + } + IResource handle = p.findMember(getName()); + return handle != null; + } +} diff --git a/resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/undo/snapshot/ContainerSnapshot.java b/resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/undo/snapshot/ContainerSnapshot.java new file mode 100644 index 00000000000..eb0be2a2bd9 --- /dev/null +++ b/resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/undo/snapshot/ContainerSnapshot.java @@ -0,0 +1,289 @@ +/******************************************************************************* + * Copyright (c) 2006, 2023 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 + * Red Hat Inc - Adapted from classes in org.eclipse.ui.ide.undo and org.eclipse.ui.internal.ide.undo + *******************************************************************************/ + +package org.eclipse.core.internal.resources.undo.snapshot; + +import java.net.URI; +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.core.resources.IResourceFilterDescription; +import org.eclipse.core.resources.IWorkspaceRoot; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.resources.undo.snapshot.IContainerSnapshot; +import org.eclipse.core.resources.undo.snapshot.IResourceSnapshot; +import org.eclipse.core.resources.undo.snapshot.ResourceSnapshotFactory; +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.SubMonitor; + +/** + * ContainerDescription is a lightweight description that describes a container + * to be created. + * + * This class is not intended to be instantiated or used by clients. + * + * @since 3.20 + */ +public abstract class ContainerSnapshot extends AbstractResourceSnapshot implements IContainerSnapshot { + + String name; + + URI location; + + IResourceFilterDescription[] filters; + + String defaultCharSet; + + IResourceSnapshot[] members; + + /** + * Create a container snapshot from the specified container handle that can be + * used to create the container. The returned ContainerSnapshot should represent + * any non-existing parents in addition to the specified container. + * + * @param container the handle of the container to be described + * @return a container snapshot describing the container and any non-existing + * parents. + */ + + public static ContainerSnapshot fromContainer(IContainer container) { + return fromContainer(container, false); + } + + /** + * Create a group container snapshot from the specified container handle that + * can be used to create the container. The returned ContainerSnapshot should + * represent any non-existing parents in addition to the specified container. + * + * @param container the handle of the container to be described + * @return a container description snapshot the container and any non-existing + * parents. + */ + + public static ContainerSnapshot fromVirtualFolderContainer(IContainer container) { + return fromContainer(container, true); + } + + public static ContainerSnapshot fromContainer(IContainer container, boolean usingVirtualFolder) { + IPath fullPath = container.getFullPath(); + ContainerSnapshot firstCreatedParent = null; + ContainerSnapshot currentContainerDescription = null; + + // Does the container exist already? If so, then the parent exists and + // we use the normal creation constructor. + IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); + IContainer currentContainer = (IContainer) root.findMember(fullPath); + if (currentContainer != null) { + return (ContainerSnapshot) ResourceSnapshotFactory + .fromResource(container); + } + + // Create container descriptions for any uncreated parents in the given + // path. + currentContainer = root; + for (int i = 0; i < fullPath.segmentCount(); i++) { + String currentSegment = fullPath.segment(i); + IResource resource = currentContainer.findMember(currentSegment); + if (resource != null) { + // parent already exists, no need to create a description for it + currentContainer = (IContainer) resource; + } else if (i == 0) { + // parent does not exist and it is a project + firstCreatedParent = new ProjectSnapshot(root + .getProject(currentSegment)); + currentContainerDescription = firstCreatedParent; + } else { + IFolder folderHandle = currentContainer.getFolder(IPath.fromOSString(currentSegment)); + ContainerSnapshot currentFolder; + currentFolder = new FolderSnapshot(folderHandle, usingVirtualFolder); + currentContainer = folderHandle; + if (currentContainerDescription != null) { + currentContainerDescription.addMember(currentFolder); + } + currentContainerDescription = currentFolder; + if (firstCreatedParent == null) { + firstCreatedParent = currentFolder; + } + } + } + return firstCreatedParent; + } + + /** + * Create a ContainerDescription with no state. + */ + public ContainerSnapshot() { + + } + + /** + * Create a ContainerSnapshot from the specified container handle. Typically + * used when the container handle represents a resource that actually exists, + * although it will not fail if the resource is non-existent. + * + * @param container the container to be described + */ + public ContainerSnapshot(IContainer container) { + super(container); + this.name = container.getName(); + if (container.isLinked()) { + this.location = container.getLocationURI(); + } + try { + if (container.isAccessible()) { + defaultCharSet = container.getDefaultCharset(false); + IResource[] resourceMembers = container.members(); + members = new AbstractResourceSnapshot[resourceMembers.length]; + for (int i = 0; i < resourceMembers.length; i++) { + members[i] = (AbstractResourceSnapshot) ResourceSnapshotFactory + .fromResource(resourceMembers[i]); + } + } + } catch (CoreException e) { + // Eat this exception because it only occurs when the resource + // does not exist and we have already checked this. + // We do not want to throw exceptions on the simple constructor, as + // no one has actually tried to do anything yet. + } + } + + /** + * Create any child resources known by this container snapshot. + * + * @param parentHandle the handle of the created parent + * @param monitor the progress monitor to be used + * @throws CoreException + */ + protected final void createChildResources(IContainer parentHandle, + IProgressMonitor monitor) throws CoreException { + // restore any children + if (members != null && members.length > 0) { + SubMonitor subMonitor = SubMonitor.convert(monitor, members.length); + for (IResourceSnapshot member : members) { + if (member instanceof AbstractResourceSnapshot) + ((AbstractResourceSnapshot) member).parent = parentHandle; + member.createResource(subMonitor.split(1)); + } + } + } + + @Override + public void recordStateFromHistory(IResource resource, IProgressMonitor mon) throws CoreException { + if (members != null) { + SubMonitor subMonitor = SubMonitor.convert(mon, ResourceSnapshotMessages.FolderDescription_SavingUndoInfoProgress, + members.length); + for (IResourceSnapshot member : members) { + SubMonitor iterationMonitor = subMonitor.split(1); + if (member instanceof FileSnapshot) { + IPath path = resource.getFullPath().append(((FileSnapshot) member).name); + IFile fileHandle = resource.getWorkspace().getRoot().getFile(path); + member.recordStateFromHistory(fileHandle, iterationMonitor); + } else if (member instanceof FolderSnapshot) { + IPath path = resource.getFullPath().append(((FolderSnapshot) member).name); + IFolder folderHandle = resource.getWorkspace().getRoot().getFolder(path); + member.recordStateFromHistory(folderHandle, iterationMonitor); + } + } + } + } + + /** + * Return the name of the container described by this ContainerSnapshot. + * + * @return the name of the container. + */ + @Override + public String getName() { + return name; + } + + @Override + public IResourceSnapshot[] getMembers() { + AbstractResourceSnapshot[] clone = new AbstractResourceSnapshot[members.length]; + System.arraycopy(members, 0, clone, 0, members.length); + return clone; + } + + /** + * Add the specified resource snapshot as a member of this resource description + * + * @param member the resource snapshot considered a member of this container. + */ + @Override + public void addMember(IResourceSnapshot member) { + if (members == null) { + members = new IResourceSnapshot[] { member }; + } else { + IResourceSnapshot[] expandedMembers = new AbstractResourceSnapshot[members.length + 1]; + System.arraycopy(members, 0, expandedMembers, 0, members.length); + expandedMembers[members.length] = member; + members = expandedMembers; + } + } + + @Override + protected void restoreResourceAttributes(IResource resource) + throws CoreException { + super.restoreResourceAttributes(resource); + Assert.isLegal(resource instanceof IContainer); + IContainer container = (IContainer) resource; + if (defaultCharSet != null) { + container.setDefaultCharset(defaultCharSet, null); + } + } + + /** + * Set the location to which this container is linked. + * + * @param location the location URI, or null if there is no link + */ + @Override + public void setLocation(URI location) { + this.location = location; + } + + /** + * Set the filters to which should be created on this container. + * + * @param filters the filters + */ + @Override + public void setFilters(IResourceFilterDescription[] filters) { + this.filters = filters; + } + + @Override + public boolean verifyExistence(boolean checkMembers) { + boolean existence = super.verifyExistence(checkMembers); + if (existence) { + if (checkMembers) { + // restore any children + if (members != null && members.length > 0) { + for (IResourceSnapshot member : members) { + if (!member.verifyExistence(checkMembers)) { + return false; + } + } + } + } + return true; + } + return false; + } +} diff --git a/resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/undo/snapshot/FileSnapshot.java b/resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/undo/snapshot/FileSnapshot.java new file mode 100644 index 00000000000..4e60c7b65e8 --- /dev/null +++ b/resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/undo/snapshot/FileSnapshot.java @@ -0,0 +1,210 @@ +/******************************************************************************* + * Copyright (c) 2006, 2023 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 + * Red Hat Inc - Adapted from classes in org.eclipse.ui.ide.undo and org.eclipse.ui.internal.ide.undo + *******************************************************************************/ + +package org.eclipse.core.internal.resources.undo.snapshot; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.net.URI; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IFileState; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IResourceStatus; +import org.eclipse.core.resources.IWorkspaceRoot; +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.SubMonitor; + + +/** + * FileSnapshot is a lightweight description that describes a file to be + * created. + * + * This class is not intended to be instantiated or used by clients. + * + * @since 3.20 + * + */ +public class FileSnapshot extends AbstractResourceSnapshot { + + String name; + + URI location; + + String charset; + + private IFileContentSnapshot fileContentDescription; + + /** + * Create a FileSnapshot that can be used to later restore the given file. The + * file typically already exists, but this constructor will not fail if the file + * does not exist. + * + * @param file the file to be restored. + */ + public FileSnapshot(IFile file) { + super(file); + this.name = file.getName(); + try { + this.charset = file.getCharset(false); + } catch (CoreException e) { + // we don't care, a null charset is fine. + } + if (file.isLinked()) { + location = file.getLocationURI(); + } + + } + + /** + * Create a file snapshot from the specified file handle. The handle does not + * exist, so no information should be derived from it. If a location path is + * specified, this file should represent a link to another location. The content + * description describes any state that should be used when the file resource is + * created. + * + * @param file the file to be described + * @param linkLocation the location of the file's link, or + * null if the file is not linked + * @param fileContentDescription the file content description that can be used + * to get information about the file, such as its + * initial content + */ + public FileSnapshot(IFile file, URI linkLocation, + IFileContentSnapshot fileContentDescription) { + super(file); + this.name = file.getName(); + this.location = linkLocation; + this.charset = null; + this.fileContentDescription = fileContentDescription; + } + + @Override + public void recordStateFromHistory(IResource resource, + IProgressMonitor monitor) throws CoreException { + Assert.isLegal(resource.getType() == IResource.FILE); + + if (location != null) { + // file is linked, no need to record any history + return; + } + IFileState[] states = ((IFile) resource).getHistory(monitor); + if (states.length > 0) { + final IFileState state = getMatchingFileState(states); + this.fileContentDescription = new IFileContentSnapshot() { + @Override + public boolean exists() { + return state.exists(); + } + + @Override + public InputStream getContents() throws CoreException { + return state.getContents(); + } + + @Override + public String getCharset() throws CoreException { + return state.getCharset(); + } + }; + } + } + + @Override + public IResource createResourceHandle() { + IWorkspaceRoot workspaceRoot = parent.getWorkspace().getRoot(); + IPath fullPath = parent.getFullPath().append(name); + return workspaceRoot.getFile(fullPath); + } + + @Override + public void createExistentResourceFromHandle(IResource resource, IProgressMonitor mon) throws CoreException { + + Assert.isLegal(resource instanceof IFile); + if (resource.exists()) { + return; + } + IFile fileHandle = (IFile) resource; + SubMonitor subMonitor = SubMonitor.convert(mon, 200); + subMonitor.setTaskName(ResourceSnapshotMessages.FileDescription_NewFileProgress); + try { + if (location != null) { + fileHandle.createLink(location, IResource.ALLOW_MISSING_LOCAL, subMonitor.split(200)); + } else { + InputStream contents = new ByteArrayInputStream( + ResourceSnapshotMessages.FileDescription_ContentsCouldNotBeRestored + .getBytes()); + // Retrieve the contents from the file content + // description. Other file state attributes, such as timestamps, + // have already been retrieved from the original IResource + // object and are restored in #restoreResourceAttributes + if (fileContentDescription != null + && fileContentDescription.exists()) { + contents = fileContentDescription.getContents(); + } + fileHandle.create(contents, false, subMonitor.split(100)); + fileHandle.setCharset(charset, subMonitor.split(100)); + } + } catch (CoreException e) { + if (e.getStatus().getCode() == IResourceStatus.PATH_OCCUPIED) { + fileHandle.refreshLocal(IResource.DEPTH_ZERO, null); + } else { + throw e; + } + } + } + + @Override + public boolean isValid() { + if (location != null) { + return super.isValid(); + } + return super.isValid() && fileContentDescription != null + && fileContentDescription.exists(); + } + + @Override + public String getName() { + return name; + } + + /* + * Get the file state that matches this file snapshot. The local time stamp is + * used to try to find a matching file state. If none can be found, the most + * recent copy of the file state is used. + */ + private IFileState getMatchingFileState(IFileState[] states) { + for (IFileState state : states) { + if (localTimeStamp == state.getModificationTime()) { + return state; + } + } + return states[0]; + + } + + @Override + protected void restoreResourceAttributes(IResource resource) + throws CoreException { + super.restoreResourceAttributes(resource); + Assert.isLegal(resource instanceof IFile); + IFile file = (IFile) resource; + if (charset != null) { + file.setCharset(charset, null); + } + } +} diff --git a/resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/undo/snapshot/FolderSnapshot.java b/resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/undo/snapshot/FolderSnapshot.java new file mode 100644 index 00000000000..2b094bbd956 --- /dev/null +++ b/resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/undo/snapshot/FolderSnapshot.java @@ -0,0 +1,100 @@ +/******************************************************************************* + * Copyright (c) 2006, 2023 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 + * Red Hat Inc - Adapted from classes in org.eclipse.ui.ide.undo and org.eclipse.ui.internal.ide.undo + ******************************************************************************/ + +package org.eclipse.core.internal.resources.undo.snapshot; + +import java.net.URI; +import org.eclipse.core.resources.IFolder; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IResourceFilterDescription; +import org.eclipse.core.resources.IWorkspaceRoot; +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.SubMonitor; + +/** + * FolderDescription is a lightweight snapshot that describes a folder to be + * created. + * + * This class is not intended to be instantiated or used by clients. + * + * @since 3.20 + * + */ +public class FolderSnapshot extends ContainerSnapshot { + + private boolean virtual = false; + + /** + * Create a FolderSnapshot from the specified folder handle. Typically used when + * the folder handle represents a resource that actually exists, although it + * will not fail if the resource is non-existent. + * + * @param folder the folder to be described + * @param virtual the folder is a virtual folder + */ + public FolderSnapshot(IFolder folder, boolean virtual) { + super(folder); + this.virtual = virtual; + } + + /** + * Create a FolderSnapshot from the specified folder handle. If the folder to be + * created should be linked to a different location, specify the location. + * + * @param folder the folder to be described + * @param linkLocation the location to which the folder is linked, or + * null if it is not linked + */ + public FolderSnapshot(IFolder folder, URI linkLocation) { + super(folder); + this.name = folder.getName(); + this.location = linkLocation; + } + + @Override + public IResource createResourceHandle() { + IWorkspaceRoot workspaceRoot = getWorkspace().getRoot(); + IPath folderPath = parent.getFullPath().append(name); + return workspaceRoot.getFolder(folderPath); + } + + @Override + public void createExistentResourceFromHandle(IResource resource, IProgressMonitor mon) throws CoreException { + Assert.isLegal(resource instanceof IFolder); + if (resource.exists()) { + return; + } + IFolder folderHandle = (IFolder) resource; + SubMonitor subMonitor = SubMonitor.convert(mon, 300); + subMonitor.setTaskName(ResourceSnapshotMessages.FolderDescription_NewFolderProgress); + if (filters != null) { + SubMonitor loopMonitor = subMonitor.split(100).setWorkRemaining(filters.length); + for (IResourceFilterDescription filter : filters) { + folderHandle.createFilter(filter.getType(), filter.getFileInfoMatcherDescription(), 0, + loopMonitor.split(1)); + } + } + subMonitor.setWorkRemaining(200); + if (location != null) { + folderHandle.createLink(location, IResource.ALLOW_MISSING_LOCAL, subMonitor.split(100)); + } else { + folderHandle.create(virtual ? IResource.VIRTUAL : 0, true, subMonitor.split(100)); + } + createChildResources(folderHandle, subMonitor.split(100)); + } +} \ No newline at end of file diff --git a/resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/undo/snapshot/IFileContentSnapshot.java b/resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/undo/snapshot/IFileContentSnapshot.java new file mode 100644 index 00000000000..69fc87c0ebe --- /dev/null +++ b/resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/undo/snapshot/IFileContentSnapshot.java @@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright (c) 2006, 2023 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 + * Red Hat Inc - Adapted from classes in org.eclipse.ui.ide.undo and org.eclipse.ui.internal.ide.undo + *******************************************************************************/ +package org.eclipse.core.internal.resources.undo.snapshot; + +import java.io.InputStream; +import org.eclipse.core.runtime.CoreException; + +/** + * IFileContentSnapshot is a description of a file's content. + * + * This class is not intended to be instantiated or used by clients. + * + * @since 3.20 + * + */ +public interface IFileContentSnapshot { + /** + * Returns an open input stream on the contents of the file described. The + * client is responsible for closing the stream when finished. + * + * @return an input stream containing the contents of the file + * @throws CoreException + * any CoreException encountered retrieving the contents + */ + InputStream getContents() throws CoreException; + + /** + * Returns whether this file content description still exists. If it does + * not exist, it will be unable to produce the contents. + * + * @return true if this description exists, and + * false if it does not + */ + boolean exists(); + + /** + * Returns the name of a charset encoding to be used when decoding the + * contents into characters. Returns null if a charset + * has not been explicitly specified. + * + * @return the name of a charset, or null + * @throws CoreException + * any CoreException encountered while determining the character + * set + * + */ + String getCharset() throws CoreException; +} diff --git a/resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/undo/snapshot/MarkerSnapshot.java b/resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/undo/snapshot/MarkerSnapshot.java new file mode 100644 index 00000000000..716fe68287c --- /dev/null +++ b/resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/undo/snapshot/MarkerSnapshot.java @@ -0,0 +1,112 @@ +/******************************************************************************* + * Copyright (c) 2006, 2023 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 + * Red Hat Inc - Adapted from classes in org.eclipse.ui.ide.undo and org.eclipse.ui.internal.ide.undo + ******************************************************************************/ + +package org.eclipse.core.internal.resources.undo.snapshot; + +import java.util.Map; +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.undo.snapshot.IMarkerSnapshot; +import org.eclipse.core.runtime.CoreException; + +/** + * MarkerSnapshot is a lightweight snapshot of a marker that can be used to + * describe a marker to be created or updated. + * + * This class is not intended to be instantiated or used by clients. + * + * @since 3.20 + * + */ +public class MarkerSnapshot implements IMarkerSnapshot { + String type; + + Map attributes; + + IResource resource; + + /** + * + * Create a marker snapshot from the specified marker. + * + * @param marker the marker to be described + * @throws CoreException + */ + public MarkerSnapshot(IMarker marker) throws CoreException { + this.type = marker.getType(); + this.attributes = marker.getAttributes(); + this.resource = marker.getResource(); + + } + + /** + * Create a marker snapshot from the specified marker type, attributes, and + * resource. + * + * @param type the type of marker to be created. + * @param attributes the attributes to be assigned to the marker + * @param resource the resource on which the marker should be created + */ + public MarkerSnapshot(String type, Map attributes, IResource resource) { + this.type = type; + this.attributes = attributes; + this.resource = resource; + } + + /** + * Create a marker from the marker description. + * + * @return the created marker + * @throws CoreException + */ + @Override + public IMarker createMarker() throws CoreException { + IMarker marker = resource.createMarker(type); + marker.setAttributes(attributes); + return marker; + } + + /** + * Update an existing marker using the attributes in the marker description. + * + * @param marker + * the marker to be updated + * @throws CoreException + */ + @Override + public void updateMarker(IMarker marker) throws CoreException { + marker.setAttributes(attributes); + } + + /** + * Return the resource associated with this marker. + * + * @return the resource associated with this marker + */ + @Override + public IResource getResource() { + return resource; + } + + /** + * Return the marker type associated with this marker. + * + * @return the string marker type of this marker + */ + @Override + public String getType() { + return type; + } +} \ No newline at end of file diff --git a/resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/undo/snapshot/ProjectSnapshot.java b/resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/undo/snapshot/ProjectSnapshot.java new file mode 100644 index 00000000000..59623f9180a --- /dev/null +++ b/resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/undo/snapshot/ProjectSnapshot.java @@ -0,0 +1,118 @@ +/******************************************************************************* + * Copyright (c) 2006, 2023 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 + * Red Hat Inc - Adapted from classes in org.eclipse.ui.ide.undo and org.eclipse.ui.internal.ide.undo + ******************************************************************************/ + +package org.eclipse.core.internal.resources.undo.snapshot; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IProjectDescription; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.SubMonitor; + +/** + * ProjectSnapshot is a lightweight snapshot that describes a project to be + * created. + * + * This class is not intended to be instantiated or used by clients. + * + * @since 3.20 + * + */ +public class ProjectSnapshot extends ContainerSnapshot { + + private IProjectDescription projectDescription; + private boolean openOnCreate = true; + + /** + * Create a project snapshot from a specified project. + * + * @param project The project to be described. The project must exist. + */ + public ProjectSnapshot(IProject project) { + super(project); + Assert.isLegal(project.exists()); + if (project.isOpen()) { + try { + this.projectDescription = project.getDescription(); + } catch (CoreException e) { + // Eat this exception because it only occurs when the project + // is not accessible and we have already checked this. We + // don't want to propagate the CoreException into the + // constructor + // API. + } + } else { + openOnCreate = false; + } + } + + /** + * Create a project snapshot from a specified IProjectDescription. Used when the + * project does not yet exist. + * + * @param projectDescription the project description for the future project + */ + public ProjectSnapshot(IProjectDescription projectDescription) { + super(); + this.projectDescription = projectDescription; + } + + @Override + public IResource createResourceHandle() { + return ResourcesPlugin.getWorkspace().getRoot().getProject(getName()); + } + + @Override + public void createExistentResourceFromHandle(IResource resource, + IProgressMonitor monitor) throws CoreException { + SubMonitor subMonitor = SubMonitor.convert(monitor, 200); + Assert.isLegal(resource instanceof IProject); + if (resource.exists()) { + return; + } + IProject projectHandle = (IProject) resource; + subMonitor.setTaskName(ResourceSnapshotMessages.FolderDescription_NewFolderProgress); + if (projectDescription == null) { + projectHandle.create(subMonitor.split(100)); + } else { + projectHandle.create(projectDescription, subMonitor.split(100)); + } + + if (openOnCreate) { + projectHandle.open(IResource.NONE, subMonitor.split(100)); + } + } + + @Override + public String getName() { + if (projectDescription != null) { + return projectDescription.getName(); + } + return super.getName(); + } + + @Override + public boolean verifyExistence(boolean checkMembers) { + // We can only check members if the project is open. + IProject projectHandle = (IProject) createResourceHandle(); + if (projectHandle.isAccessible()) { + return super.verifyExistence(checkMembers); + } + return super.verifyExistence(false); + } +} \ No newline at end of file diff --git a/resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/undo/snapshot/ResourceSnapshotMessages.java b/resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/undo/snapshot/ResourceSnapshotMessages.java new file mode 100644 index 00000000000..368396ccc21 --- /dev/null +++ b/resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/undo/snapshot/ResourceSnapshotMessages.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright (c) 2000, 2023 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 + * Red Hat Inc - Adapted from classes in org.eclipse.ui.ide.undo and org.eclipse.ui.internal.ide.undo + *******************************************************************************/ +package org.eclipse.core.internal.resources.undo.snapshot; + +import org.eclipse.osgi.util.NLS; + +/** + * Helper class to get NLSed messages. + * + * @since 3.20 + * + */ +public final class ResourceSnapshotMessages extends NLS { + + private static final String BUNDLE_NAME= ResourceSnapshotMessages.class.getName(); + + static { + NLS.initializeMessages(BUNDLE_NAME, ResourceSnapshotMessages.class); + } + + private ResourceSnapshotMessages() { + // Do not instantiate + } + + public static String FileDescription_NewFileProgress; + public static String FileDescription_ContentsCouldNotBeRestored; + public static String FolderDescription_NewFolderProgress; + public static String FolderDescription_SavingUndoInfoProgress; +} diff --git a/resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/undo/snapshot/ResourceSnapshotMessages.properties b/resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/undo/snapshot/ResourceSnapshotMessages.properties new file mode 100644 index 00000000000..ef207f7d672 --- /dev/null +++ b/resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/undo/snapshot/ResourceSnapshotMessages.properties @@ -0,0 +1,18 @@ +############################################################################### +# Copyright (c) 2006, 2023 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 +############################################################################### + +FileDescription_NewFileProgress=Creating new file... +FileDescription_ContentsCouldNotBeRestored=Unexpected error. File contents could not be restored from local history during undo/redo. +FolderDescription_NewFolderProgress=Creating new folder... +FolderDescription_SavingUndoInfoProgress=Saving folder info... diff --git a/resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/resources/undo/snapshot/IContainerSnapshot.java b/resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/resources/undo/snapshot/IContainerSnapshot.java new file mode 100644 index 00000000000..e5dfe0f2b73 --- /dev/null +++ b/resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/resources/undo/snapshot/IContainerSnapshot.java @@ -0,0 +1,59 @@ +/******************************************************************************* + * Copyright (c) 2023 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 + * Red Hat Inc - Adapted from classes in org.eclipse.ui.ide.undo and org.eclipse.ui.internal.ide.undo + *******************************************************************************/ +package org.eclipse.core.resources.undo.snapshot; + +import java.net.URI; +import org.eclipse.core.resources.IResourceFilterDescription; + +/** + * IContainerSnapshot is a lightweight description that describes a container to + * be created. + * + * This class is not intended to be instantiated by clients. + * + * @since 3.20 + */ +public interface IContainerSnapshot extends IResourceSnapshot { + + /** + * Get a list of snapshots of members of this container + * + * @return a list of snapshots + */ + public IResourceSnapshot[] getMembers(); + + /** + * Add the specified resource description as a member of this resource + * description + * + * @param member the resource description considered a member of this container. + */ + public void addMember(IResourceSnapshot member); + + /** + * Set the location to which this container is linked. + * + * @param linkLocation the location URI, or null if there is no + * link + */ + public void setLocation(URI linkLocation); + + /** + * Set the filters to which should be created on this container. + * + * @param filterList the filters + */ + public void setFilters(IResourceFilterDescription[] filterList); +} diff --git a/resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/resources/undo/snapshot/IMarkerSnapshot.java b/resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/resources/undo/snapshot/IMarkerSnapshot.java new file mode 100644 index 00000000000..1ceb6c723ad --- /dev/null +++ b/resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/resources/undo/snapshot/IMarkerSnapshot.java @@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright (c) 2023 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 + * Red Hat Inc - Adapted from classes in org.eclipse.ui.ide.undo and org.eclipse.ui.internal.ide.undo + *******************************************************************************/ +package org.eclipse.core.resources.undo.snapshot; + +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.CoreException; + +/** + * IMarkerSnapshot is a lightweight snapshot of a marker for the purposes of + * undoing. + * + * This class is not intended to be instantiated by clients. + * + * @since 3.20 + */ +public interface IMarkerSnapshot { + + /** + * Create a marker from the marker description. + * + * @return the created marker + * @throws CoreException + */ + public IMarker createMarker() throws CoreException; + + /** + * Update an existing marker using the attributes in the marker description. + * + * @param marker the marker to be updated + * @throws CoreException + */ + public void updateMarker(IMarker marker) throws CoreException; + + /** + * Return the resource associated with this marker. + * + * @return the resource associated with this marker + */ + public IResource getResource(); + + /** + * Return the marker type associated with this marker. + * + * @return the string marker type of this marker + */ + public String getType(); +} diff --git a/resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/resources/undo/snapshot/IResourceSnapshot.java b/resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/resources/undo/snapshot/IResourceSnapshot.java new file mode 100644 index 00000000000..79f8daa1ee2 --- /dev/null +++ b/resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/resources/undo/snapshot/IResourceSnapshot.java @@ -0,0 +1,109 @@ +/******************************************************************************* + * Copyright (c) 2007, 2023 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 + * Red Hat Inc - Adapted from classes in org.eclipse.ui.ide.undo and org.eclipse.ui.internal.ide.undo + ******************************************************************************/ + +package org.eclipse.core.resources.undo.snapshot; + +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; + +/** + * IResourceSnapshot is a lightweight snapshot that describes the common + * attributes of a resource to be created. + * + * This class is not intended to be extended by clients. + * + * @since 3.20 + * + */ +public interface IResourceSnapshot { + + /** + * Create a resource handle that can be used to create a resource from this + * resource description. This handle can be used to create the actual + * resource, or to describe the creation to a resource delta factory. + * + * @return the resource handle that can be used to create a resource from + * this description + */ + public IResource createResourceHandle(); + + /** + * Get the name of this resource. + * + * @return the name of the Resource + */ + public String getName(); + + /** + * Create an existent resource from this resource description. + * + * @param monitor + * the progress monitor to use + * @return a resource that has the attributes of this resource description + * @throws CoreException if creation failed + */ + public IResource createResource(IProgressMonitor monitor) throws CoreException; + + /** + * Given a resource handle, create an actual resource with the attributes of + * the receiver resource description. + * + * @param resource + * the resource handle + * @param monitor + * the progress monitor to be used when creating the resource + * @throws CoreException if creation failed + */ + public void createExistentResourceFromHandle(IResource resource, + IProgressMonitor monitor) throws CoreException; + + /** + * Return a boolean indicating whether this resource description has enough + * information to create a resource. + * + * @return true if the resource can be created, and + * false if it does not have enough information + */ + public boolean isValid(); + + /** + * Record the appropriate state of this resource description using + * any available resource history. + * + * @param resource + * the resource whose state is to be recorded. + * @param monitor + * the progress monitor to be used + * @throws CoreException in case of error + */ + public void recordStateFromHistory(IResource resource, + IProgressMonitor monitor) throws CoreException; + + /** + * Return a boolean indicating whether this description represents an + * existent resource. + * + * @param checkMembers + * Use true if members should also exist in order + * for this description to be considered existent. A value of + * false indicates that the existence of members + * does not matter. + * + * @return a boolean indicating whether this description represents an + * existent resource. + */ + public boolean verifyExistence(boolean checkMembers); +} diff --git a/resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/resources/undo/snapshot/ResourceSnapshotFactory.java b/resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/resources/undo/snapshot/ResourceSnapshotFactory.java new file mode 100644 index 00000000000..7ffe66d9cc7 --- /dev/null +++ b/resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/resources/undo/snapshot/ResourceSnapshotFactory.java @@ -0,0 +1,167 @@ +/******************************************************************************* + * Copyright (c) 2023 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 + * Red Hat Inc - Adapted from classes in org.eclipse.ui.ide.undo and org.eclipse.ui.internal.ide.undo + *******************************************************************************/ +package org.eclipse.core.resources.undo.snapshot; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.net.URI; +import java.util.Map; +import org.eclipse.core.internal.resources.undo.snapshot.ContainerSnapshot; +import org.eclipse.core.internal.resources.undo.snapshot.FileSnapshot; +import org.eclipse.core.internal.resources.undo.snapshot.FolderSnapshot; +import org.eclipse.core.internal.resources.undo.snapshot.IFileContentSnapshot; +import org.eclipse.core.internal.resources.undo.snapshot.MarkerSnapshot; +import org.eclipse.core.internal.resources.undo.snapshot.ProjectSnapshot; +import org.eclipse.core.resources.IContainer; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IFolder; +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IProjectDescription; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.CoreException; + +/** + * ResourceSnapshotFactory provides utility methods for creating snapshots of + * resources or markers. + * + * @since 3.20 + */ +public class ResourceSnapshotFactory { + /** + * Create a resource snapshot given the specified resource. The resource is + * assumed to exist. + * + * @param resource the resource from which a description should be created + * @return the resource description + */ + public static IResourceSnapshot fromResource(IResource resource) { + if (resource.getType() == IResource.PROJECT) { + return new ProjectSnapshot((IProject) resource); + } else if (resource.getType() == IResource.FOLDER) { + return new FolderSnapshot((IFolder) resource, resource.isVirtual()); + } else if (resource.getType() == IResource.FILE) { + return new FileSnapshot((IFile) resource); + } else { + throw new IllegalArgumentException(); + } + } + + /** + * Create a project snapshot from a specified IProjectDescription. Used when the + * project does not yet exist. + * + * @param projectDescription the project description for the future project + */ + public static IContainerSnapshot fromProjectDescription(IProjectDescription projectDescription) { + return new ProjectSnapshot(projectDescription); + } + + /** + * Create a container description from the specified container handle that can + * be used to create the container. The returned ContainerDescription should + * represent any non-existing parents in addition to the specified container. + * + * @param container the handle of the container to be described + * @return a container description describing the container and any non-existing + * parents. + */ + public static IContainerSnapshot fromContainer(IContainer container) { + return ContainerSnapshot.fromContainer(container); + } + + /** + * Create a group container description from the specified container handle that + * can be used to create the container. The returned ContainerDescription should + * represent any non-existing parents in addition to the specified container. + * + * @param container the handle of the container to be described + * @return a container description describing the container and any non-existing + * parents. + */ + public static IContainerSnapshot fromVirtualFolderContainer(IContainer container) { + return ContainerSnapshot.fromContainer(container, true); + } + + /** + * Create a file snapshot from the specified file handle. The handle does not + * exist, so no information should be derived from it. If a location path is + * specified, this file should represent a link to another location. The content + * description describes any state that should be used when the file resource is + * created. + * + * @param file the file to be described + * @param linkLocation the location of the file's link, or null if + * the file is not linked + * @param contents an input stream representing the contents of the file + */ + public static IResourceSnapshot fromFileDetails(IFile file, URI linkLocation, InputStream contents) { + return new FileSnapshot(file, linkLocation, createFileContentDescription(file, contents)); + } + + /** + * + * Create a marker snapshot from the specified marker. + * + * @param marker the marker to be described + * @throws CoreException + */ + public static IMarkerSnapshot fromMarker(IMarker marker) throws CoreException { + return new MarkerSnapshot(marker); + } + + /** + * Create a marker snapshot from the specified marker type, attributes, and + * resource. + * + * @param type the type of marker to be created. + * @param attributes the attributes to be assigned to the marker + * @param resource the resource on which the marker should be created + */ + public static IMarkerSnapshot fromMarkerDetails(String type, Map attributes, IResource resource) { + return new MarkerSnapshot(type, attributes, resource); + } + + /* + * Create a file state that represents the desired contents and attributes of + * the file to be created. Used to mimic file history when a resource is first + * created. + */ + private static IFileContentSnapshot createFileContentDescription(final IFile file, final InputStream contents) { + return new IFileContentSnapshot() { + @Override + public InputStream getContents() { + if (contents != null) { + return contents; + } + return new ByteArrayInputStream(new byte[0]); + } + + @Override + public String getCharset() { + try { + return file.getCharset(false); + } catch (CoreException e) { + return null; + } + } + + @Override + public boolean exists() { + return true; + } + }; + } +}