/*******************************************************************************
* Copyright (c) 2000, 2004 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.vfny.geoserver.issues;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import java.net.URL;
import java.util.ArrayList;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Result;
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.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
/**
* This class represents the default implementation of the
* <code>IMemento</code> interface.
* <p>
* This class is not intended to be extended by clients.
* </p>
* This class was adapted by Q.Anderson to be used in geoserver to enable
* xml based persistance of any memento passed to the issues service. Methods
* to save and load were added for files and more importantly strings.
*
* @see IMemento
*/
public final class XMLMemento implements IMemento {
private Document factory;
private Element element;
/**
* Creates a <code>Document</code> from the <code>Reader</code>
* and returns a memento on the first <code>Element</code> for reading
* the document.
* <p>
* Same as calling createReadRoot(reader, null)
* </p>
*
* @param reader the <code>Reader</code> used to create the memento's document
* @return a memento on the first <code>Element</code> for reading the document
* @throws <code>WorkbenchException</code> if IO problems, invalid format, or no element.
*/
public static XMLMemento createReadRoot(Reader reader)
throws Exception {
return createReadRoot(reader, null);
}
/**
* Creates a <code>Document</code> from the <code>Reader</code>
* and returns a memento on the first <code>Element</code> for reading
* the document.
*
* @param reader the <code>Reader</code> used to create the memento's document
* @param baseDir the directory used to resolve relative file names
* in the XML document. This directory must exist and include the
* trailing separator. The directory format, including the separators,
* must be valid for the platform. Can be <code>null</code> if not
* needed.
* @return a memento on the first <code>Element</code> for reading the document
* @throws <code>WorkbenchException</code> if IO problems, invalid format, or no element.
*/
public static XMLMemento createReadRoot(Reader reader, String baseDir)
throws Exception {
String errorMessage = null;
Exception exception = null;
try {
DocumentBuilderFactory factory = DocumentBuilderFactory
.newInstance();
DocumentBuilder parser = factory.newDocumentBuilder();
InputSource source = new InputSource(reader);
if (baseDir != null)
source.setSystemId(baseDir);
Document document = parser.parse(source);
NodeList list = document.getChildNodes();
for (int i = 0; i < list.getLength(); i++) {
Node node = list.item(i);
if (node instanceof Element)
return new XMLMemento(document, (Element) node);
}
} catch (ParserConfigurationException e) {
exception = e;
errorMessage = e.getMessage(); //$NON-NLS-1$
} catch (IOException e) {
exception = e;
errorMessage = e.getMessage(); //$NON-NLS-1$
} catch (SAXException e) {
exception = e;
errorMessage = e.getMessage(); //$NON-NLS-1$
}
String problemText = null;
if (exception != null)
problemText = exception.getMessage();
if (problemText == null || problemText.length() == 0)
problemText = errorMessage != null ? errorMessage
: "XMLMemento.noElement"; //$NON-NLS-1$
throw new Exception(problemText, exception);
}
/**
* Returns a root memento for writing a document.
*
* @param type the element node type to create on the document
* @return the root memento for writing a document
*/
public static XMLMemento createWriteRoot(String type) {
Document document;
try {
document = DocumentBuilderFactory.newInstance()
.newDocumentBuilder().newDocument();
Element element = document.createElement(type);
document.appendChild(element);
return new XMLMemento(document, element);
} catch (ParserConfigurationException e) {
throw new Error(e);
}
}
/**
* Creates a memento for the specified document and element.
* <p>
* Clients should use <code>createReadRoot</code> and
* <code>createWriteRoot</code> to create the initial
* memento on a document.
* </p>
*
* @param document the document for the memento
* @param element the element node for the memento
*/
public XMLMemento(Document document, Element element) {
super();
this.factory = document;
this.element = element;
}
/* (non-Javadoc)
* Method declared in IMemento.
*/
public IMemento createChild(String type) {
Element child = factory.createElement(type);
element.appendChild(child);
return new XMLMemento(factory, child);
}
/* (non-Javadoc)
* Method declared in IMemento.
*/
public IMemento createChild(String type, String id) {
Element child = factory.createElement(type);
child.setAttribute(TAG_ID, id == null ? "" : id); //$NON-NLS-1$
element.appendChild(child);
return new XMLMemento(factory, child);
}
/* (non-Javadoc)
* Method declared in IMemento.
*/
public IMemento copyChild(IMemento child) {
Element childElement = ((XMLMemento) child).element;
Element newElement = (Element) factory.importNode(childElement, true);
element.appendChild(newElement);
return new XMLMemento(factory, newElement);
}
/* (non-Javadoc)
* Method declared in IMemento.
*/
public IMemento getChild(String type) {
// Get the nodes.
NodeList nodes = element.getChildNodes();
int size = nodes.getLength();
if (size == 0)
return null;
// Find the first node which is a child of this node.
for (int nX = 0; nX < size; nX++) {
Node node = nodes.item(nX);
if (node instanceof Element) {
Element element = (Element) node;
if (element.getNodeName().equals(type))
return new XMLMemento(factory, element);
}
}
// A child was not found.
return null;
}
/* (non-Javadoc)
* Method declared in IMemento.
*/
public IMemento[] getChildren(String type) {
// Get the nodes.
NodeList nodes = element.getChildNodes();
int size = nodes.getLength();
if (size == 0)
return new IMemento[0];
// Extract each node with given type.
ArrayList list = new ArrayList(size);
for (int nX = 0; nX < size; nX++) {
Node node = nodes.item(nX);
if (node instanceof Element) {
Element element = (Element) node;
if (element.getNodeName().equals(type))
list.add(element);
}
}
// Create a memento for each node.
size = list.size();
IMemento[] results = new IMemento[size];
for (int x = 0; x < size; x++) {
results[x] = new XMLMemento(factory, (Element) list.get(x));
}
return results;
}
/* (non-Javadoc)
* Method declared in IMemento.
*/
public Float getFloat(String key) {
Attr attr = element.getAttributeNode(key);
if (attr == null)
return null;
String strValue = attr.getValue();
try {
return new Float(strValue);
} catch (NumberFormatException e) {
return null;
}
}
/* (non-Javadoc)
* Method declared in IMemento.
*/
public String getID() {
return element.getAttribute(TAG_ID);
}
/* (non-Javadoc)
* Method declared in IMemento.
*/
public Integer getInteger(String key) {
Attr attr = element.getAttributeNode(key);
if (attr == null)
return null;
String strValue = attr.getValue();
try {
return new Integer(strValue);
} catch (NumberFormatException e) {
return null;
}
}
/* (non-Javadoc)
* Method declared in IMemento.
*/
public String getString(String key) {
Attr attr = element.getAttributeNode(key);
if (attr == null)
return null;
return attr.getValue();
}
/* (non-Javadoc)
* Method declared in IMemento.
*/
public String getTextData() {
Text textNode = getTextNode();
if (textNode != null) {
return textNode.getData();
} else {
return null;
}
}
/**
* Returns the Text node of the memento. Each memento is allowed only
* one Text node.
*
* @return the Text node of the memento, or <code>null</code> if
* the memento has no Text node.
*/
private Text getTextNode() {
// Get the nodes.
NodeList nodes = element.getChildNodes();
int size = nodes.getLength();
if (size == 0)
return null;
for (int nX = 0; nX < size; nX++) {
Node node = nodes.item(nX);
if (node instanceof Text) {
return (Text) node;
}
}
// a Text node was not found
return null;
}
/**
* Places the element's attributes into the document.
*/
private void putElement(Element element) {
NamedNodeMap nodeMap = element.getAttributes();
int size = nodeMap.getLength();
for (int i = 0; i < size; i++) {
Attr attr = (Attr) nodeMap.item(i);
putString(attr.getName(), attr.getValue());
}
NodeList nodes = element.getChildNodes();
size = nodes.getLength();
for (int i = 0; i < size; i++) {
Node node = nodes.item(i);
if (node instanceof Element) {
XMLMemento child = (XMLMemento) createChild(node.getNodeName());
child.putElement((Element) node);
}
}
}
/* (non-Javadoc)
* Method declared in IMemento.
*/
public void putFloat(String key, float f) {
element.setAttribute(key, String.valueOf(f));
}
/* (non-Javadoc)
* Method declared in IMemento.
*/
public void putInteger(String key, int n) {
element.setAttribute(key, String.valueOf(n));
}
/* (non-Javadoc)
* Method declared in IMemento.
*/
public void putMemento(IMemento memento) {
putElement(((XMLMemento) memento).element);
}
/* (non-Javadoc)
* Method declared in IMemento.
*/
public void putString(String key, String value) {
if (value == null)
return;
element.setAttribute(key, value);
}
/* (non-Javadoc)
* Method declared in IMemento.
*/
public void putTextData(String data) {
Text textNode = getTextNode();
if (textNode == null) {
textNode = factory.createTextNode(data);
element.appendChild(textNode);
} else {
textNode.setData(data);
}
}
/**
* Saves this memento's document current values to the
* specified writer.
*
* @param writer the writer used to save the memento's document
* @throws IOException if there is a problem serializing the document to the stream.
*/
public void save(Writer writer) throws IOException {
Result result = new StreamResult(writer);
Source source = new DOMSource(factory);
try {
Transformer transformer = TransformerFactory.newInstance()
.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes"); //$NON-NLS-1$
transformer.setOutputProperty(OutputKeys.METHOD, "xml"); //$NON-NLS-1$
transformer.transform(source, result);
} catch (TransformerConfigurationException e) {
throw (IOException) (new IOException().initCause(e));
} catch (TransformerException e) {
throw (IOException) (new IOException().initCause(e));
}
}
/**
* Save this Memento to a Writer.
*/
public void save(OutputStream os) throws IOException {
Result result = new StreamResult(os);
Source source = new DOMSource(factory);
try {
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes"); //$NON-NLS-1$
transformer.setOutputProperty(OutputKeys.METHOD, "xml"); //$NON-NLS-1$
transformer.transform(source, result);
} catch (Exception e) {
throw (IOException) (new IOException().initCause(e));
}
}
/**
* Saves the memento to the given file.
*
* @param filename java.lang.String
* @exception java.io.IOException
*/
public void saveToFile(String filename) throws IOException {
Writer w = null;
try {
w = new FileWriter(filename);
save(w);
} catch (IOException e) {
throw e;
} catch (Exception e) {
throw new IOException(e.getLocalizedMessage());
} finally {
if (w != null) {
try {
w.close();
} catch (Exception e) { }
}
}
}
public String saveToString() throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
save(out);
return out.toString("UTF-8");
}
/**
* Loads a memento from the given filename.
*
* @param filename java.lang.String
* @return org.eclipse.ui.IMemento
* @exception java.io.IOException
*/
public static IMemento loadMemento(String filename) throws Exception {
return XMLMemento.createReadRoot(new FileReader(filename));
}
/**
* Loads a memento from the given filename.
*
* @param url java.net.URL
* @return org.eclipse.ui.IMemento
* @exception java.io.IOException
*/
public static IMemento loadMemento(URL url) throws Exception {
return XMLMemento.createReadRoot(new InputStreamReader(url.openStream()));
}
/**
* Loads a memento from the given string.
*
* @param url java.net.URL
* @return org.eclipse.ui.IMemento
* @exception java.io.IOException
*/
public static IMemento loadMementoFromString(String mementoString) throws Exception {
ByteArrayInputStream in = new ByteArrayInputStream(mementoString.getBytes());
return XMLMemento.createReadRoot(new InputStreamReader(in));
}
}