// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package net.sourceforge.eclipsejetty.util;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
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.Comment;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.ProcessingInstruction;
/**
* Utility class to quickly build DOMs
*
* @author Manfred Hantschel
*/
public class DOMBuilder
{
protected Document document = null;
protected Node rootNode = null;
protected Node activeNode = null;
/**
* Initializes the builder and creates a new document
*
* @throws IllegalStateException if the parser could not be configured
*/
public DOMBuilder() throws IllegalStateException
{
super();
try
{
reset();
}
catch (ParserConfigurationException e)
{
throw new IllegalStateException("Could not configure parser", e); //$NON-NLS-1$
}
}
/**
* Initialized the generator and uses the specified document
*
* @param document the document
* @param rootNode the node to attach all other nodes to
*/
public DOMBuilder(Document document, Node rootNode)
{
super();
reset(document, rootNode);
}
/**
* Resets the generator
*
* @return the DOM builder instance
* @throws ParserConfigurationException sometimes
*/
public DOMBuilder reset() throws ParserConfigurationException
{
Document newDocument = createDocument();
return reset(newDocument, newDocument);
}
/**
* Resets the generator with the specified document and rootNode
*
* @param newDocument the document
* @param newRootNode the root node
* @return the DOM builder instance
*/
public DOMBuilder reset(Document newDocument, Node newRootNode)
{
document = newDocument;
rootNode = newRootNode;
activeNode = newRootNode;
return this;
}
/**
* Creates the document
*
* @return the document
*/
private Document createDocument() throws IllegalArgumentException
{
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder;
try
{
builder = factory.newDocumentBuilder();
}
catch (ParserConfigurationException e)
{
throw new IllegalArgumentException("Failed to configure parser", e); //$NON-NLS-1$
}
return builder.newDocument();
}
/**
* Returns the document
*
* @return the document
*/
public Document getDocument()
{
return document;
}
/**
* Adds a processing instruction for the stylesheet
*
* @param link the link to the stylesheet
* @return the DOM builder instance
*/
public DOMBuilder setStylesheet(String link)
{
ProcessingInstruction pi =
document
.createProcessingInstruction("xml-stylesheet", String.format("type=\"text/xml\" href=\"%s\"", link)); //$NON-NLS-1$ //$NON-NLS-2$
document.insertBefore(pi, document.getFirstChild());
return this;
}
/**
* Returns the root node
*
* @return the root node
*/
public Node getRootNode()
{
return rootNode;
}
/**
* Returns the currently active node
*
* @return the currently active node
*/
public Node getActiveNode()
{
return activeNode;
}
/**
* Adds a comment
*
* @param comment the comment
* @return the DOM builder instance
*/
public DOMBuilder comment(Object comment)
{
Comment commentElement = document.createComment(String.valueOf(comment));
activeNode.appendChild(commentElement);
return this;
}
/**
* Begins a new element and adds it at the current position
*
* @param name the name of the element
* @return the DOM builder instance
*/
public DOMBuilder begin(String name)
{
Element element = document.createElement(name);
activeNode.appendChild(element);
activeNode = element;
return this;
}
/**
* Adds an element to the current element
*
* @param name the name of the attribute
* @param value the value of the attribute
* @return the DOM builder instance
*/
public DOMBuilder attribute(String name, Object value)
{
if (value != null)
{
((Element) activeNode).setAttribute(name, String.valueOf(value));
}
return this;
}
/**
* Adds text to the current element
*
* @param text the text
* @return the DOM builder instance
*/
public DOMBuilder text(Object text)
{
if (text != null)
{
activeNode.appendChild(document.createTextNode(String.valueOf(text)));
}
return this;
}
/**
* Adds an empty element with the specified name
*
* @param name the name
* @return the DOM builder instance
*/
public DOMBuilder element(String name)
{
begin(name);
end();
return this;
}
/**
* Adds an element with the specified name that contains the specified text
*
* @param name the name
* @param text the text
* @return the DOM builder instance
*/
public DOMBuilder element(String name, Object text)
{
begin(name);
text(text);
end();
return this;
}
/**
* Adds an element with the specified name and exactly one attribute. The element contains no text.
*
* @param name the name
* @param attrName the name of the attribute
* @param attrValue the value of the attribute
* @return the DOM builder instance
*/
public DOMBuilder element(String name, String attrName, Object attrValue)
{
begin(name);
attribute(attrName, attrValue);
end();
return this;
}
/**
* Adds an element with the specified name and exactly one attribute. The element contains the specified text.
*
* @param name the name
* @param attrName the name of the attribute
* @param attrValue the value of the attribute
* @param text the text
* @return the DOM builder instance
*/
public DOMBuilder element(String name, String attrName, Object attrValue, Object text)
{
begin(name);
attribute(attrName, attrValue);
text(text);
end();
return this;
}
/**
* Ends the current node and steps one node back
*
* @return the DOM builder instance
*/
public DOMBuilder end()
{
activeNode = activeNode.getParentNode();
return this;
}
/**
* Returns a string that contains the XML data
*
* @return the XML as string
* @see java.lang.Object#toString()
*/
@Override
public String toString()
{
try
{
ByteArrayOutputStream out = new ByteArrayOutputStream();
try
{
write(out, true);
}
finally
{
out.close();
}
return out.toString("UTF-8"); //$NON-NLS-1$
}
catch (IOException e)
{
throw new IllegalArgumentException("Could not write transformer result"); //$NON-NLS-1$
}
}
/**
* Exports the specified document to the specified stream
*
* @param out the stream
* @param formatted true if formatted
* @throws IOException on occasion
*/
public void write(OutputStream out, boolean formatted) throws IOException
{
TransformerFactory factory = TransformerFactory.newInstance();
if (formatted)
{
factory.setAttribute("indent-number", 4); //$NON-NLS-1$
}
Transformer transformer;
try
{
transformer = factory.newTransformer();
}
catch (TransformerConfigurationException e)
{
throw new IOException(String.format("Failed to create transformer: %s", e)); //$NON-NLS-1$
}
transformer.setParameter("encoding", "UTF-8"); //$NON-NLS-1$ //$NON-NLS-2$
if (formatted)
{
transformer.setOutputProperty(OutputKeys.INDENT, "yes"); //$NON-NLS-1$
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4"); //$NON-NLS-1$ //$NON-NLS-2$
}
try
{
transformer.transform(new DOMSource(document), new StreamResult(out));
}
catch (TransformerException e)
{
throw new IOException(String.format("Failed to transform node: %s", e)); //$NON-NLS-1$
}
}
}