/******************************************************************************* * Copyright (c) 2007, 2010 Intel 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 * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Intel Corporation - Initial API and implementation * Markus Schorn (Wind River Systems) * IBM Corporation * James Blackburn (Broadcom Corp.) *******************************************************************************/ package org.eclipse.cdt.internal.core.settings.model.xml; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.lang.ref.Reference; import java.lang.ref.SoftReference; import java.net.URI; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.FactoryConfigurationError; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.eclipse.cdt.core.CCorePlugin; import org.eclipse.cdt.core.settings.model.ICProjectDescription; import org.eclipse.cdt.core.settings.model.ICProjectDescriptionManager; import org.eclipse.cdt.core.settings.model.ICSettingsStorage; import org.eclipse.cdt.core.settings.model.ICStorageElement; import org.eclipse.cdt.core.settings.model.extension.ICProjectConverter; import org.eclipse.cdt.core.settings.model.util.CDataUtil; import org.eclipse.cdt.internal.core.XmlUtil; import org.eclipse.cdt.internal.core.envvar.ContributedEnvironment; import org.eclipse.cdt.internal.core.settings.model.AbstractCProjectDescriptionStorage; import org.eclipse.cdt.internal.core.settings.model.CProjectDescription; import org.eclipse.cdt.internal.core.settings.model.CProjectDescriptionManager; import org.eclipse.cdt.internal.core.settings.model.CProjectDescriptionStorageManager; import org.eclipse.cdt.internal.core.settings.model.ExceptionFactory; import org.eclipse.cdt.internal.core.settings.model.ICProjectDescriptionStorageType; import org.eclipse.cdt.internal.core.settings.model.ICProjectDescriptionStorageType.CProjectDescriptionStorageTypeProxy; import org.eclipse.cdt.internal.core.settings.model.SettingsContext; import org.eclipse.cdt.internal.core.settings.model.SettingsModelMessages; import org.eclipse.core.filesystem.EFS; import org.eclipse.core.filesystem.IFileInfo; import org.eclipse.core.filesystem.IFileStore; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IProjectDescription; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspace; import org.eclipse.core.resources.IWorkspaceRunnable; 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.NullProgressMonitor; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.QualifiedName; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.IJobChangeEvent; import org.eclipse.core.runtime.jobs.ILock; import org.eclipse.core.runtime.jobs.ISchedulingRule; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.core.runtime.jobs.JobChangeAdapter; import org.eclipse.core.runtime.jobs.MultiRule; import org.osgi.framework.Version; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.w3c.dom.ProcessingInstruction; import org.xml.sax.SAXException; /** * This class acts as the (de)serialization of the Xml Project model * * The read-only project description is referenced through a volatile reference * which is updated atomically. * The serializationLock is used to prevent concurrent read & write of the project * description by Eclipse. All Scheduling rules _must_ be acquired before attempting * to lock serializationLock (as it happens the setCProjectDescriptionOperation uses * the Workspace scheduling rule). * * FIXME JBB we should use a more advanced overlay tree in the project * description manager to allow safe concurrent access to the tree -- this will * save both space (memory only needed for deltas) and provide easier access * to the deltas. Having done this we should return a different ICDescriptor for * each thread that requests it. * @see AbstractCProjectDescriptionStorage */ public class XmlProjectDescriptionStorage extends AbstractCProjectDescriptionStorage { public static final int INTERNAL_GET_IGNORE_CLOSE = 1 << 31; private static final String OLD_PROJECT_DESCRIPTION = "cdtproject"; //$NON-NLS-1$ private static final String OLD_CDTPROJECT_FILE_NAME = ".cdtproject"; //$NON-NLS-1$ private static final String OLD_PROJECT_OWNER_ID = "id"; //$NON-NLS-1$ private static final String CONVERTED_CFG_NAME = "convertedConfig"; //$NON-NLS-1$ private static final String CONVERTED_CFG_ID_PREFIX = "converted.config"; //$NON-NLS-1$ /** The version of this project description storage */ public static final Version STORAGE_DESCRIPTION_VERSION = new Version("4.0"); //$NON-NLS-1$ /** The extension point ID of this description storage type */ public static final String STORAGE_TYPE_ID = CCorePlugin.PLUGIN_ID + ".XmlProjectDescriptionStorage"; //$NON-NLS-1$ final static String MODULE_ID = "org.eclipse.cdt.core.settings"; //$NON-NLS-1$ static final String CONFIGURATION = "cconfiguration"; //$NON-NLS-1$ private static final QualifiedName LOAD_FLAG = new QualifiedName(CCorePlugin.PLUGIN_ID, "descriptionLoadded"); //$NON-NLS-1$ public XmlProjectDescriptionStorage(CProjectDescriptionStorageTypeProxy type, IProject project, Version version) { super(type, project, version); } /** * The workspace runnable that actually goes about serializing the project description */ private class DesSerializationRunnable implements IWorkspaceRunnable { private final ICProjectDescription fDes; private final ICStorageElement fElement; /* * See Bug 249951 & Bug 310007 * Notification run with the workspace lock (which clients can't acquire explicitly) * The result is deadlock if: * 1) Notification listener does getProjectDescription (workspaceLock -> serializingLock) * 2) setProjectDescription does IFile write (serializingLock -> workspaceLock) * This workaround stops the periodic notification job while we're persisting the project description * which prevents notification (1) from occurring while we do (2) */ private class NotifyJobCanceller extends JobChangeAdapter { @Override public void aboutToRun(IJobChangeEvent event) { final Job job = event.getJob(); if ("org.eclipse.core.internal.events.NotificationManager$NotifyJob".equals(job.getClass().getName())) { //$NON-NLS-1$ job.cancel(); } } } public DesSerializationRunnable(ICProjectDescription des, ICStorageElement el) { fDes = des; fElement = el; } public void run(IProgressMonitor monitor) throws CoreException { JobChangeAdapter notifyJobCanceller = new NotifyJobCanceller(); try { // See Bug 249951 & Bug 310007 Job.getJobManager().addJobChangeListener(notifyJobCanceller); // Ensure we can check a null-job into the workspace // i.e. if notification is currently in progress wait for it to finish... ResourcesPlugin.getWorkspace().run(new IWorkspaceRunnable() { public void run(IProgressMonitor monitor) throws CoreException { } }, null, IWorkspace.AVOID_UPDATE, null); // end Bug 249951 & Bug 310007 serializingLock.acquire(); projectModificaitonStamp = serialize(fDes.getProject(), ICProjectDescriptionStorageType.STORAGE_FILE_NAME, fElement); ((ContributedEnvironment) CCorePlugin.getDefault().getBuildEnvironmentManager().getContributedEnvironment()).serialize(fDes); } finally { serializingLock.release(); Job.getJobManager().removeJobChangeListener(notifyJobCanceller); } } } /** A soft reference to the read-only project description * Volatile provides a memory barrier in Java 5+ */ private volatile Reference<ICProjectDescription> fProjectDescription = new SoftReference<ICProjectDescription>(null); /** The last modification stamp of the .cproject project description file */ private volatile long projectModificaitonStamp = IResource.NULL_STAMP; /** A lock that is held during project description serialization * This lock is also head during load to prevent a load overlapping * with a concurrent reply (as resource locks aren't used for load...)*/ private final ILock serializingLock = Job.getJobManager().newLock(); @Override public ICSettingsStorage getStorageForElement(ICStorageElement element) throws CoreException { return new XmlStorage((InternalXmlStorageElement)element); } @Override public final ICProjectDescription getProjectDescription(int flags, IProgressMonitor monitor) throws CoreException { ICProjectDescription des = null; boolean write = checkFlags(flags, ICProjectDescriptionManager.GET_WRITABLE); // Only 'load' if the caller hasn't explicitly requested currently loaded config boolean load = !checkFlags(flags, ICProjectDescriptionManager.GET_IF_LOADDED); // Create an empty configuration if the user has requested it boolean empty = checkFlags(flags, ICProjectDescriptionManager.GET_EMPTY_PROJECT_DESCRIPTION); boolean ignoreClose = checkFlags(flags, INTERNAL_GET_IGNORE_CLOSE); boolean create = checkFlags(flags, ICProjectDescriptionManager.GET_CREATE_DESCRIPTION); // set the PROJECT_CREATING flag on the project description boolean creatingState = checkFlags(flags, ICProjectDescriptionManager.PROJECT_CREATING); SettingsContext context = null; des = super.getProjectDescription(flags, monitor); // If no thread local project description then check for the previous loaded one // or load from the .cproject file if (des == null) { boolean released = false; try { // If the description is already loaded and has been modified externally, reload it checkExternalModification(); // Acquire the (de)serializing lock serializingLock.acquire(); if (ignoreClose || project.isOpen()) des = getLoadedDescription(); if (!empty && des == null && load && project.isOpen()) { try { des = loadProjectDescription(project); } catch (CoreException e) { // This isn't an issue as there may not be a project description // file yet } if (des == null) { // TODO: check if conversion needed try { context = new SettingsContext(project); des = getConvertedDescription(context); } catch (CoreException e) { CCorePlugin.log(e); } } if (des != null) { if (setLoaddedDescriptionOnLoad(project, des)) { // Current read-only description loaded, unlock the serializing lock // (as saving conversion below will require acquiring resource scheduling rules...) serializingLock.release(); released = true; if (context != null) saveConversion(project, context, (CProjectDescription) des, new NullProgressMonitor()); fireLoadedEvent(des); des = getLoadedDescription(); } } } } finally { if (!released) serializingLock.release(); } } // Only create a new empty configuration if the caller has requested an empty configuration // or they're creating a configuration and there is no existing configuration found if (empty || (des == null && create)) { if (creatingState && des != null) creatingState = des.isCdtProjectCreating(); try { InternalXmlStorageElement element = createStorage(project, ICProjectDescriptionStorageType.STORAGE_FILE_NAME, false, true, false); return new CProjectDescription(project, new XmlStorage(element), element, false, creatingState); } catch (CoreException e) { CCorePlugin.log(e); } } if (des != null && write) { des = createWritableDescription((CProjectDescription) des); } return des; } /** * Method to check whether the description has been modified externally. * If so the current read-only descriptor is nullified. * It updates the cached modification stamp * @return boolean indicating whether reload is needed */ protected synchronized boolean checkExternalModification() { // If loaded, and we have cached the modification stamp, reload long currentModificationStamp = getModificationStamp(project.getFile(ICProjectDescriptionStorageType.STORAGE_FILE_NAME)); if (projectModificaitonStamp != currentModificationStamp) { setCurrentDescription(null, true); projectModificaitonStamp = currentModificationStamp; return true; } return false; } /** * Gets the modification stamp for the resource. * If the returned value has changed since last call to * {@link #getModificationStamp(IResource)}, then the resource has changed. * @param resource IResource to fetch modification stamp for * @return long modification stamp */ protected long getModificationStamp(IResource resource) { // The modification stamp is based on the ResourceInfo modStamp and file store modification time. Note that // because of bug 160728 subsequent generations of resources may have the same modStamp. Until this is fixed // the suggested solution is to use modStamp + modTime // // Both values are cached in resourceInfo, so this is fast. return resource.getModificationStamp() + resource.getLocalTimeStamp(); } /** * Create a writable version of the description * * @param cache The base CProjectDescription on which the writable copy is to be created * @return CProjectDescription of null on failure */ private CProjectDescription createWritableDescription(CProjectDescription cache) { CProjectDescription des = null; try { InternalXmlStorageElement el = (InternalXmlStorageElement)cache.getRootStorageElement(); el = copyElement(el, false); des = new CProjectDescription(cache, false, new XmlStorage(el), el, cache.isCdtProjectCreating()); fireCopyCreatedEvent(des, cache); } catch (CoreException e) { CCorePlugin.log(e); } return des; } /** * @param project * @param des * @return */ private boolean setLoaddedDescriptionOnLoad(IProject project, ICProjectDescription des) { des.setSessionProperty(LOAD_FLAG, Boolean.TRUE); ICProjectDescription oldDes = getLoadedDescription(); setCurrentDescription(des, true); if (oldDes == null) return true; return oldDes.getSessionProperty(LOAD_FLAG) == null; } /* Sets the current read-only descriptions -- uses the write lock * (non-Javadoc) * @see org.eclipse.cdt.core.settings.model.AbstractCProjectDescriptionStorage#setCurrentDescription(org.eclipse.cdt.core.settings.model.ICProjectDescription, boolean) */ @Override public boolean setCurrentDescription(ICProjectDescription des, boolean overwriteIfExists) { if (!overwriteIfExists && fProjectDescription.get() != null) return false; if (des != null) { if (project.exists() && project.isOpen()) { fProjectDescription = new SoftReference<ICProjectDescription>(des); } else { IStatus status = new Status(IStatus.ERROR, CCorePlugin.PLUGIN_ID, -1, SettingsModelMessages.getString("CProjectDescriptionManager.16"), null); //$NON-NLS-1$ CCorePlugin.log(new CoreException(status)); } } else { fProjectDescription = new SoftReference<ICProjectDescription>(null); } return true; } private Object[] loadProjectDescriptionFromOldstyleStorage() throws CoreException { ICStorageElement rootEl = readOldCDTProjectFile(project); if (rootEl != null) { String ownerId = rootEl.getAttribute(OLD_PROJECT_OWNER_ID); CProjectDescription des = (CProjectDescription) CProjectDescriptionManager.getInstance().createProjectDescription(project, false); String id = CDataUtil.genId(CONVERTED_CFG_ID_PREFIX); des.createConvertedConfiguration(id, CONVERTED_CFG_NAME, rootEl); return new Object[] { ownerId, des }; } return null; } /** * Convert and Load a previous version of the Project Description * * @param project * @param context * @return * @throws CoreException */ private ICProjectDescription getConvertedDescription(SettingsContext context) throws CoreException { Object info[] = loadProjectDescriptionFromOldstyleStorage(); CProjectDescription des = null; String ownerId = null; try { if (info != null) { ownerId = (String) info[0]; des = (CProjectDescription) info[1]; setThreadLocalProjectDesc(des); des.setLoading(true); } IProjectDescription eDes = context.getEclipseProjectDescription(); ICProjectConverter converter = CProjectDescriptionManager.getInstance().getConverter(project, ownerId, des); if (converter != null) { CProjectDescription convertedDes = (CProjectDescription) converter.convertProject(project, eDes, ownerId, des); if (convertedDes != null) { CProjectDescriptionManager.getInstance().checkHandleActiveCfgChange(convertedDes, null, eDes, new NullProgressMonitor()); des = convertedDes; } } if (des != null && des.isValid()) { // TODO: should be set via the CModel operation? InternalXmlStorageElement el = null; context.setEclipseProjectDescription(eDes); try { el = copyElement(des.getRootStorageElement(), false); } catch (CoreException e2) { } des = new CProjectDescription(des, true, new XmlStorage(el), el, des.isCdtProjectCreating()); setThreadLocalProjectDesc(des); des.applyDatas(context); des.doneApplying(); } } finally { setThreadLocalProjectDesc(null); if (des != null) des.setLoading(false); } return des; } private void saveConversion(final IProject proj, final SettingsContext context, CProjectDescription des, IProgressMonitor monitor) { try { context.addWorkspaceRunnable(createDesSerializationRunnable()); } catch (CoreException e1) { CCorePlugin.log(e1); } IWorkspaceRunnable toRun = context.createOperationRunnable(); if (toRun != null) CProjectDescriptionManager.runWspModification(toRun, monitor); } /** * Return the read-only ICProjectDescription * in a thread-safe manner */ public ICProjectDescription getLoadedDescription() { return fProjectDescription.get(); } /** * The internal method that actually causes the CProjectDescription to be created from an external storage. * @param project * @return the loaded ICProjectDescription * @throws CoreException */ protected ICProjectDescription loadProjectDescription(IProject project) throws CoreException { try { // Ensure that there isn't a write to the project description occurring concurrently serializingLock.acquire(); // Check if the description has already been loaded if (!checkExternalModification() && getLoadedDescription() != null) return getLoadedDescription(); // Don't log core exceptions caused by .cproject file not exists. Leave that to caller InternalXmlStorageElement storage = createStorage(project, ICProjectDescriptionStorageType.STORAGE_FILE_NAME, true, false, false); try { // Update the modification stamp projectModificaitonStamp = getModificationStamp(project.getFile(ICProjectDescriptionStorageType.STORAGE_FILE_NAME)); CProjectDescription des = new CProjectDescription(project, new XmlStorage(storage), storage, true, false); try { setThreadLocalProjectDesc(des); des.loadDatas(); des.doneLoading(); } finally { setThreadLocalProjectDesc(null); } return des; } catch (CoreException e) { // XmlStorage constructor does sanity checking of the project xml storage element, ensure that errors here are logged CCorePlugin.log(e); throw e; } } finally { serializingLock.release(); } } @Override public IWorkspaceRunnable createDesSerializationRunnable() throws CoreException { CProjectDescription des = (CProjectDescription)getLoadedDescription(); if (des == null) // This won't happen because CModelOperation has a handle on the current read-only description throw ExceptionFactory.createCoreException("No read-only Project Description found! Project: " + project.getName()); //$NON-NLS-1$ final ICStorageElement element = des.getRootStorageElement(); IWorkspaceRunnable r = new DesSerializationRunnable(des, element); return r; } /** * Convert an Xml based ICStorageElement to an ByteArrayOutputStream */ private ByteArrayOutputStream write(ICStorageElement element) throws CoreException { Document doc = ((InternalXmlStorageElement) element).fElement.getOwnerDocument(); XmlUtil.prettyFormat(doc); ByteArrayOutputStream stream = new ByteArrayOutputStream(); try { Transformer transformer = TransformerFactory.newInstance().newTransformer(); transformer.setOutputProperty(OutputKeys.METHOD, "xml"); //$NON-NLS-1$ transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); //$NON-NLS-1$ transformer.setOutputProperty(OutputKeys.INDENT, "yes"); //$NON-NLS-1$ DOMSource source = new DOMSource(doc); StreamResult result = new StreamResult(stream); transformer.transform(source, result); return stream; } catch (TransformerConfigurationException e) { throw ExceptionFactory.createCoreException(e); } catch (TransformerException e) { throw ExceptionFactory.createCoreException(e); } } /** * The method that *actually* performs the serialization of the settings... * * @param container - the folder in which file is to be serialized * @param file - the name of the file to serialize the storage element tree into * @param element - read-only ICStorageElement tree to be serialized (no need to lock) * @throws CoreException * @return long modification stamp of the file in the container */ protected long serialize(IContainer container, String file, ICStorageElement element) throws CoreException { try { final IFile projectFile = container.getFile(new Path(file)); final ISchedulingRule rule = MultiRule.combine(new ISchedulingRule[] { ResourcesPlugin.getWorkspace().getRuleFactory().modifyRule(projectFile), ResourcesPlugin.getWorkspace().getRuleFactory().createRule(projectFile), ResourcesPlugin.getWorkspace().getRuleFactory().deleteRule(projectFile) }); String utfString; ByteArrayOutputStream stream = null; try { // Get the ProjectDescription as a utf-8 string stream = write(element); utfString = stream.toString("UTF-8"); //$NON-NLS-1$ } finally { if (stream != null) stream.close(); // Cleanup the stream } try { // Lock the projectFile Job.getJobManager().beginRule(rule, null); // Ensure the file is writable CProjectDescriptionStorageManager.ensureWritable(projectFile); if (projectFile.exists()) { try { projectFile.setContents(new ByteArrayInputStream(utfString.getBytes("UTF-8")), IResource.FORCE, new NullProgressMonitor()); //$NON-NLS-1$ } catch (CoreException e) { if (projectFile.getLocation().toFile().isHidden()) { String os = System.getProperty("os.name"); //$NON-NLS-1$ if (os != null && os.startsWith("Win")) { //$NON-NLS-1$ projectFile.delete(true, null); projectFile.create(new ByteArrayInputStream(utfString.getBytes("UTF-8")), IResource.FORCE, new NullProgressMonitor()); //$NON-NLS-1$ CCorePlugin.log(e.getLocalizedMessage() + "\n** Error occured because of file status <hidden>." + //$NON-NLS-1$ "\n** This status is disabled now, to allow writing."); //$NON-NLS-1$ } else throw (e); } else throw (e); } } else { projectFile.create(new ByteArrayInputStream(utfString.getBytes("UTF-8")), IResource.FORCE, new NullProgressMonitor()); //$NON-NLS-1$ } return getModificationStamp(projectFile); } finally { Job.getJobManager().endRule(rule); } } catch (IOException e) { throw ExceptionFactory.createCoreException(e); } } private ICStorageElement readOldCDTProjectFile(IProject project) throws CoreException { ICStorageElement storage = null; try { DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); Document doc = null; InputStream stream = getSharedProperty(project, OLD_CDTPROJECT_FILE_NAME); if (stream != null) { doc = builder.parse(stream); NodeList nodeList = doc.getElementsByTagName(OLD_PROJECT_DESCRIPTION); if (nodeList != null && nodeList.getLength() > 0) { Node node = nodeList.item(0); storage = new InternalXmlStorageElement((Element) node, false); } } } catch (ParserConfigurationException e) { throw ExceptionFactory.createCoreException(e); } catch (SAXException e) { throw ExceptionFactory.createCoreException(e); } catch (IOException e) { throw ExceptionFactory.createCoreException(e); } return storage; } /** * This method returns an ICStorageElement from a given Xml file filename in the container * container * * @param container * @param fileName * @param reCreate * @param createEmptyIfNotFound * @param readOnly * @return InternalXmlStorageElement representing the particular storage * @throws CoreException */ protected InternalXmlStorageElement createStorage(IContainer container, String fileName, boolean reCreate, boolean createEmptyIfNotFound, boolean readOnly) throws CoreException{ try { DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); Document doc = null; Element element = null; InputStream stream = null; if(reCreate){ try{ stream = getSharedProperty(container, fileName); if(stream != null){ doc = builder.parse(stream); // Get the first element in the project file Node rootElement = doc.getFirstChild(); if (rootElement.getNodeType() != Node.PROCESSING_INSTRUCTION_NODE) { throw ExceptionFactory.createCoreException(SettingsModelMessages.getString("CProjectDescriptionManager.7")); //$NON-NLS-1$ } else { // Make sure that the version is compatible with the manager String fileVersion = rootElement.getNodeValue(); Version version = new Version(fileVersion); if (getVersion().compareTo(version) < 0) { throw ExceptionFactory.createCoreException(SettingsModelMessages.getString("CProjectDescriptionManager.8")); //$NON-NLS-1$ } } // Now get the project root element (there should be only one) NodeList nodes = doc.getElementsByTagName(ICProjectDescriptionStorageType.STORAGE_ROOT_ELEMENT_NAME); if (nodes.getLength() == 0) throw ExceptionFactory.createCoreException(SettingsModelMessages.getString("CProjectDescriptionManager.9")); //$NON-NLS-1$ Node node = nodes.item(0); if(node.getNodeType() != Node.ELEMENT_NODE) throw ExceptionFactory.createCoreException(SettingsModelMessages.getString("CProjectDescriptionManager.10")); //$NON-NLS-1$ element = (Element)node; } else if(!createEmptyIfNotFound){ throw ExceptionFactory.createCoreException(SettingsModelMessages.getString("CProjectDescriptionManager.11") + fileName); //$NON-NLS-1$ } } catch (FactoryConfigurationError e) { if(!createEmptyIfNotFound) throw ExceptionFactory.createCoreException(e.getLocalizedMessage()); } catch (SAXException e) { if(!createEmptyIfNotFound) throw ExceptionFactory.createCoreException(e); } catch (IOException e) { if(!createEmptyIfNotFound) throw ExceptionFactory.createCoreException(e); } finally { if(stream != null){ try { stream.close(); } catch (IOException e) { } } } } if(element == null) { doc = builder.newDocument(); ProcessingInstruction instruction = doc.createProcessingInstruction(ICProjectDescriptionStorageType.STORAGE_VERSION_NAME, getVersion().toString()); doc.appendChild(instruction); element = doc.createElement(ICProjectDescriptionStorageType.STORAGE_ROOT_ELEMENT_NAME); element.setAttribute(ICProjectDescriptionStorageType.STORAGE_TYPE_ATTRIBUTE, getStorageTypeId()); doc.appendChild(element); } return new InternalXmlStorageElement(element, null, false, readOnly); } catch (ParserConfigurationException e) { throw ExceptionFactory.createCoreException(e); } } /** * @return the maximum version supported by this description storage */ protected Version getVersion() { return STORAGE_DESCRIPTION_VERSION; } /** * @return Return the storage type id for this storage */ protected String getStorageTypeId() { return STORAGE_TYPE_ID; } /** * Return an input stream for the given file in the provided container * @param container * @param key * @return InputStream * @throws CoreException on failure */ public InputStream getSharedProperty(IContainer container, String key) throws CoreException { InputStream stream = null; final IFile rscFile = container.getFile(new Path(key)); if (rscFile.exists()) { try { stream = rscFile.getContents(true); } catch (CoreException e) { // try refreshing final Throwable[] t = new Throwable[1]; Job job = CProjectDescriptionManager.runWspModification(new IWorkspaceRunnable() { public void run(IProgressMonitor monitor) throws CoreException { try { rscFile.refreshLocal(IResource.DEPTH_ZERO, null); } catch (Exception e) { t[0] = e; } } }, rscFile, new NullProgressMonitor()); // if refresh was performed "inline" without job scheduled if (job == null) { // if no exceptions occured if (t[0] == null) { // try get contents stream = rscFile.getContents(); } else { // refresh failed if (t[0] instanceof CoreException) throw (CoreException) t[0]; throw e; } } else { throw e; } } } else { // FIXME JBB remove? // when a project is imported, we get a first delta for the addition // of the .project, but the .classpath is not accessible // so default to using java.io.File // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=96258 URI location = rscFile.getLocationURI(); if (location != null) { IFileStore file = EFS.getStore(location); IFileInfo info = null; if (file != null) { info = file.fetchInfo(); if (info != null && info.exists()) stream = file.openInputStream(EFS.NONE, null); } } } return stream; } @Override public void projectCloseRemove() { super.projectCloseRemove(); setCurrentDescription(null, true); } @Override public void projectMove(IProject newProject) { super.projectMove(newProject); // FIXME JBB From ResourceChangeHandler. // Why do the project description and configurations need to know // their project? They should ask their ProjectDescriptionStorage CProjectDescription desc = (CProjectDescription)fProjectDescription.get(); if (desc != null) { desc.updateProject(newProject); } } /** * Return an Xml element copy based on a passed in InternalXmlStorageElement * * @param el * @return Xml element based on a passed in xml ICStorageElement (InternalXmlStorageElement) * @throws CoreException */ public Element createXmlElementCopy(InternalXmlStorageElement el) throws CoreException { try { DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); Document doc = builder.newDocument(); Element newXmlEl = null; synchronized (doc) { synchronized (el.fLock) { if (el.fElement.getParentNode().getNodeType() == Node.DOCUMENT_NODE) { Document baseDoc = el.fElement.getOwnerDocument(); NodeList list = baseDoc.getChildNodes(); for (int i = 0; i < list.getLength(); i++) { Node node = list.item(i); node = importAddNode(doc, node); if (node.getNodeType() == Node.ELEMENT_NODE && newXmlEl == null) { newXmlEl = (Element) node; } } } else { newXmlEl = (Element) importAddNode(doc, el.fElement); } return newXmlEl; }} } catch (ParserConfigurationException e) { throw ExceptionFactory.createCoreException(e); } catch (FactoryConfigurationError e) { throw ExceptionFactory.createCoreException(e); } } @Override public InternalXmlStorageElement copyElement(ICStorageElement el, boolean readOnly) throws CoreException { InternalXmlStorageElement internalEl = (InternalXmlStorageElement)el; Element newXmlEl = createXmlElementCopy(internalEl); return new InternalXmlStorageElement(newXmlEl, internalEl.getParent(), internalEl.getAttributeFilters(), internalEl.getChildFilters(), readOnly); } private Node importAddNode(Document doc, Node node) { if (node.getOwnerDocument().equals(doc)) { node = node.cloneNode(true); } else { node = doc.importNode(node, true); } return doc.appendChild(node); } }