### Eclipse Workspace Patch 1.0 #P org.eclipse.core.resources Index: src/org/eclipse/core/internal/resources/LocalMetaArea.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/LocalMetaArea.java,v retrieving revision 1.41 diff -u -r1.41 LocalMetaArea.java --- src/org/eclipse/core/internal/resources/LocalMetaArea.java 3 Jun 2008 12:34:50 -0000 1.41 +++ src/org/eclipse/core/internal/resources/LocalMetaArea.java 4 Mar 2010 05:37:13 -0000 @@ -31,6 +31,7 @@ /* package */static final String F_PROJECT_LOCATION = ".location"; //$NON-NLS-1$ /* package */static final String F_PROJECTS = ".projects"; //$NON-NLS-1$ /* package */static final String F_PROPERTIES = ".properties"; //$NON-NLS-1$ + /* package */static final String F_REFRESH = ".refresh"; //$NON-NLS-1$ /* package */static final String F_ROOT = ".root"; //$NON-NLS-1$ /* package */static final String F_SAFE_TABLE = ".safetable"; //$NON-NLS-1$ /* package */static final String F_SNAP = ".snap"; //$NON-NLS-1$ @@ -62,6 +63,13 @@ Workspace.clear(getOldDescriptionLocationFor(target).toFile()); } + /** + * Delete the refresh snapshot once it has been used to open a new project. + */ + public void clearRefresh(IProject target) { + Workspace.clear(getRefreshLocationFor(target).toFile()); + } + public void create(IProject target) { java.io.File file = locationFor(target).toFile(); //make sure area is empty @@ -147,6 +155,15 @@ return locationFor(resource).append(F_PROPERTIES); } + /** + * Returns the path of the file in which to save the refresh snapshot for + * the given project. + */ + public IPath getRefreshLocationFor(IProject project) { + Assert.isNotNull(project); + return locationFor(project).append(F_REFRESH); + } + public IPath getSafeTableLocationFor(String pluginId) { IPath prefix = metaAreaLocation.append(F_SAFE_TABLE); // if the plugin is the resources plugin, we return the master table Index: src/org/eclipse/core/internal/resources/Project.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/Project.java,v retrieving revision 1.165 diff -u -r1.165 Project.java --- src/org/eclipse/core/internal/resources/Project.java 25 Jan 2010 14:08:43 -0000 1.165 +++ src/org/eclipse/core/internal/resources/Project.java 4 Mar 2010 05:37:14 -0000 @@ -9,9 +9,12 @@ * IBM Corporation - initial API and implementation * Serge Beauchamp (Freescale Semiconductor) - [229633] Project Path Variable Support * Anton Leherbauer (Wind River) - [198591] Allow Builder to specify scheduling rule + * Francis Lynch (Wind River) - [301563] Save and load refresh information snapshots *******************************************************************************/ package org.eclipse.core.internal.resources; +import org.eclipse.core.runtime.IPath; + import java.net.URI; import java.util.*; import org.eclipse.core.filesystem.*; @@ -820,6 +823,25 @@ } /* (non-Javadoc) + * @see IProject#loadSnapshot(int, URI, IProgressMonitor) + */ + public void loadSnapshot(int options, URI snapshotLocation, + IProgressMonitor monitor) throws CoreException { + if ((options & SNAPSHOT_TREE) != 0) { + // load a snapshot of refresh information when project is opened + if (isOpen()) { + String message = Messages.resources_projectMustNotBeOpen; + MultiStatus status = new MultiStatus(ResourcesPlugin.PI_RESOURCES, IStatus.ERROR, message, null); + throw new CoreException(status); + } + // copy the snapshot from the URI into the project metadata + IPath snapshotPath = workspace.getMetaArea().getRefreshLocationFor(this); + IFileStore snapshotFileStore = EFS.getStore(org.eclipse.core.filesystem.URIUtil.toURI(snapshotPath)); + snapshotFileStore.delete(EFS.NONE, monitor); + EFS.getStore(snapshotLocation).copy(snapshotFileStore, options, monitor); + } + } + /* (non-Javadoc) * @see IProject#move(IProjectDescription, boolean, IProgressMonitor) */ public void move(IProjectDescription destination, boolean force, IProgressMonitor monitor) throws CoreException { @@ -921,14 +943,24 @@ } startup(); //request a refresh if the project is new and has unknown members on disk - // or restore of the project is not fully succesfull + // or restore of the project is not fully successful if ((!used && unknownChildren) || !minorIssuesDuringRestore) { + boolean refreshed = false; + if (!used) { + refreshed = workspace.getSaveManager().restoreFromRefreshSnapshot( + this, Policy.subMonitorFor(monitor, Policy.opWork * 20 / 100)); + if (refreshed) { // account for the refresh work + monitor.worked(Policy.opWork * 60 / 100); + } + } //refresh either in background or foreground - if ((updateFlags & IResource.BACKGROUND_REFRESH) != 0) { - workspace.refreshManager.refresh(this); - monitor.worked(Policy.opWork * 80 / 100); - } else { - refreshLocal(IResource.DEPTH_INFINITE, Policy.subMonitorFor(monitor, Policy.opWork * 80 / 100)); + if (!refreshed) { + if ((updateFlags & IResource.BACKGROUND_REFRESH) != 0) { + workspace.refreshManager.refresh(this); + monitor.worked(Policy.opWork * 60 / 100); + } else { + refreshLocal(IResource.DEPTH_INFINITE, Policy.subMonitorFor(monitor, Policy.opWork * 60 / 100)); + } } } //creation of this project may affect overlapping resources @@ -1036,6 +1068,31 @@ } /* (non-Javadoc) + * @see IProject#saveSnapshot(int, URI, IProgressMonitor) + */ + public void saveSnapshot(int options, URI snapshotLocation, + IProgressMonitor monitor) throws CoreException { + if ((options & SNAPSHOT_TREE) != 0) { + // write a snapshot of refresh information + monitor = Policy.monitorFor(monitor); + try { + String msg = NLS.bind(Messages.resources_copying, getName()); + monitor.beginTask(msg, Policy.totalWork); + try { + IProgressMonitor sub = Policy.subMonitorFor(monitor, Policy.opWork / 2, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL); + workspace.getSaveManager().saveRefreshSnapshot( + this, snapshotLocation, sub); + monitor.worked(Policy.opWork / 2); + } catch (OperationCanceledException e) { + workspace.getWorkManager().operationCanceled(); + throw e; + } + } finally { + monitor.done(); + } + } + } + /* (non-Javadoc) * @see IProject#setDescription(IProjectDescription, int, IProgressMonitor) */ public void setDescription(IProjectDescription description, int updateFlags, IProgressMonitor monitor) throws CoreException { Index: src/org/eclipse/core/internal/resources/SaveManager.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/SaveManager.java,v retrieving revision 1.102 diff -u -r1.102 SaveManager.java --- src/org/eclipse/core/internal/resources/SaveManager.java 2 Nov 2009 10:12:59 -0000 1.102 +++ src/org/eclipse/core/internal/resources/SaveManager.java 4 Mar 2010 05:37:16 -0000 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2009 IBM Corporation and others. + * Copyright (c) 2000, 2010 IBM Corporation 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 @@ -7,11 +7,19 @@ * * Contributors: * IBM Corporation - initial API and implementation + * Francis Lynch (Wind River) - add support for snapshot of project tree *******************************************************************************/ package org.eclipse.core.internal.resources; +import org.eclipse.core.filesystem.IFileStore; + +import org.eclipse.core.filesystem.EFS; + +import java.net.URI; + import java.io.*; import java.util.*; +import java.util.zip.*; import org.eclipse.core.internal.events.*; import org.eclipse.core.internal.localstore.*; import org.eclipse.core.internal.utils.*; @@ -708,6 +716,43 @@ } /** + * Restores the contents of this project from a refresh snapshot, if possible. + * Throws an exception if the snapshot is found but an error occurs when reading + * the file. + * @return true if the project data was restored successfully, + * and false if the refresh snapshot was not found or could not be opened. + * @exception CoreException if an error occurred reading the snapshot file. + */ + protected boolean restoreFromRefreshSnapshot(Project project, + IProgressMonitor monitor) throws CoreException { + boolean status = true; + IPath snapshotPath = workspace.getMetaArea().getRefreshLocationFor(project); + java.io.File snapshotFile = snapshotPath.toFile(); + if (!snapshotFile.exists()) + return false; + if (Policy.DEBUG_RESTORE) + System.out.println("Restore project " + project.getFullPath() + ": starting..."); //$NON-NLS-1$ //$NON-NLS-2$ + long start = System.currentTimeMillis(); + monitor = Policy.monitorFor(monitor); + try { + monitor.beginTask("", 40); //$NON-NLS-1$ + status = restoreTreeFromRefreshSnapshot(project, + snapshotFile, Policy.subMonitorFor(monitor, 40)); + if (status) { + // load the project description and set internal description + ProjectDescription description = workspace.getFileSystemManager().read(project, true); + project.internalSetDescription(description, false); + workspace.getMetaArea().clearRefresh(project); + } + } finally { + monitor.done(); + } + if (Policy.DEBUG_RESTORE) + System.out.println("Restore project " + project.getFullPath() + ": " + (System.currentTimeMillis() - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + return status; + } + + /** * Reads the markers which were originally saved * for the tree rooted by the given resource. */ @@ -970,6 +1015,47 @@ return true; } + /** + * Restores a tree saved as a refresh snapshot to a specified URI. + * @return true if the snapshot exists, false otherwise. + * @exception CoreException if the project could not be restored. + */ + protected boolean restoreTreeFromRefreshSnapshot(Project project, + java.io.File snapshotFile, IProgressMonitor monitor) throws CoreException { + long start = System.currentTimeMillis(); + monitor = Policy.monitorFor(monitor); + String message; + IPath snapshotPath = null; + try { + monitor.beginTask("", Policy.totalWork); //$NON-NLS-1$ + InputStream snapIn = new FileInputStream(snapshotFile); + ZipInputStream zip = new ZipInputStream(snapIn); + ZipEntry treeEntry = zip.getNextEntry(); + if (treeEntry == null || !treeEntry.getName().equals("resource-index.tree")) { //$NON-NLS-1$ + zip.close(); + return false; + } + DataInputStream input = new DataInputStream(zip); + try { + WorkspaceTreeReader reader = WorkspaceTreeReader.getReader(workspace, input.readInt()); + reader.readTree(project, input, Policy.subMonitorFor(monitor, Policy.totalWork)); + } finally { + input.close(); + zip.close(); + } + } catch (IOException e) { + snapshotPath = new Path(snapshotFile.getPath()); + message = NLS.bind(Messages.resources_readMeta, snapshotPath); + throw new ResourceException(IResourceStatus.FAILED_READ_METADATA, snapshotPath, message, e); + } finally { + monitor.done(); + } + if (Policy.DEBUG_RESTORE_TREE) { + System.out.println("Restore Tree for " + project.getFullPath() + ": " + (System.currentTimeMillis() - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + return true; + } + class InternalMonitorWrapper extends ProgressMonitorWrapper{ private boolean ignoreCancel; @@ -1162,6 +1248,58 @@ } /** + * Writes a snapshot of project refresh information to the specified + * location. + * @param project the project to write a refresh snapshot for + * @param monitor progress monitor + * @exception CoreException if there is a problem writing the snapshot. + */ + public void saveRefreshSnapshot(Project project, URI snapshotLocation, + IProgressMonitor monitor) throws CoreException { + IFileStore store = EFS.getStore(snapshotLocation); + IPath snapshotPath = new Path(snapshotLocation.getPath()); + OutputStream snapOut = store.openOutputStream(EFS.NONE, monitor); + java.io.File tmpTree = null; + try { + tmpTree = java.io.File.createTempFile("tmp", ".tree"); //$NON-NLS-1$//$NON-NLS-2$ + } catch (IOException e) { + throw new ResourceException(IResourceStatus.FAILED_WRITE_LOCAL, + snapshotPath, Messages.resources_copyProblem, e); + } + ZipOutputStream out = null; + try { + FileOutputStream fis = new FileOutputStream(tmpTree); + DataOutputStream output = new DataOutputStream(fis); + try { + output.writeInt(ICoreConstants.WORKSPACE_TREE_VERSION_2); + writeTree(project, output, monitor); + } finally { + output.close(); + } + out = new ZipOutputStream(snapOut); + out.setLevel(Deflater.BEST_COMPRESSION); + ZipEntry e = new ZipEntry("resource-index.tree"); //$NON-NLS-1$ + out.putNextEntry(e); + int read = 0; + byte[] buffer = new byte[4096]; + InputStream in = new FileInputStream(tmpTree); + try { + while ((read = in.read(buffer)) >= 0) { + out.write(buffer, 0, read); + } + out.closeEntry(); + } finally { + in.close(); + } + out.close(); + } catch (IOException e) { + throw new ResourceException(IResourceStatus.FAILED_WRITE_LOCAL, snapshotPath, Messages.resources_copyProblem, e); + } finally { + tmpTree.delete(); + } + } + + /** * Writes the current state of the entire workspace tree to disk. * This is used during workspace save. saveTree(Project) * is used to save the state of an individual project. Index: src/org/eclipse/core/internal/utils/Messages.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.core.resources/src/org/eclipse/core/internal/utils/Messages.java,v retrieving revision 1.41 diff -u -r1.41 Messages.java --- src/org/eclipse/core/internal/utils/Messages.java 25 Jan 2010 14:08:44 -0000 1.41 +++ src/org/eclipse/core/internal/utils/Messages.java 4 Mar 2010 05:37:17 -0000 @@ -247,6 +247,7 @@ public static String resources_pathNull; public static String resources_projectDesc; public static String resources_projectDescSync; + public static String resources_projectMustNotBeOpen; public static String resources_projectPath; public static String resources_pruningHistory; public static String resources_reading; Index: src/org/eclipse/core/internal/utils/messages.properties =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.core.resources/src/org/eclipse/core/internal/utils/messages.properties,v retrieving revision 1.139 diff -u -r1.139 messages.properties --- src/org/eclipse/core/internal/utils/messages.properties 25 Jan 2010 14:08:44 -0000 1.139 +++ src/org/eclipse/core/internal/utils/messages.properties 4 Mar 2010 05:37:18 -0000 @@ -9,6 +9,7 @@ # IBM Corporation - initial API and implementation # Serge Beauchamp (Freescale Semiconductor) - [252996] add resource filtering # Serge Beauchamp (Freescale Semiconductor) - [229633] Group and Project Path Variable Support +# Francis Lynch (Wind River) - [301563] project refresh snapshots ############################################################################### ### Resources plugin messages. @@ -243,6 +244,7 @@ resources_pathNull = Paths must not be null. resources_projectDesc = Problems encountered while setting project description. resources_projectDescSync = Could not set the project description for ''{0}'' because the project description file (.project) is out of sync with the file system. +resources_projectMustNotBeOpen = Project must not be open. resources_projectPath = Path for project must have only one segment. resources_pruningHistory = Pruning history. resources_reading = Reading. Index: src/org/eclipse/core/resources/IProject.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.core.resources/src/org/eclipse/core/resources/IProject.java,v retrieving revision 1.47 diff -u -r1.47 IProject.java --- src/org/eclipse/core/resources/IProject.java 25 Jan 2010 14:08:44 -0000 1.47 +++ src/org/eclipse/core/resources/IProject.java 4 Mar 2010 05:37:19 -0000 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2009 IBM Corporation and others. + * Copyright (c) 2000, 2010 IBM Corporation 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 @@ -7,9 +7,12 @@ * * Contributors: * IBM Corporation - initial API and implementation + * Francis Lynch (Wind River) - added loadSnapshot/saveSnapshot *******************************************************************************/ package org.eclipse.core.resources; +import java.net.URI; + import java.util.Map; import org.eclipse.core.runtime.*; import org.eclipse.core.runtime.content.IContentTypeMatcher; @@ -42,6 +45,15 @@ */ public interface IProject extends IContainer, IAdaptable { /** + * Option constant (value 1) indicating that the snapshot to be + * loaded or saved contains refresh information. + * @see #loadSnapshot(int, URI, IProgressMonitor) + * @see #saveSnapshot(int, URI, IProgressMonitor) + * @since 3.6 + */ + public static final int SNAPSHOT_TREE = 1; + + /** * Invokes the build method of the specified builder * for this project. Does nothing if this project is closed. If this project * has multiple builders on its build spec matching the given name, only @@ -569,6 +581,29 @@ public boolean isOpen(); /** + * Loads a snapshot of project meta-data from the given location URI. + * Must be called after the project has been created, but before it is + * opened. The options constant controls what kind of snapshot information + * to load. Valid option values include: + * + * @param options kind of snapshot information to load + * @param snapshotLocation URI for the snapshot information + * @param monitor a progress monitor, or null if progress + * reporting is not desired + * @exception CoreException if this method fails. Reasons include: + * + * @exception OperationCanceledException if the operation is canceled. + * @see #saveSnapshot(int, URI, IProgressMonitor) + * @since 3.6 + */ + public void loadSnapshot(int options, URI snapshotLocation, + IProgressMonitor monitor) throws CoreException; + + /** * Renames this project so that it is located at the name in * the given description. *

