/** * */ package org.openntf.domino.utils.xml; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.Serializable; import java.io.StringWriter; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Set; import javax.xml.transform.OutputKeys; import javax.xml.transform.Source; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; import org.openntf.domino.utils.DominoUtils; import org.w3c.dom.Document; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; /** * @author jgallagher * */ public class XMLNode implements Map<String, Object>, Serializable { private static final long serialVersionUID = 2304991412510751453L; private static TransformerFactory tFactory = TransformerFactory.newInstance(); public static Transformer DEFAULT_TRANSFORMER = createTransformer(null); protected org.w3c.dom.Node node_ = null; private transient XPath xPath_ = null; private Map<String, Object> getResults_ = new HashMap<String, Object>(); protected XMLNode() { } public XMLNode(final org.w3c.dom.Node node) { node_ = node; } public XMLNode selectSingleNode(final String xpathString) { XMLNodeList result = this.selectNodes(xpathString); return result.size() == 0 ? null : result.get(0); } public static Transformer createTransformer(final InputStream xsltStream) { Transformer transformer = null; try { if (xsltStream == null) { transformer = tFactory.newTransformer(); } else { Source filter = new StreamSource(xsltStream); transformer = tFactory.newTransformer(filter); } // We don't want the XML declaration in front //transformer.setOutputProperty("omit-xml-declaration", "yes"); transformer.setOutputProperty(OutputKeys.METHOD, "xml"); transformer.setOutputProperty(OutputKeys.INDENT, "yes"); transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); } catch (TransformerConfigurationException e) { e.printStackTrace(); } return transformer; } public XMLNodeList selectNodes(final String xpathString) { try { NodeList nodes = (NodeList) this.getXPath().compile(xpathString).evaluate(node_, XPathConstants.NODESET); XMLNodeList result = new XMLNodeList(nodes.getLength()); for (int i = 0; i < nodes.getLength(); i++) { result.add(new XMLNode(nodes.item(i))); } return result; } catch (XPathExpressionException xee) { DominoUtils.handleException(xee); return null; } } public String getAttribute(final String attribute) { if (this.node_ == null) { return ""; } NamedNodeMap attributes = this.node_.getAttributes(); if (attributes == null) { return ""; } Node attr = attributes.getNamedItem(attribute); if (attr == null) { return ""; } return attr.getTextContent(); } public void removeAttribute(final String attribute) { Node attr = this.node_.getAttributes().getNamedItem(attribute); attr.getParentNode().removeChild(attr); } public void setAttribute(final String attribute, final String value) { Node attr = this.node_.getAttributes().getNamedItem(attribute); if (attr == null) { attr = getDocument().createAttribute(attribute); } attr.setNodeValue(value == null ? "" : value); this.node_.getAttributes().setNamedItem(attr); } public String getNodeName() { return node_.getNodeName(); } public short getNodeType() { return node_.getNodeType(); } public String getText() { if (node_ == null) { return ""; } return node_.getTextContent(); } public void setText(final String text) { if (node_ == null) { return; } node_.setTextContent(text); } public String getTextContent() { return this.getText(); } public void setTextContent(final String textContent) { this.setText(textContent); } public String getNodeValue() { if (node_ == null) { return ""; } return node_.getNodeValue(); } public void setNodeValue(final String value) { if (node_ == null) { return; } node_.setNodeValue(value); } public XMLNode addChildElement(final String elementName) { Node node = this.getDocument().createElement(elementName); this.node_.appendChild(node); return new XMLNode(node); } public XMLNode insertChildElementBefore(final String elementName, final XMLNode refNode) { Node node = this.getDocument().createElement(elementName); this.node_.insertBefore(node, refNode.getNode()); return new XMLNode(node); } public XMLNode getFirstChild() { Node node = this.getNode().getFirstChild(); if (node != null) { return new XMLNode(node); } return null; } public XMLNode getFirstChildElement() { Node node = this.getNode().getFirstChild(); while (node != null && node.getNodeType() != Node.ELEMENT_NODE) { node = node.getNextSibling(); } return node == null ? null : new XMLNode(node); } public XMLNode getParentNode() { Node node = this.getNode().getParentNode(); if (node != null) { return new XMLNode(node); } return null; } public void removeChild(final XMLNode childNode) { this.getNode().removeChild(childNode.getNode()); } public XMLNodeList getChildNodes() { return new XMLNodeList(getNode().getChildNodes()); } public void removeChildren() { for (XMLNode child : this.getChildNodes()) { removeChild(child); } } public XMLNode getNextSibling() { Node node = this.getNode().getNextSibling(); if (node != null) { return new XMLNode(node); } return null; } public XMLNode getNextSiblingElement() { Node node = this.getNode().getNextSibling(); while (node != null && node.getNodeType() != Node.ELEMENT_NODE) { node = node.getNextSibling(); } return node == null ? null : new XMLNode(node); } public void appendChild(final XMLNode node) { this.getNode().appendChild(node.getNode()); } public void insertBefore(final XMLNode newChild, final XMLNode refChild) { this.getNode().insertBefore(newChild.getNode(), refChild.getNode()); } public org.w3c.dom.Node getNode() { return this.node_; } @Override public Object get(final Object arg0) { String path = String.valueOf(arg0); if (path.equals("nodeValue")) { return this.getNode().getNodeValue(); } else if (path.equals("textContent")) { return this.getNode().getTextContent(); } if (!this.getResults_.containsKey(path)) { try { XMLNodeList nodes = this.selectNodes(path); if (nodes.size() == 1) { // this.getResults.put(path, nodes.get(0).getNode()); this.getResults_.put(path, nodes.get(0)); } else { this.getResults_.put(path, nodes); } } catch (Exception e) { e.printStackTrace(); } } return this.getResults_.get(path); } /** * Equivalent to {@link #getXml(Transformer)} passed null. */ public String getXml() throws IOException { return getXml(null); } public String getXml(Transformer transformer) throws IOException { try { if (transformer == null) transformer = DEFAULT_TRANSFORMER; StreamResult result = new StreamResult(new StringWriter()); DOMSource source = new DOMSource(this.node_); transformer.transform(source, result); return result.getWriter().toString(); } catch (Exception e) { e.printStackTrace(); } return null; } public void getXml(Transformer transformer, final File out) throws IOException { try { if (transformer == null) transformer = DEFAULT_TRANSFORMER; // StreamResult xResult = new StreamResult(out); - This constructor has problems with german umlauts // See: http://comments.gmane.org/gmane.text.xml.saxon.help/6790 StreamResult result = new StreamResult(out.toURI().toString()); DOMSource source = new DOMSource(this.node_); transformer.transform(source, result); } catch (Exception e) { e.printStackTrace(); } } private XPath getXPath() { if (this.xPath_ == null) { xPath_ = XPathFactory.newInstance().newXPath(); } return this.xPath_; } private Document getDocument() { return this.node_.getOwnerDocument(); } @Override public void clear() { } @Override public boolean containsKey(final Object arg0) { return false; } @Override public boolean containsValue(final Object arg0) { return false; } @Override public Set<java.util.Map.Entry<String, Object>> entrySet() { return null; } @Override public boolean isEmpty() { return false; } @Override public Set<String> keySet() { return null; } @Override public Object put(final String arg0, final Object arg1) { if (arg0.equals("nodeValue")) { this.getNode().setNodeValue(String.valueOf(arg1)); return arg1; } else if (arg0.equals("textContent")) { this.getNode().setNodeValue(String.valueOf(arg1)); return arg1; } return null; } @Override public void putAll(final Map<? extends String, ? extends Object> arg0) { } @Override public Object remove(final Object arg0) { return null; } @Override public int size() { return 0; } @Override public Collection<Object> values() { return null; } }