/*
* @(#)NanoXMLDOMOutput.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 edu.umd.cs.findbugs.annotations.Nullable;
import java.util.*;
import java.io.*;
import net.n3.nanoxml.*;
import org.jhotdraw.app.Disposable;
/**
* DOMOutput using Nano XML.
* <p>
* Design pattern:<br>
* Name: Adapter.<br>
* Role: Adapter.<br>
* Partners: {@link net.n3.nanoxml.XMLElement} as Adaptee.
*
* @author Werner Randelshofer
* @version $Id$
*/
public class NanoXMLDOMOutput implements DOMOutput, Disposable {
/**
* 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.
*/
@Nullable private XMLElement document;
/**
* The current node used for output.
*/
private XMLElement current;
/**
* The factory used to create objects.
*/
private DOMFactory factory;
/**
* The stack.
*/
private Stack<XMLElement> stack;
/** Creates a new instance. */
public NanoXMLDOMOutput(DOMFactory factory) {
this.factory = factory;
objectids = new HashMap<Object,String>();
document = new XMLElement();//new HashMap(), false, false);
current = document;
stack = new Stack<XMLElement>();
stack.push(current);
}
/**
* Writes the contents of the DOMOutput into the specified output stream.
*/
public void save(OutputStream out) throws IOException {
Writer w = new OutputStreamWriter(out, "UTF8");
save(w);
w.flush();
}
/**
* Writes the contents of the DOMOutput into the specified writer.
*/
public void save(Writer out) throws IOException {
if (doctype != null) {
out.write("<!DOCTYPE ");
out.write(doctype);
out.write(">\n");
}
XMLWriter writer = new XMLWriter(out);
writer.write((XMLElement) document.getChildren().get(0));
}
/**
* Prints the contents of the DOMOutput into the specified print writer.
*/
public void print(PrintWriter out) {
XMLWriter writer = new XMLWriter(out);
try {
// writer.write(document);
writer.write((XMLElement) document.getChildren().get(0), true);
} catch (IOException e) {
InternalError error = new InternalError();
error.initCause(e);
throw error;
}
//((XMLElement) document.getChildren().get(0)).print(out);
}
/**
* 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) {
XMLElement newElement = new XMLElement();//new HashMap(), false, false);
newElement.setName(tagName);
current.addChild(newElement);
stack.push(current);
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() {
current = stack.pop();
}
/**
* Adds a comment to the current element of the DOM Document.
*/
@Override
public void addComment(String comment) {
// NanoXML does not support comments
}
/**
* Adds a text to current element of the DOM Document.
* Note: Multiple consecutives texts will be merged.
*/
@Override
public void addText(String text) {
String old = current.getContent();
if (old == null) {
current.setContent(text);
} else {
current.setContent(old+text);
}
}
/**
* Adds an attribute to current element of the DOM Document.
*/
@Override
public void addAttribute(String name, String value) {
if (value != null) {
current.setAttribute(name, value);
}
}
/**
* Adds an attribute to current element of the DOM Document.
*/
@Override
public void addAttribute(String name, int value) {
current.setAttribute(name, Integer.toString(value));
}
/**
* Adds an attribute to current element of the DOM Document.
*/
@Override
public void addAttribute(String name, boolean value) {
current.setAttribute(name, Boolean.valueOf(value).toString());
}
/**
* 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);
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);
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);
XMLElement element = current;
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 != null && ! value.equals(defaultValue)) {
addAttribute(name, value);
}
}
@Override
public Object getPrototype() {
if (prototypes == null) {
prototypes = new HashMap<String, Object>();
}
if (! prototypes.containsKey(current.getName())) {
prototypes.put(current.getName(), factory.create(current.getName()));
}
return prototypes.get(current.getName());
}
@Override
public void setDoctype(String doctype) {
this.doctype = doctype;
}
@Override
public void dispose() {
if (document != null) {
document.dispose();
document = null;
}
}
}