1
/*******************************************************************************
2
* Copyright (c) 2008, 2010 Broadcom Corporation 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
* James Blackburn (Broadcom Corp.)
10
*******************************************************************************/
12
package org.eclipse.cdt.internal.core.settings.model.xml2;
14
import java.util.Arrays;
15
import java.util.Collections;
16
import java.util.HashMap;
17
import java.util.LinkedList;
19
import java.util.Queue;
21
import org.eclipse.cdt.core.CCorePlugin;
22
import org.eclipse.cdt.core.settings.model.ICStorageElement;
23
import org.eclipse.cdt.internal.core.settings.model.CProjectDescriptionManager;
24
import org.eclipse.cdt.internal.core.settings.model.CProjectDescriptionStorageManager;
25
import org.eclipse.cdt.internal.core.settings.model.ICProjectDescriptionStorageType;
26
import org.eclipse.cdt.internal.core.settings.model.ICProjectDescriptionStorageType.CProjectDescriptionStorageTypeProxy;
27
import org.eclipse.cdt.internal.core.settings.model.xml.InternalXmlStorageElement;
28
import org.eclipse.cdt.internal.core.settings.model.xml.XmlProjectDescriptionStorage;
29
import org.eclipse.cdt.internal.core.settings.model.xml.XmlStorage;
30
import org.eclipse.core.resources.IContainer;
31
import org.eclipse.core.resources.IFolder;
32
import org.eclipse.core.resources.IProject;
33
import org.eclipse.core.resources.IResource;
34
import org.eclipse.core.resources.IResourceProxy;
35
import org.eclipse.core.resources.IResourceProxyVisitor;
36
import org.eclipse.core.runtime.Assert;
37
import org.eclipse.core.runtime.CoreException;
38
import org.eclipse.core.runtime.NullProgressMonitor;
39
import org.osgi.framework.Version;
40
import org.w3c.dom.Element;
43
* This class extends XmlProjectDescriptionStroage to provide the following
45
* - Configuration Description storage modules are persisted in separate XML
46
* files stored under .csettings in the project root directory. They
47
* are linked into the main .cproject file via "externalCElementFile" element
49
* It is backwards compatible with XmlProjectDescriptionStorage. If it finds a file
50
* with Version less than 5.0, it will delegate to the previous XmlProjectDescriptionStorage
52
* This allows users to more easily version control their CDT project descriptions
53
* @see <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=226457">Bug 226457</a>
55
public class XmlProjectDescriptionStorage2 extends XmlProjectDescriptionStorage {
56
/** Folder Name for storing Project specific configuration settings */
57
static final String STORAGE_FOLDER_NAME = ".csettings"; //$NON-NLS-1$
58
/** Element providing the name of the external C Element file */
59
public static final String EXTERNAL_CELEMENT_KEY = "externalCElementFile"; //$NON-NLS-1$
60
private static final String ID = "id"; //$NON-NLS-1$
62
/** Override the description version, we support version '5.0' files */
63
@SuppressWarnings("hiding")
64
public static final Version STORAGE_DESCRIPTION_VERSION = new Version("5.0"); //$NON-NLS-1$
65
/** The storage type id of this extended project description type */
66
@SuppressWarnings("hiding")
67
public static final String STORAGE_TYPE_ID = CCorePlugin.PLUGIN_ID + ".XmlProjectDescriptionStorage2"; //$NON-NLS-1$
69
/** Map from storageModuleId to modification time stamp */
70
Map<String, Long> modificationMap = Collections.synchronizedMap(new HashMap<String, Long>());
72
public XmlProjectDescriptionStorage2(CProjectDescriptionStorageTypeProxy type, IProject project, Version version) {
73
super(type, project, version);
77
* Check for external modification in the module files in the .csettings directory
79
* @see org.eclipse.cdt.internal.core.settings.model.xml.XmlProjectDescriptionStorage#checkExternalModification()
82
protected synchronized boolean checkExternalModification() {
83
// Check if our parent thinks we need a reload
84
if (super.checkExternalModification())
86
// If not currently loaded, then nothing to do
87
if (getLoadedDescription() == null)
89
final boolean[] needReload = new boolean[] { false };
91
project.getFolder(STORAGE_FOLDER_NAME).accept(new IResourceProxyVisitor() {
92
public boolean visit(IResourceProxy proxy) throws CoreException {
93
if (modificationMap.containsKey(proxy.getName())) {
94
long modStamp = getModificationStamp(proxy.requestResource());
95
if (modificationMap.get(proxy.getName()) != modStamp) {
96
// There may be old storages in here, ensure we don't infinite reload...
97
modificationMap.put(proxy.getName(), modStamp);
98
setCurrentDescription(null, true);
105
} catch (CoreException e) {
106
// STORAGE_FOLDER_NAME doesn't exist... or something went wrong during reload
107
if (project.getFolder(STORAGE_FOLDER_NAME).exists())
108
CCorePlugin.log("XmlProjectDescriptionStorage2: Problem checking for external modification: " + e.getMessage()); //$NON-NLS-1$
110
return needReload[0];
115
* @see org.eclipse.cdt.internal.core.settings.model.xml.XmlProjectDescriptionStorage#createStorage(org.eclipse.core.resources.IContainer, java.lang.String, boolean, boolean, boolean)
118
protected final InternalXmlStorageElement createStorage(IContainer container, String fileName, boolean reCreate, boolean createEmptyIfNotFound, boolean readOnly) throws CoreException {
119
InternalXmlStorageElement el = super.createStorage(container, fileName, reCreate, createEmptyIfNotFound, readOnly);
121
Queue<ICStorageElement> nodesToCheck = new LinkedList<ICStorageElement>();
122
nodesToCheck.addAll(Arrays.asList(el.getChildren()));
123
while (!nodesToCheck.isEmpty()) {
124
ICStorageElement currEl = nodesToCheck.remove();
126
// If not a storageModule element or doesn't have EXTERNAL_CELEMENT_KEY, continue
127
if (!XmlStorage.MODULE_ELEMENT_NAME.equals(currEl.getName())
128
|| currEl.getAttribute(EXTERNAL_CELEMENT_KEY) == null) {
129
nodesToCheck.addAll(Arrays.asList(currEl.getChildren()));
134
// Found -- load the Document pointed to (allows multiple layers of indirection if need be)
135
InternalXmlStorageElement el2 = createStorage(project.getFolder(STORAGE_FOLDER_NAME),
136
currEl.getAttribute(EXTERNAL_CELEMENT_KEY),
137
reCreate, createEmptyIfNotFound, readOnly);
138
// Update the modification stamp
139
modificationMap.put(currEl.getAttribute(EXTERNAL_CELEMENT_KEY),
140
getModificationStamp(project.getFolder(STORAGE_FOLDER_NAME).getFile(currEl.getAttribute(EXTERNAL_CELEMENT_KEY))));
142
ICStorageElement currParent = currEl.getParent();
143
// Get the storageModule element in the new Document
144
ICStorageElement[] childStorages = el2.getChildrenByName(XmlStorage.MODULE_ELEMENT_NAME);
145
Assert.isTrue(childStorages.length == 1, "More than one storageModule in external Document!"); //$NON-NLS-1$
147
// Import the loaded element
148
currParent.importChild(childStorages[0]);
149
// Remove the placeholder
150
currParent.removeChild(currEl);
151
} catch (CoreException e) {
152
// If we're here there was a problem loading csettings when there is a .cproject file.
161
* Serialize the Project Description:
162
* - The .cproject file contains the root XML Elements.
163
* - We serialize the storageModule children of the CConfiguration elements (in the org.eclipse.cdt.settings module)
164
* to separate files in the .csettings directory to prevent unmanageably large XML deltas
165
* Return the modification stamp of the main .cproject file as our super method does
167
* @see org.eclipse.cdt.internal.core.settings.model.xml.XmlProjectDescriptionStorage#serialize(org.eclipse.core.resources.IContainer, java.lang.String, org.eclipse.cdt.core.settings.model.ICStorageElement)
170
protected long serialize(IContainer container, String file, ICStorageElement element) throws CoreException {
171
// If the current loaded version is less than our version, then delegate to parent to handle persisting
172
// i.e. don't auto-upgrade
173
if (version.compareTo(type.version) < 0)
174
return super.serialize(container, file, element);
176
// Copy the original passed in element, as we're going to re-write the children
177
InternalXmlStorageElement copy = copyElement(element, false);
178
// Map containing external CConfiguration elements to be serialized
179
Map<String, InternalXmlStorageElement> externalStorageElements = new HashMap<String, InternalXmlStorageElement>();
181
// Iterate through the initial children
182
for (ICStorageElement el : copy.getChildren()) {
183
// Only interested in root level org.eclipse.cdt.settings storageModules
184
if (!CProjectDescriptionManager.MODULE_ID.equals(el.getAttribute(XmlStorage.MODULE_ID_ATTRIBUTE)))
187
Queue<ICStorageElement> configStorages = new LinkedList<ICStorageElement>();
188
configStorages.addAll(Arrays.asList(el.getChildren()));
189
while (!configStorages.isEmpty()) {
190
InternalXmlStorageElement iEl = (InternalXmlStorageElement)configStorages.remove();
192
// If not a stroageModule element, the just add children ...
193
// e.g. configuration element...
194
if (iEl.getAttribute(XmlStorage.MODULE_ID_ATTRIBUTE) == null) {
195
configStorages.addAll(Arrays.asList(iEl.getChildren()));
199
// Persist this storage Element fileName = configurationID_moduleID
200
String storageId = ((Element)iEl.fElement.getParentNode()).getAttribute(ID) + "_" //$NON-NLS-1$
201
+ iEl.getAttribute(XmlStorage.MODULE_ID_ATTRIBUTE);
203
// create a dummy storage for the element <cproject> held
204
InternalXmlStorageElement newEl = createStorage(project.getFolder(STORAGE_FOLDER_NAME), storageId, false, true, false);
205
newEl.importChild(copyElement(iEl, false));
206
externalStorageElements.put(storageId, newEl);
208
// Clear other attributes and children
210
// Set the external c element key
211
iEl.fElement.setAttribute(EXTERNAL_CELEMENT_KEY, storageId);
215
// Serialize the Root XML document Element (.cproject)
216
long cproject_mod_time = super.serialize(project, ICProjectDescriptionStorageType.STORAGE_FILE_NAME, copy);
218
// Check that the .csettings directory exists and is writable
219
IFolder csettings = project.getFolder(STORAGE_FOLDER_NAME);
221
CProjectDescriptionStorageManager.ensureWritable(csettings);
222
if (!csettings.exists())
223
csettings.create(true, true, new NullProgressMonitor());
224
} catch (CoreException e) {
225
if (!csettings.exists())
229
// Serialize all the external elements
230
for (Map.Entry<String, InternalXmlStorageElement> e : externalStorageElements.entrySet())
231
modificationMap.put(e.getKey(), super.serialize(csettings, e.getKey(), e.getValue()));
233
return cproject_mod_time;
237
protected Version getVersion() {
238
return STORAGE_DESCRIPTION_VERSION;
242
protected String getStorageTypeId() {
243
return STORAGE_TYPE_ID;