/******************************************************************************* * Copyright (c) 2009 QNX Software Systems 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: * QNX Software Systems - initial API and implementation *******************************************************************************/ package org.eclipse.cdt.internal.ui.workingsets; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Map; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.IncrementalProjectBuilder; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.MultiStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.SubMonitor; import org.eclipse.osgi.util.NLS; import org.eclipse.ui.IWorkingSet; import org.eclipse.cdt.core.model.CoreModel; import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; import org.eclipse.cdt.core.settings.model.ICProjectDescription; import org.eclipse.cdt.ui.CUIPlugin; /** * <p> * A snapshot of the working set configurations and project configurations across the workspace at the time * when it was created. The snapshot maintains a delta from that original state to the current state, for such * comparison operations as determining which projects need to be re-built because their active configurations * have changed. The snapshot provides mutable working-copy views of the working set configurations at the * time of snapshot creation. * </p> * <p> * To make changes to working set configurations, first * {@linkplain WorkingSetConfigurationManager#createWorkspaceSnapshot() obtain a snapshot} from the * {@link WorkingSetConfigurationManager}. Then make edits to the various snapshots of the configuration * elements and {@linkplain #save() save} the snapshot * </p> * * @author Christian W. Damus (cdamus) * * @since 6.0 * */ public class WorkspaceSnapshot { private Map<String, IWorkingSetProxy.ISnapshot> workingSets = new java.util.HashMap<String, IWorkingSetProxy.ISnapshot>(); private Map<IProject, ProjectState> projectStates = new java.util.HashMap<IProject, ProjectState>(); /** * Initializes me. I capture the current C/C++ active configuration state of the projects in the * workspace. */ WorkspaceSnapshot() { IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); for (IProject next : root.getProjects()) { ICProjectDescription desc = CoreModel.getDefault().getProjectDescription(next); if (desc != null) { projectStates.put(next, createProjectState(next, desc)); } } } /** * Creates a project state using the registered factory, if possible. * * @param project * a workspace project * @param desc * its CDT project description * * @return its state capture (will never be <code>null</code>) */ private static ProjectState createProjectState(IProject project, ICProjectDescription desc) { ProjectState result = null; IWorkingSetProjectConfigurationFactory factory = IWorkingSetProjectConfigurationFactory.Registry.INSTANCE .getFactory(project); if (factory != null) { result = factory.createProjectState(project, desc); } if (result == null) { // the default-default result = new ProjectState(project, desc); } return result; } /** * Initializes me with the specified working sets to copy for editing. * * @param workingSets * the working sets to copy * @return myself */ WorkspaceSnapshot initialize(Map<String, IWorkingSetProxy> workingSets) { for (IWorkingSet next : WorkingSetConfigurationManager.WS_MGR.getWorkingSets()) { IWorkingSetProxy workingSet = workingSets.get(next.getName()); if (workingSet == null) { workingSet = new WorkingSetProxy(); ((WorkingSetProxy) workingSet).setName(next.getName()); } if (workingSet.isValid()) { this.workingSets.put(workingSet.getName(), workingSet.createSnapshot(this)); } } return this; } /** * Obtains a mutable snapshot of the named working set. * * @param name * the working set to retrieve * * @return the working set snapshot, or <code>null</code> if there is no working set by this name * * @see #getWorkingSets() */ public IWorkingSetProxy.ISnapshot getWorkingSet(String name) { return workingSets.get(name); } /** * Obtains snapshots of all of the working sets currently defined by the workbench. * * @return the working set snapshots * * @see #getWorkingSet(String) */ public Collection<IWorkingSetProxy.ISnapshot> getWorkingSets() { return workingSets.values(); } /** * Obtains the project state recording the initial configuration of the specified <tt>project</tt> at the * time that this snapshot was taken. * * @param project * a project * @return its snapshot/delta state */ public ProjectState getState(IProject project) { return projectStates.get(project); } /** * Queries the ID of the configuration of the specified project that was active when the workspace * snapshot was taken. * * @param project * a project * @return its active configuration ID at the time of the snapshot */ String getBaselineConfigurationID(IProject project) { String result = null; ProjectState state = getState(project); if (state != null) { result = state.getBaselineConfigurationID(); } return result; } /** * Queries the ID of the currently active configuration of the specified project. * * @param project * a project * * @return the current active configuration ID */ String getActiveConfigurationID(IProject project) { String result = null; ProjectState state = getState(project); if (state != null) { result = state.getActiveConfigurationID(); } return result; } /** * Queries whether the configuration selected by the given project in a working set configuration is * currently active in the workspace. * * @param project * a project configuration element in a working set configuration * * @return whether the project's selected configuration is active * * @see #activate(IProject, String) */ boolean isActive(IWorkingSetProjectConfiguration project) { boolean result = false; ProjectState state = getState(project.resolveProject()); if (state != null) { result = state.isActive(project.getSelectedConfigurationID()); } return result; } /** * Activates, in the workspace, the specified configuration of a project. This method has no effect if the * given configuration is already active. * * @param project * the project for which to set the active configuration * @param configID * the ID of the configuration to activate * * @see #isActive(IWorkingSetProjectConfiguration) */ void activate(IProject project, String configID) { ProjectState state = getState(project); if (state != null) { state.activate(configID); } } IStatus build(IProject project, String configID, IProgressMonitor monitor) { ProjectState state = getState(project); if (state != null) { return state.build(configID, monitor); } return new Status(IStatus.ERROR, CUIPlugin.PLUGIN_ID, NLS.bind( WorkingSetMessages.WorkspaceSnapshot_buildNoProj, project.getName())); } /** * Obtains the configurations of the specified project, as known at the time that this snapshot was taken. * * @param project * a project * * @return its configurations, which may be an empty collection if the project is not a C/C++ project */ public Collection<ICConfigurationDescription> getConfigurations(IProject project) { Collection<ICConfigurationDescription> result; ProjectState state = getState(project); if (state == null) { result = Collections.emptyList(); } else { result = state.getConfigurations(); } return result; } /** * Obtains the specified configuration of a project, as known at the time that this snapshot was taken. * * @param project * a project * @param id * the ID of a configuration * * @return the configuration, or <code>null</code> if there is none such by this ID */ public ICConfigurationDescription getConfiguration(IProject project, String id) { ProjectState state = getState(project); return (state == null) ? null : state.getConfiguration(id); } /** * Queries the projects that need to be built because their active configurations have been changed since * this snapshot was taken. * * @return the projects needing to be re-built */ public Collection<IProject> getProjectsToBuild() { Collection<IProject> result = new java.util.ArrayList<IProject>(); for (ProjectState next : projectStates.values()) { if (next.needsBuild()) { result.add(next.getProject()); } } return result; } /** * Saves my working set configuration settings. */ public void save() { WorkingSetConfigurationManager.getDefault().save(this); } // // Nested classes // /** * Capture of the current state of a project at the time when a {@linkplain WorkspaceSnapshot workspace * snapshot} was taken, and its delta from that original state. This tracks at least the C/C++ project * description (if any) and the original active configuration. Subclasses may track additional * configuration details. * * @author Christian W. Damus (cdamus) * * @since 6.0 */ public static class ProjectState { private final IProject project; private final ICProjectDescription projectDescription; private final String baselineConfigID; private String currentConfigID; private String lastBuiltConfigID; /** * Initializes me with a project and its description. * * @param project * the project whose state I track * @param desc * the project's description, from which I capture the initial state snapshot */ protected ProjectState(IProject project, ICProjectDescription desc) { this.project = project; this.projectDescription = desc; if (desc != null) { ICConfigurationDescription config = desc.getActiveConfiguration(); this.baselineConfigID = (config == null) ? "" : config.getId(); //$NON-NLS-1$ } else { this.baselineConfigID = ""; //$NON-NLS-1$ } this.currentConfigID = this.baselineConfigID; } /** * Obtains the project that I track. * * @return my project */ public final IProject getProject() { return project; } /** * Obtains the project description that was current when the snapshot was taken. * * @return my project description */ protected final ICProjectDescription getProjectDescription() { return projectDescription; } /** * Queries whether my project needs to be re-built because its active configuration has been changed * since the snapshot was taken, and it hasn't been built already. * * @return whether I need to be re-built */ public boolean needsBuild() { return !currentConfigID.equals(baselineConfigID) && !currentConfigID.equals(lastBuiltConfigID); } /** * Queries whether the specified configuration is currently active in the workspace for my project. * * @param configID * the ID of a project build configuration * @return whether it is my project's active configuration */ public boolean isActive(String configID) { return currentConfigID.equals(configID); } /** * Queries the ID of the currently active configuration. * * @return the current active configuration ID */ protected String getActiveConfigurationID() { return currentConfigID; } /** * Queries the ID of the configuration of my project that was active when the workspace snapshot was * taken. * * @return its active configuration ID at the time of the snapshot */ protected String getBaselineConfigurationID() { return baselineConfigID; } /** * Sets my project's active configuration to the specified configuration. This method has no effect if * this configuration is already active. * * @param configID * the ID of the configuration to activate */ protected void activate(String configID) { if (!currentConfigID.equals(configID) && (projectDescription != null)) { try { ICConfigurationDescription realConfig = projectDescription.getConfigurationById(configID); realConfig.setActive(); CoreModel.getDefault().setProjectDescription(project, projectDescription); currentConfigID = configID; } catch (CoreException e) { CUIPlugin.log(e); } } } /** * Builds the specified configuration of my project. I update myself to record a new build baseline if * the build succeeds. * * @param configID * the configuration to build * @param monitor * a monitor to report build progress * * @return the status of the build */ protected IStatus build(String configID, IProgressMonitor monitor) { IStatus result = Status.OK_STATUS; ICConfigurationDescription config = getConfiguration(configID); if (config == null) { result = new Status(IStatus.WARNING, CUIPlugin.PLUGIN_ID, NLS.bind( WorkingSetMessages.WSProjConfig_noConfig, getProject().getName())); } else { if (!isActive(configID)) { activate(configID); result = new Status(IStatus.WARNING, CUIPlugin.PLUGIN_ID, NLS.bind( WorkingSetMessages.WSProjConfig_activatedWarning, config.getName(), getProject() .getName())); } monitor = SubMonitor.convert(monitor); try { getProject().build(IncrementalProjectBuilder.INCREMENTAL_BUILD, monitor); // update the build baseline to this config, which is now active built(configID); } catch (CoreException e) { if (result.isOK()) { result = e.getStatus(); } else { result = new MultiStatus(CUIPlugin.PLUGIN_ID, 0, new IStatus[] { result, e.getStatus() }, NLS.bind(WorkingSetMessages.WSProjConfig_buildProblem, getProject().getName()), null); } } } return result; } /** * Records that we built the specified configuration ID. I will not {@linkplain #needsBuild() need a * build} if the last build configuration is my active configuration. * * @param configID * the configuration that was built (not <code>null</code>) */ protected void built(String configID) { lastBuiltConfigID = configID; } /** * Obtains the configurations of my project that were defined at the time that the snapshot was taken. * * @return my project's configurations, which may be empty if it is not a C/C++ project */ protected Collection<ICConfigurationDescription> getConfigurations() { Collection<ICConfigurationDescription> result; if (projectDescription == null) { result = Collections.emptyList(); } else { result = Arrays.asList(projectDescription.getConfigurations()); } return result; } /** * Obtains the specified configuration of my project as it was defined at the time that the snapshot * was taken. * * @param id * a configuration ID * @return the matching configuration, or <code>null</code> if it did not exist */ protected ICConfigurationDescription getConfiguration(String id) { return (projectDescription == null) ? null : projectDescription.getConfigurationById(id); } } }