@@ -621,13 +656,19 @@ * of its resources from information stored on disk. *

*

- * The BACKGROUND_REFRESH update flag controls how - * this method behaves when a project is opened for the first time on a location - * that has existing resources on disk. If this flag is specified, resources on disk - * will be added to the project in the background after this method returns. - * Child resources of the project may not be available until this background - * refresh completes. If this flag is not specified, resources on disk are added - * to the project in the foreground before this method returns. + * When a project is opened for the first time, initial information about the + * project's existing resources can be obtained in the following ways: + *

*

* This method changes resources; these changes will be reported * in a subsequent resource change event that includes @@ -689,6 +730,28 @@ public void open(IProgressMonitor monitor) throws CoreException; /** + * Writes a snapshot of project meta-data into the given location URI. + * The options constant controls what kind of snapshot information to + * write. Valid option values include: + * + * @param options kind of snapshot information to save + * @param snapshotLocation URI for the snapshot information + * @param monitor a progress monitor, or null if progress + * reporting is not desired + * @exception CoreException if this method fails. Reasons include: + * + * @exception OperationCanceledException if the operation is canceled. + * @see #loadSnapshot(int, URI, IProgressMonitor) + * @since 3.6 + */ + public void saveSnapshot(int options, URI snapshotLocation, + IProgressMonitor monitor) throws CoreException; + + /** * Changes this project resource to match the given project * description. This project should exist and be open. *

@@ -852,4 +915,5 @@ * @since 3.6 */ public IPathVariableManager getPathVariableManager(); + }