| /******************************************************************************* |
| * Copyright (c) 2001, 2004 IBM 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: |
| * IBM Corporation - initial API and implementation |
| * Jens Lukowski/Innoopract - initial renaming/restructuring |
| * |
| *******************************************************************************/ |
| package org.eclipse.wst.sse.ui.preferences; |
| |
| import java.io.File; |
| import java.io.FileNotFoundException; |
| import java.io.FileReader; |
| import java.io.FileWriter; |
| import java.io.IOException; |
| import java.io.Reader; |
| import java.io.Writer; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.StringTokenizer; |
| |
| import javax.xml.parsers.DocumentBuilder; |
| import javax.xml.parsers.DocumentBuilderFactory; |
| import javax.xml.parsers.ParserConfigurationException; |
| import javax.xml.transform.OutputKeys; |
| import javax.xml.transform.Source; |
| import javax.xml.transform.Transformer; |
| import javax.xml.transform.TransformerConfigurationException; |
| import javax.xml.transform.TransformerException; |
| import javax.xml.transform.TransformerFactory; |
| import javax.xml.transform.TransformerFactoryConfigurationError; |
| import javax.xml.transform.dom.DOMSource; |
| import javax.xml.transform.stream.StreamResult; |
| |
| import org.eclipse.wst.sse.core.internal.preferences.PreferenceChangeListener; |
| import org.eclipse.wst.sse.ui.internal.Logger; |
| import org.eclipse.wst.sse.ui.internal.SSEUIMessages; |
| import org.w3c.dom.Document; |
| import org.w3c.dom.Element; |
| import org.w3c.dom.Node; |
| import org.w3c.dom.NodeList; |
| import org.xml.sax.InputSource; |
| import org.xml.sax.SAXException; |
| |
| /** |
| * @deprecated no longer need a special PreferenceManager for our component. |
| * All preferences should be accessible using the base preference |
| * manager. |
| */ |
| public abstract class PreferenceManager { |
| |
| class EmptyNodeList implements NodeList { |
| protected EmptyNodeList() { |
| super(); |
| } |
| |
| public int getLength() { |
| return 0; |
| } |
| |
| public Node item(int param1) { |
| return null; |
| } |
| } |
| |
| /** |
| * The PreferenceRuntimeException is often thrown by methods when a |
| * service we use throws a checked exception, but we want to convert and |
| * treat as a runtime exception. |
| */ |
| class PreferenceRuntimeException extends RuntimeException { |
| /** |
| * Comment for <code>serialVersionUID</code> |
| */ |
| private static final long serialVersionUID = 1L; |
| private Throwable originalException; |
| |
| public PreferenceRuntimeException() { |
| super(); |
| } |
| |
| /** |
| * This form of the constructor is used to wrapper another exception. |
| */ |
| public PreferenceRuntimeException(Throwable t) { |
| this(); |
| originalException = t; |
| } |
| |
| public String getMessage() { |
| String result = super.getMessage(); |
| if ((result != null) && (!result.endsWith("."))) //$NON-NLS-1$ |
| result = result + "."; //$NON-NLS-1$ |
| if (originalException != null) { |
| String embeddedMessage = originalException.getMessage(); |
| embeddedMessage = originalException.getClass().getName() + ": " + originalException.getMessage(); //$NON-NLS-1$ |
| // not all exceptions have messages (e.g. many |
| // NullPointerException) |
| String originalError = SSEUIMessages.PreferenceManager_0; //$NON-NLS-1$ |
| if (result == null) |
| result = ""; //$NON-NLS-1$ |
| if (embeddedMessage != null) |
| result = result + " " + originalError + " " + embeddedMessage; //$NON-NLS-2$//$NON-NLS-1$ |
| else |
| result = result + " " + originalError + " " + originalException.toString(); //$NON-NLS-2$//$NON-NLS-1$ |
| } |
| return result; |
| } |
| |
| public Throwable getOriginalException() { |
| return originalException; |
| } |
| |
| public String toString() { |
| // we don't put super.toString or getClass to "hide" that it was a |
| // SourceEditing exception (otherwise, focus goes on that, |
| // instead of original exception. |
| String message = getMessage(); |
| // message should never be null ... but just in case |
| return (message != null) ? message : super.toString(); |
| } |
| } |
| |
| protected Document document = null; |
| |
| protected String fileName = null; |
| private List preferenceChangeListeners = new ArrayList(1); |
| |
| protected Document _getNewDocumentDOM2() { |
| Document result = null; |
| // settings |
| DocumentBuilder builder = getDocumentBuilder(); |
| result = builder.newDocument(); |
| Element settings = result.createElement(getRootElementName()); |
| result.appendChild(settings); |
| return result; |
| |
| } |
| |
| protected Document _getParsedDocumentDOM2(String filename) { |
| Document result = null; |
| DocumentBuilder builder = getDocumentBuilder(); |
| try { |
| Reader inputReader = new FileReader(getFilename()); |
| InputSource inputSource = new InputSource(inputReader); |
| result = builder.parse(inputSource); |
| } |
| catch (FileNotFoundException e) { |
| // file not found is "ok" ... it'll be created if we return null |
| result = null; |
| } |
| catch (IOException e) { |
| result = null; |
| } |
| catch (SAXException e) { |
| result = null; |
| } |
| |
| return result; |
| |
| } |
| |
| public void addPreferenceChangeListener(PreferenceChangeListener l) { |
| if (!preferenceChangeListeners.contains(l)) |
| preferenceChangeListeners.add(l); |
| } |
| |
| /** |
| * Returns a new document containing the defaults for this manager. This |
| * SHOULD NOT overwrite the actual document stored within this manager, |
| * and while a root element MAY BE created by the DOM implementation, it |
| * is recommended that subclasses NOT RELY upon it being there. |
| * |
| * @return org.w3c.dom.Document |
| */ |
| public Document createDefaultPreferences() { |
| Document txobj = null; |
| txobj = _getNewDocumentDOM2(); |
| return txobj; |
| } |
| |
| protected void firePreferenceChangeListeners() { |
| if (preferenceChangeListeners != null) |
| for (int i = 0; i < preferenceChangeListeners.size(); ++i) |
| ((PreferenceChangeListener) preferenceChangeListeners.get(i)).preferencesChanged(); |
| } |
| |
| /** |
| * |
| * @return Document |
| */ |
| public Document getDocument() { |
| if (document == null) |
| load(); |
| return document; |
| } |
| |
| private DocumentBuilder getDocumentBuilder() { |
| DocumentBuilder result = null; |
| try { |
| result = DocumentBuilderFactory.newInstance().newDocumentBuilder(); |
| } |
| catch (ParserConfigurationException e) { |
| Logger.logException(e); |
| } |
| return result; |
| } |
| |
| /************************************************************************* |
| * Takes a single string of the form "a/b/c" and ensures that that |
| * structure exists below the head element, down through 'c', and returns |
| * a <em>single</em> element 'c'. For multiple elements (such as |
| * multiple <macro> elements contained within a single |
| * <macros> element, full DOM access is required for searching and |
| * child element manipulation. |
| ************************************************************************/ |
| public Element getElement(String name) { |
| if (document == null) |
| load(); |
| if (document != null) |
| return (Element) getNode(getRootElement(), name); |
| else |
| return null; |
| } |
| |
| protected abstract String getFilename(); |
| |
| protected Node getNamedChild(Node parent, String childName) { |
| if (parent == null) { |
| return null; |
| } |
| NodeList childList = parent.getChildNodes(); |
| for (int i = 0; i < childList.getLength(); i++) { |
| if (childList.item(i).getNodeName().equals(childName)) |
| return childList.item(i); |
| } |
| return null; |
| } |
| |
| /************************************************************************* |
| * Takes a single string of the form "a/b/c" and ensures that that |
| * structure exists below the head element, down through 'c', and returns |
| * the element 'c'. |
| ************************************************************************/ |
| public Node getNode(Node node, String name) { |
| StringTokenizer tokenizer = new StringTokenizer(name, "/"); //$NON-NLS-1$ |
| String token = null; |
| while (tokenizer.hasMoreTokens()) { |
| token = tokenizer.nextToken(); |
| if (getNamedChild(node, token) == null) { |
| Document localDocument = node.getOwnerDocument(); |
| node.appendChild(localDocument.createElement(token)); |
| } |
| node = getNamedChild(node, token); |
| } |
| return node; |
| } |
| |
| protected Document getParsedDocument(String filename) { |
| Document result = null; |
| // file name is almost never null, |
| // but can be if preferences are being ran |
| // outside of a workbench application |
| if (filename != null) { |
| File existenceTester = new File(filename); |
| if (!existenceTester.exists()) |
| result = null; |
| else |
| result = _getParsedDocumentDOM2(filename); |
| } |
| return result; |
| |
| } |
| |
| /** |
| * Returns the root element of the current document |
| * |
| * @return org.w3c.dom.Element |
| */ |
| public Node getRootElement() { |
| return getRootElement(getDocument()); |
| } |
| |
| /** |
| * Returns the root element of the current document |
| * |
| * @return org.w3c.dom.Element |
| */ |
| public Node getRootElement(Document doc) { |
| if (doc == null) |
| return null; |
| Node rootElement = doc.getFirstChild(); |
| while (rootElement != null && rootElement.getNodeType() != Node.ELEMENT_NODE && !rootElement.getNodeName().equals(getRootElementName())) { |
| rootElement = rootElement.getNextSibling(); |
| } |
| return rootElement; |
| } |
| |
| /** |
| * The intended name for the root Element of the Document; what is also |
| * listed within the DOCTYPE declaration. |
| * |
| * @return String |
| */ |
| public String getRootElementName() { |
| return "settings"; //$NON-NLS-1$ |
| } |
| |
| public void load() { |
| document = getParsedDocument(getFilename()); |
| |
| if (document == null) { |
| document = createDefaultPreferences(); |
| } |
| } |
| |
| public void removePreferenceChangeListener(PreferenceChangeListener l) { |
| preferenceChangeListeners.remove(l); |
| } |
| |
| public void save() { |
| if (document == null) { |
| document = createDefaultPreferences(); |
| } |
| try { |
| // pa_TODO is this still going to be done like this? |
| FileWriter output = new FileWriter(getFilename()); |
| saveDocument(document, output); |
| output.flush(); |
| output.close(); |
| } |
| catch (IOException e) { |
| Logger.logException("Program Error: PreferenceManager::save. Exception saving preferences ", e); //$NON-NLS-1$ |
| throw new PreferenceRuntimeException(e); |
| } |
| firePreferenceChangeListeners(); |
| } |
| |
| public void saveDocument(Document document, Writer writer) throws IOException { |
| |
| serialize(document, writer); |
| } |
| |
| private void serialize(Document sourceDocument, Writer writer) throws IOException { |
| Source domSource = new DOMSource(sourceDocument); |
| try { |
| Transformer serializer = TransformerFactory.newInstance().newTransformer(); |
| try { |
| serializer.setOutputProperty(OutputKeys.INDENT, "yes"); //$NON-NLS-1$ |
| serializer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4"); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| catch (IllegalArgumentException e) { |
| // unsupported properties |
| } |
| serializer.transform(domSource, new StreamResult(writer)); |
| } |
| catch (TransformerConfigurationException e) { |
| throw new IOException(e.getMessage()); |
| } |
| catch (TransformerFactoryConfigurationError e) { |
| throw new IOException(e.getMessage()); |
| } |
| catch (TransformerException e) { |
| throw new IOException(e.getMessage()); |
| } |
| } |
| |
| } |