/******************************************************************************* * 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 * James Blackburn (Broadcom Corp.) *******************************************************************************/ package org.eclipse.cdt.internal.core.settings.model.xml; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import org.eclipse.cdt.core.settings.model.ICSettingsStorage; import org.eclipse.cdt.core.settings.model.ICStorageElement; import org.eclipse.cdt.internal.core.settings.model.ExceptionFactory; import org.eclipse.core.runtime.CoreException; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; /** * Concrete implementation of ICSettingsStorage backed by an XML document * * ICStorageElements are stored in the tree under a storageModule element. * (This class was previously called CStorage) * TODO JBB Make this independent of the Xml Element */ public class XmlStorage implements ICSettingsStorage { public static final String MODULE_ELEMENT_NAME = "storageModule"; //$NON-NLS-1$ public static final String MODULE_ID_ATTRIBUTE = "moduleId"; //$NON-NLS-1$ // Lock to prevent concurrent access to XML DOM which isn't thread-safe for read (Bug 319245) final Object fLock; public Element fElement; private Map<String, InternalXmlStorageElement> fStorageElementMap = new HashMap<String, InternalXmlStorageElement>(); private volatile boolean fChildrenInited; private boolean fIsReadOnly; private boolean fIsDirty; public XmlStorage(Element element, boolean isReadOnly){ fElement = element; fLock = element.getOwnerDocument(); fIsReadOnly = isReadOnly; } public XmlStorage(InternalXmlStorageElement element) throws CoreException { fElement = element.fElement; fLock = fElement.getOwnerDocument(); fIsReadOnly = element.isReadOnly(); element.storageCreated(this); sanityCheck(element); } /** * Check that the XmlStorageElement on which this SettingsStorage * is based doesn't have any attributes, values or children which * are invalid for a settings storage. * @param element * @throws CoreException */ public void sanityCheck(ICStorageElement element) throws CoreException { if (element.getValue() != null && element.getValue().trim().length() > 0) throw ExceptionFactory.createCoreException("XmlStorage '" + element.getName() + "' has unexpected child Value: " + element.getValue()); //$NON-NLS-1$ //$NON-NLS-2$ for (ICStorageElement child : element.getChildren()) { if (!MODULE_ELEMENT_NAME.equals(child.getName())) throw ExceptionFactory.createCoreException("XmlStorage '" + element.getName() + "' has unexpected child element: " + child.getName()); //$NON-NLS-1$ //$NON-NLS-2$ if (child.getAttribute(MODULE_ID_ATTRIBUTE) == null) throw ExceptionFactory.createCoreException("XmlStorage '" + element.getName() + "' has storageModule child without moduleId"); //$NON-NLS-1$ //$NON-NLS-2$ } } public boolean isReadOnly(){ return fIsReadOnly; } /** * Initialize the set of storageModules of this XmlStorage */ private void initChildren(){ if(fChildrenInited) return; synchronized (fLock) { if (fChildrenInited) return; NodeList children = fElement.getChildNodes(); int size = children.getLength(); for(int i = 0; i < size; i++){ Node node = children.item(i); if(node.getNodeType() != Node.ELEMENT_NODE) continue; if(!MODULE_ELEMENT_NAME.equals(node.getNodeName())) continue; Element element = (Element)node; String moduleId = element.getAttribute(MODULE_ID_ATTRIBUTE).trim(); if(moduleId.length() == 0) continue; createAddStorageElement(moduleId, element); } fChildrenInited = true; } } private InternalXmlStorageElement createAddStorageElement(String id, Element element){ InternalXmlStorageElement se = createStorageElement(element, fIsReadOnly); fStorageElementMap.put(id, se); return se; } public static InternalXmlStorageElement createStorageElement(Element el, boolean isReadOnly){ return new InternalXmlStorageElement(el, null, new String[]{MODULE_ID_ATTRIBUTE}, null, isReadOnly); } // public ICStorageElement getStorage(String id){ // return getStorage(id, true); // } public boolean containsStorage(String id) throws CoreException { return getStorage(id, false) != null; } public ICStorageElement importStorage(String id, ICStorageElement el) throws UnsupportedOperationException { if(fIsReadOnly) throw ExceptionFactory.createIsReadOnlyException(); // Remove existing storage with this ID removeStorage(id); // Create the storage element for import synchronized (fLock) { Document thisDoc = fElement.getOwnerDocument(); Element newEl = thisDoc.createElement(MODULE_ELEMENT_NAME); fIsDirty = true; if (el instanceof XmlStorageElement) { // If we're importing an XmlStorageElement use XML methods XmlStorageElement xmlStEl = (XmlStorageElement)el; synchronized (xmlStEl.fLock) { Element xmlEl = xmlStEl.fElement; Document otherDoc = xmlEl.getOwnerDocument(); if(!thisDoc.equals(otherDoc)){ xmlEl = (Element)thisDoc.importNode(xmlEl, true); } NodeList nl = xmlEl.getChildNodes(); for(int i = 0; i < nl.getLength(); i++){ Node child = nl.item(i).cloneNode(true); newEl.appendChild(child); } newEl = (Element)fElement.appendChild(newEl); newEl.setAttribute(MODULE_ID_ATTRIBUTE, id); return createAddStorageElement(id, newEl); } } else { // Otherwise importing generic ICStorageElement ICStorageElement storageEl = getStorage(id, true); for (String attrName: el.getAttributeNames()) storageEl.setAttribute(attrName, el.getAttribute(attrName)); for (ICStorageElement child: el.getChildren()) storageEl.importChild(child); return storageEl; } } } public ICStorageElement getStorage(String id, boolean create){ initChildren(); InternalXmlStorageElement se = fStorageElementMap.get(id); if(se == null && create){ // if(fIsReadOnly) // throw ExceptionFactory.createIsReadOnlyException(); fIsDirty = true; synchronized (fLock) { Document doc = fElement.getOwnerDocument(); Element child = createStorageXmlElement(doc, id); fElement.appendChild(child); se = createAddStorageElement(id, child); } } return se; } public static Element createStorageXmlElement(Document doc, String storageId){ Element child = doc.createElement(MODULE_ELEMENT_NAME); child.setAttribute(MODULE_ID_ATTRIBUTE, storageId); return child; } public void removeStorage(String id){ initChildren(); InternalXmlStorageElement se = fStorageElementMap.remove(id); if(se != null){ if(fIsReadOnly) throw ExceptionFactory.createIsReadOnlyException(); synchronized (fLock) { synchronized (se.fLock){ fIsDirty = true; Node nextSibling = se.fElement.getNextSibling(); fElement.removeChild(se.fElement); if (nextSibling != null && nextSibling.getNodeType() == Node.TEXT_NODE) { String value = nextSibling.getNodeValue(); if (value != null && value.trim().length() == 0) { // remove whitespace fElement.removeChild(nextSibling); } } }} } } public boolean isModified(){ if(fIsDirty) return true; for(Iterator<InternalXmlStorageElement> iter = fStorageElementMap.values().iterator(); iter.hasNext();){ InternalXmlStorageElement el = iter.next(); if(el.isModified()) return true; } return false; } public void setReadOnly(boolean readOnly, boolean keepModify){ fIsReadOnly = readOnly; fIsDirty &= keepModify; for(Iterator<InternalXmlStorageElement> iter = fStorageElementMap.values().iterator(); iter.hasNext();){ InternalXmlStorageElement el = iter.next(); el.setReadOnly(readOnly, keepModify); } } public void setDirty(boolean isDirty){ fIsDirty = isDirty; if(!fIsDirty){ for(Iterator<InternalXmlStorageElement> iter = fStorageElementMap.values().iterator(); iter.hasNext();){ InternalXmlStorageElement el = iter.next(); el.setDirty(false); } } } public void save() throws CoreException { throw new UnsupportedOperationException(); } }