/*
* @(#)DOMOutput.java
*
* Copyright (c) 1996-2010 The authors and contributors of JHotDraw.
* You may not use, copy or modify this file, except in compliance with the
* accompanying license terms.
*/
package org.jhotdraw.xml;
import java.util.*;
import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.*;
import javax.xml.transform.stream.*;
import org.w3c.dom.*;
import java.io.*;
/**
* DOMOutput.
* <p>
* Design pattern:<br>
* Name: Adapter.<br>
* Role: Adapter.<br>
* Partners: {@link org.w3c.dom.Document} as Adaptee.
*
* @author Werner Randelshofer
* @version $Id$
*/
public class JavaxDOMOutput implements DOMOutput {
/**
* The doctype of the XML document.
*/
private String doctype;
/**
* This map is used to marshall references to objects to
* the XML DOM. A key in this map is a Java Object, a value in this map
* is String representing a marshalled reference to that object.
*/
private HashMap<Object,String> objectids;
/**
* This map is used to cache prototype objects.
*/
private HashMap<String,Object> prototypes;
/**
* The document used for output.
*/
private Document document;
/**
* The current node used for output.
*/
private Node current;
/**
* The factory used to create objects.
*/
private DOMFactory factory;
/** Creates a new instance. */
public JavaxDOMOutput(DOMFactory factory) throws IOException {
this.factory = factory;
}
protected void reset() throws IOException {
try {
objectids = new HashMap<Object,String>();
document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
current = document;
} catch (ParserConfigurationException e) {
IOException error = new IOException(e.getMessage());
error.initCause(e);
throw error;
}
}
/**
* Writes the contents of the DOMOutput into the specified output stream.
*/
public void save(OutputStream out) throws IOException {
reset();
try {
if (doctype != null) {
OutputStreamWriter w = new OutputStreamWriter(out, "UTF8");
w.write("<!DOCTYPE ");
w.write(doctype);
w.write(">\n");
w.flush();
}
Transformer t = TransformerFactory.newInstance().newTransformer();
t.transform(new DOMSource(document), new StreamResult(out));
} catch (TransformerException e) {
IOException error = new IOException(e.getMessage());
error.initCause(e);
throw error;
}
}
/**
* Writes the contents of the DOMOutput into the specified output stream.
*/
public void save(Writer out) throws IOException {
reset();
try {
if (doctype != null) {
out.write("<!DOCTYPE ");
out.write(doctype);
out.write(">\n");
}
Transformer t = TransformerFactory.newInstance().newTransformer();
t.transform(new DOMSource(document), new StreamResult(out));
} catch (TransformerException e) {
IOException error = new IOException(e.getMessage());
error.initCause(e);
throw error;
}
}
/**
* Puts a new element into the DOM Document.
* The new element is added as a child to the current element in the DOM
* document. Then it becomes the current element.
* The element must be closed using closeElement.
*/
@Override
public void openElement(String tagName) {
Element newElement = document.createElement(tagName);
current.appendChild(newElement);
current = newElement;
}
/**
* Closes the current element of the DOM Document.
* The parent of the current element becomes the current element.
* @exception IllegalArgumentException if the provided tagName does
* not match the tag name of the element.
*/
@Override
public void closeElement() {
/*
if (! ((Element) current).getName().equals(tagName)) {
throw new IllegalArgumentException("Attempt to close wrong element:"+tagName +"!="+((Element) current).getName());
}*/
current = current.getParentNode();
}
/**
* Adds a comment to the current element of the DOM Document.
*/
@Override
public void addComment(String comment) {
current.appendChild(document.createComment(comment));
}
/**
* Adds a text to current element of the DOM Document.
* Note: Multiple consecutives texts will be merged.
*/
@Override
public void addText(String text) {
current.appendChild(document.createTextNode(text));
}
/**
* Adds an attribute to current element of the DOM Document.
*/
@Override
public void addAttribute(String name, String value) {
if (value != null) {
((Element) current).setAttribute(name, value);
}
}
/**
* Adds an attribute to current element of the DOM Document.
*/
@Override
public void addAttribute(String name, int value) {
((Element) current).setAttribute(name, Integer.toString(value));
}
/**
* Adds an attribute to current element of the DOM Document.
*/
@Override
public void addAttribute(String name, boolean value) {
((Element) current).setAttribute(name, Boolean.toString(value));
}
/**
* Adds an attribute to current element of the DOM Document.
*/
@Override
public void addAttribute(String name, float value) {
// Remove the awkard .0 at the end of each number
String str = Float.toString(value);
if (str.endsWith(".0")) str = str.substring(0, str.length() - 2);
((Element) current).setAttribute(name, str);
}
/**
* Adds an attribute to current element of the DOM Document.
*/
@Override
public void addAttribute(String name, double value) {
// Remove the awkard .0 at the end of each number
String str = Double.toString(value);
if (str.endsWith(".0")) str = str.substring(0, str.length() - 2);
((Element) current).setAttribute(name, str);
}
@Override
public void writeObject(Object o) throws IOException {
String tagName = factory.getName(o);
if (tagName == null) throw new IllegalArgumentException("no tag name for:"+o);
openElement(tagName);
if (objectids.containsKey(o)) {
addAttribute("ref", objectids.get(o));
} else {
String id = Integer.toString(objectids.size(), 16);
objectids.put(o, id);
addAttribute("id", id);
factory.write(this,o);
}
closeElement();
}
@Override
public void addAttribute(String name, float value, float defaultValue) {
if (value != defaultValue) {
addAttribute(name, value);
}
}
@Override
public void addAttribute(String name, int value, int defaultValue) {
if (value != defaultValue) {
addAttribute(name, value);
}
}
@Override
public void addAttribute(String name, double value, double defaultValue) {
if (value != defaultValue) {
addAttribute(name, value);
}
}
@Override
public void addAttribute(String name, boolean value, boolean defaultValue) {
if (value != defaultValue) {
addAttribute(name, value);
}
}
@Override
public void addAttribute(String name, String value, String defaultValue) {
if (! value.equals(defaultValue)) {
addAttribute(name, value);
}
}
@Override
public Object getPrototype() {
if (prototypes == null) {
prototypes = new HashMap<String, Object>();
}
if (! prototypes.containsKey(current.getNodeName())) {
prototypes.put(current.getNodeName(), factory.create(current.getNodeName()));
}
return prototypes.get(current.getNodeName());
}
@Override
public void setDoctype(String doctype) {
this.doctype = doctype;
}
}