1
/*******************************************************************************
2
* Copyright (c) 2009 QNX Software Systems and others.
3
* All rights reserved. This program and the accompanying materials
4
* are made available under the terms of the Eclipse Public License v1.0
5
* which accompanies this distribution, and is available at
6
* http://www.eclipse.org/legal/epl-v10.html
9
* QNX Software Systems - initial API and implementation
10
*******************************************************************************/
12
package org.eclipse.cdt.internal.ui.workingsets;
15
import java.io.FileReader;
16
import java.io.FileWriter;
17
import java.io.IOException;
18
import java.util.Collection;
19
import java.util.HashMap;
20
import java.util.List;
23
import org.eclipse.core.runtime.IPath;
24
import org.eclipse.core.runtime.IProgressMonitor;
25
import org.eclipse.core.runtime.IStatus;
26
import org.eclipse.core.runtime.Status;
27
import org.eclipse.core.runtime.jobs.ISchedulingRule;
28
import org.eclipse.core.runtime.jobs.Job;
29
import org.eclipse.jface.util.IPropertyChangeListener;
30
import org.eclipse.jface.util.PropertyChangeEvent;
31
import org.eclipse.ui.IMemento;
32
import org.eclipse.ui.IWorkingSet;
33
import org.eclipse.ui.IWorkingSetManager;
34
import org.eclipse.ui.XMLMemento;
36
import org.eclipse.cdt.ui.CUIPlugin;
37
import org.eclipse.cdt.ui.newui.CDTPrefUtil;
40
* The purveyor of working set configurations. It provides a current view of the {@linkplain IWorkingSetProxy
41
* working set configurations} defined in the workspace, as well as a working-copy
42
* {@linkplain WorkspaceSnapshot snapshot} of the same for making modifications.
44
* @author Christian W. Damus (cdamus)
49
public class WorkingSetConfigurationManager {
51
static final String TYPE_WORKING_SET_CONFIGS = "org.eclipse.cdt.ui.workingSetConfigurations"; //$NON-NLS-1$
52
static final String KEY_WORKING_SET = "workingSet"; //$NON-NLS-1$
53
static final String ATTR_NAME = "name"; //$NON-NLS-1$
54
static final String KEY_CONFIG = "config"; //$NON-NLS-1$
55
static final String KEY_PROJECT = "project"; //$NON-NLS-1$
56
static final String ATTR_CONFIG = "config"; //$NON-NLS-1$
57
static final String ATTR_FACTORY = "factory"; //$NON-NLS-1$
59
static IWorkingSetManager WS_MGR = CUIPlugin.getDefault().getWorkbench().getWorkingSetManager();
61
private static final WorkingSetConfigurationManager INSTANCE = new WorkingSetConfigurationManager();
63
private Map<String, IWorkingSetProxy> workingSets;
65
private final Object storeLock = new Object();
66
private IMemento store;
68
private final ISchedulingRule saveRule = new ISchedulingRule() {
70
public boolean isConflicting(ISchedulingRule rule) {
74
public boolean contains(ISchedulingRule rule) {
80
* Not instantiable by clients.
82
private WorkingSetConfigurationManager() {
83
store = loadMemento();
84
new WorkingSetChangeTracker();
88
* Obtains the default working set configuration manager.
90
* @return the working set configuration manager
92
public static WorkingSetConfigurationManager getDefault() {
96
private Map<String, IWorkingSetProxy> getWorkingSetMap() {
97
Map<String, IWorkingSetProxy> result;
99
synchronized (storeLock) {
100
if (workingSets == null) {
103
result = workingSets;
110
* Obtains the current immutable view of the specified working set's configurations. These configurations
111
* may be {@linkplain IWorkingSetConfiguration#activate() activated} to apply their settings to the
112
* workspace, but they cannot be modified.
115
* the name of the working set to retrieve
116
* @return the named working set, or <code>null</code> if there is none available by that name
118
public IWorkingSetProxy getWorkingSet(String name) {
119
return getWorkingSetMap().get(name);
123
* Obtains the current immutable view of all available working set configurations. These configurations
124
* may be {@linkplain IWorkingSetConfiguration#activate() activated} to apply their settings to the
125
* workspace, but they cannot be modified.
127
* @return the working set configurations
129
public Collection<IWorkingSetProxy> getWorkingSets() {
130
return getWorkingSetMap().values();
134
* Creates a new mutable snapshot of the current working set configurations. This snapshot accepts
135
* modifications and can be {@linkplain WorkspaceSnapshot#save() saved} to persist the changes.
137
* @return a working-copy of the working set configurations
139
public WorkspaceSnapshot createWorkspaceSnapshot() {
140
return new WorkspaceSnapshot().initialize(getWorkingSetMap());
145
* Loads the working set configurations from storage.
148
* <b>Note</b> that this method must only be called within the <tt>storeLock</tt> monitor.
151
private void load() {
152
workingSets = new java.util.HashMap<String, IWorkingSetProxy>();
154
for (IMemento next : store.getChildren(KEY_WORKING_SET)) {
155
WorkingSetProxy ws = new WorkingSetProxy();
160
workingSets.put(ws.getName(), ws);
167
* Forgets the current view of the working set configurations.
170
* <b>Note</b> that this method must only be called within the <tt>storeLock</tt> monitor.
173
private void clear() {
178
* Saves the working set configurations to storage.
181
* the snapshot to save
183
void save(WorkspaceSnapshot snapshot) {
184
final XMLMemento memento = XMLMemento.createWriteRoot(TYPE_WORKING_SET_CONFIGS);
186
for (IWorkingSetConfigurationElement next : snapshot.getWorkingSets()) {
187
next.saveState(memento.createChild(KEY_WORKING_SET));
194
* Records the specified memento as our new store and asynchronously saves it in a job.
199
private void save(final XMLMemento memento) {
200
synchronized (storeLock) {
205
new Job(WorkingSetMessages.WSConfigManager_save_job) {
212
protected IStatus run(IProgressMonitor monitor) {
214
File file = getStorage();
215
FileWriter writer = null;
217
writer = new FileWriter(file);
218
memento.save(writer);
220
} catch (IOException e) {
221
if (writer != null) {
224
} catch (IOException e2) {
226
CUIPlugin.log(WorkingSetMessages.WSConfigManager_closeFailed, e);
230
file.delete(); // it is corrupt; we won't be able to load it, later
232
CUIPlugin.log(WorkingSetMessages.WSConfigManager_saveFailed, e);
234
return Status.OK_STATUS;
240
* Gets the file in which we persist the working set configurations.
242
* @return the file store
244
private File getStorage() {
245
IPath path = CUIPlugin.getDefault().getStateLocation().append("workingSetConfigs.xml"); //$NON-NLS-1$
246
return path.toFile();
250
* Loads the working set configurations from storage. For compatibility, if the XML file is not available,
251
* we load from the old preference setting format.
253
* @return the working set configuration store
255
private IMemento loadMemento() {
256
IMemento result = null;
258
File file = getStorage();
261
FileReader reader = null;
263
reader = new FileReader(file);
264
result = XMLMemento.createReadRoot(reader);
266
} catch (Exception e) {
269
if (reader != null) {
272
} catch (IOException e2) {
274
CUIPlugin.log(WorkingSetMessages.WSConfigManager_closeFailed, e);
278
CUIPlugin.log(WorkingSetMessages.WSConfigManager_loadFailed, e);
282
if (result == null) {
283
// fake one from the old preference storage format. This also
284
// handles the case of no working set configurations ever being made
285
@SuppressWarnings("deprecation")
286
List<String> configSetStrings = CDTPrefUtil.readConfigSets();
287
result = XMLMemento.createWriteRoot(TYPE_WORKING_SET_CONFIGS);
289
// collect the unordered entries by working set
290
Map<String, IMemento> configMap = new HashMap<String, IMemento>();
291
for (String next : configSetStrings) {
292
String[] bits = next.split(" "); //$NON-NLS-1$
294
if (bits.length >= 2) {
295
String configName = bits[0];
296
String wsName = bits[1];
298
IMemento workingSet = configMap.get(wsName);
299
if (workingSet == null) {
300
workingSet = result.createChild(KEY_WORKING_SET);
301
configMap.put(wsName, workingSet);
304
workingSet.putString(ATTR_NAME, wsName);
306
IMemento config = workingSet.createChild(KEY_CONFIG);
307
config.putString(ATTR_NAME, configName);
309
int limit = bits.length - (bits.length % 2);
310
for (int i = 2; i < limit; i += 2) {
311
IMemento project = config.createChild(KEY_PROJECT);
312
project.putString(ATTR_NAME, bits[i]);
313
project.putString(ATTR_CONFIG, bits[i + 1]);
327
* A working set manager listener that tracks name changes and removals of working sets to keep our
328
* configurations in synch as much as possible. It updates the memento store directly in response to
329
* changes in the working sets.
331
* @author Christian W. Damus (cdamus)
335
private class WorkingSetChangeTracker extends java.util.IdentityHashMap<IWorkingSet, String> implements
336
IPropertyChangeListener {
338
WorkingSetChangeTracker() {
339
for (IWorkingSet next : WS_MGR.getWorkingSets()) {
340
put(next, next.getName());
343
WS_MGR.addPropertyChangeListener(this);
346
public void propertyChange(PropertyChangeEvent event) {
347
String property = event.getProperty();
349
if (IWorkingSetManager.CHANGE_WORKING_SET_NAME_CHANGE.equals(property)) {
350
handleNameChange((IWorkingSet) event.getNewValue());
351
} else if (IWorkingSetManager.CHANGE_WORKING_SET_REMOVE.equals(property)) {
352
handleRemove((IWorkingSet) event.getOldValue());
353
} else if (IWorkingSetManager.CHANGE_WORKING_SET_ADD.equals(property)) {
354
handleAdd((IWorkingSet) event.getNewValue());
358
private void handleNameChange(IWorkingSet workingSet) {
359
synchronized (storeLock) {
360
String oldName = get(workingSet);
361
IMemento wsMemento = null;
362
if (oldName != null) {
363
for (IMemento next : store.getChildren(KEY_WORKING_SET)) {
364
if (oldName.equals(next.getString(ATTR_NAME))) {
371
if (wsMemento != null) {
372
// update the memento with the new name
373
wsMemento.putString(ATTR_NAME, workingSet.getName());
376
XMLMemento newStore = XMLMemento.createWriteRoot(TYPE_WORKING_SET_CONFIGS);
377
newStore.putMemento(store);
379
// save it asynchronously
383
// and update our mapping
384
put(workingSet, workingSet.getName());
388
private void handleRemove(IWorkingSet workingSet) {
389
synchronized (storeLock) {
390
String name = get(workingSet);
392
// remove the memento from the store
393
XMLMemento newStore = XMLMemento.createWriteRoot(TYPE_WORKING_SET_CONFIGS);
394
for (IMemento next : store.getChildren(KEY_WORKING_SET)) {
395
if (!name.equals(next.getString(ATTR_NAME))) {
396
newStore.createChild(KEY_WORKING_SET).putMemento(next);
400
// save asynchronously
404
// and update our mapping
409
private void handleAdd(IWorkingSet workingSet) {
410
synchronized (storeLock) {
411
put(workingSet, workingSet.getName());