
ch.inftec.ju.util.xml.XmlBuilder Maven / Gradle / Ivy
package ch.inftec.ju.util.xml;
import java.util.HashMap;
import java.util.Map;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Text;
import ch.inftec.ju.util.JuRuntimeException;
/**
* Helper class to build XMLs based on DOM (Document Object Model).
*
* By default, the generated Document will be set as standalone=yes.
* @author tgdmemae
*
*/
public class XmlBuilder {
private final XmlBuilder parentBuilder;
private final Document document;
private final Element element;
private final Map namespaces = new HashMap<>();
static XmlBuilder createRootBuilder(String rootElementName, String namespacePrefix, String namespaceUri) {
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = factory.newDocumentBuilder();
Document document = docBuilder.newDocument();
document.setXmlStandalone(true);
Element rootElement = null;
if (namespacePrefix != null) {
rootElement = document.createElementNS(namespaceUri, rootElementName);
rootElement.setPrefix(namespacePrefix);
} else {
rootElement = document.createElement(rootElementName);
}
document.appendChild(rootElement);
XmlBuilder xb = new XmlBuilder(null, document, rootElement);
if (namespacePrefix != null) {
xb.addNamespace(namespacePrefix, namespaceUri);
}
return xb;
} catch (Exception ex) {
throw new JuRuntimeException("Couldn't initialize XML Document");
}
}
private XmlBuilder(XmlBuilder parentBuilder, Document document, Element element) throws JuRuntimeException {
this.parentBuilder = parentBuilder;
this.document = document;
this.element = element;
}
/**
* Adds a new Namespace to this builder.
* @param prefix Namespace prefix, e.g. ns
* @param uri Namespace URI, e.g http://inftec.ch/ns
* @return
*/
public XmlBuilder addNamespace(String prefix, String uri) {
if (this.namespaces.containsKey(prefix)) {
throw new IllegalArgumentException(String.format("Namespace with prefix %s has already been added"));
}
this.namespaces.put(prefix, uri);
return this;
}
/**
* Sets an attribute for the current element.
* @param name Attribute name
* @param value Attribute value
* @return This builder to allow for chaining
*/
public XmlBuilder setAttribute(String name, String value) {
this.element.setAttribute(name, value);
return this;
}
/**
* Adds text to the current element.
* @param text Text to be added
* @return This builder to allow for chaining
*/
public XmlBuilder addText(String text) {
Text textNode = this.document.createTextNode(text);
this.element.appendChild(textNode);
return this;
}
/**
* Adds a child element to the current element using the default namespace.
*
* Call endChild as soon as the child node has been configured
* @param elementName Name of the child element
* @return XmlBuilder to modify the child element
*/
public XmlBuilder addChild(String elementName) {
Element child = this.document.createElement(elementName);
this.element.appendChild(child);
return new XmlBuilder(this, this.document, child);
}
/**
* Adds a child element with the specified namespace prefix.
*
* The namespace must have been added to a root element before.
*
* Call endChild as soon as the child node has been configured
* @param elementName Element name
* @param namespacePrefix Namespace prefix of a registered namespace
* @return XmlBuilder to modify the child element
*/
public XmlBuilder addChild(String elementName, String namespacePrefix) {
Element child = this.document.createElementNS(this.getNamespaceUri(namespacePrefix), elementName);
child.setPrefix(namespacePrefix);
this.element.appendChild(child);
return new XmlBuilder(this, this.document, child);
}
private String getNamespaceUri(String prefix) {
if (this.namespaces.containsKey(prefix)) {
return this.namespaces.get(prefix);
} else if (this.parentBuilder != null) {
return this.parentBuilder.getNamespaceUri(prefix);
} else {
throw new IllegalArgumentException("Unknown namespace: " + prefix);
}
}
/**
* Ends the adding of a child and returns the parent builder
* to continue constructing the XML.
* @return Parent XmlBuilder
*/
public XmlBuilder endChild() {
if (this.parentBuilder == null) {
throw new IllegalStateException("Cannot call endChild on root builder");
}
return this.parentBuilder;
}
/**
* Gets the document generated by this builder. Usually, you will want to call
* this method on the root builder, even though all builders return the same
* document.
*
* This method always returns the same instance of document.
* @return Document instance created by this builder
*/
public Document getDocument() {
return this.document;
}
}