package com.google.code.maven_replacer_plugin; import java.io.StringReader; import java.io.StringWriter; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.transform.Result; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpression; import javax.xml.xpath.XPathFactory; import org.apache.xml.serialize.OutputFormat; import org.apache.xml.serialize.XMLSerializer; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; public class XPathReplacer implements Replacer { private final TokenReplacer tokenReplacer; private final DocumentBuilder docBuilder; private final XPath xpath; private final Transformer transformer; public XPathReplacer(TokenReplacer tokenReplacer) { try { if (tokenReplacer == null) { throw new IllegalArgumentException("Must supply a tokenReplacer to change the node's content."); } this.tokenReplacer = tokenReplacer; this.xpath = XPathFactory.newInstance().newXPath(); this.docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); this.transformer = TransformerFactory.newInstance().newTransformer(); } catch (Exception e) { throw new IllegalStateException("Unable to initialise XML processing: " + e.getMessage(), e); } } public String replace(String content, Replacement replacement, boolean regex, int regexFlags) { try { Document doc = parseXml(content); NodeList replacementTargets = findReplacementNodes(doc, replacement.getXpath()); replaceContent(replacementTargets, replacement, regex, regexFlags); return writeXml(doc); } catch (Exception e) { String cause = e.getMessage() != null ? e.getMessage() : e.getCause().getMessage(); throw new RuntimeException("Error during XML replacement: " + cause, e); } } private void replaceContent(NodeList replacementNodes, Replacement replacement, boolean regex, int regexFlags) throws Exception { for (int i=0; i < replacementNodes.getLength(); i++) { Node replacementNode = replacementNodes.item(i); switch (replacementNode.getNodeType()) { case Node.ATTRIBUTE_NODE: case Node.TEXT_NODE: String replacedValue = tokenReplacer.replace(replacementNode.getTextContent(), replacement, regex, regexFlags); replacementNode.setNodeValue(replacedValue); break; default: String replacementNodeStr = convertNodeToString(replacementNode); String replacedNodeStr = tokenReplacer.replace(replacementNodeStr, replacement, regex, regexFlags); Node parent = replacementNode.getParentNode(); if (parent.getOwnerDocument() == null) { throw new UnsupportedOperationException("Cannot replace a node's content not part of a parent node."); } Node replacedNode = convertXmlToNode(replacedNodeStr); Node newNode = parent.getOwnerDocument().importNode(replacedNode, true); parent.replaceChild(newNode, replacementNode); } } } private Document parseXml(String content) throws Exception { return docBuilder.parse(new InputSource(new StringReader(content))); } private NodeList findReplacementNodes(Document doc, String xpathString) throws Exception { XPathExpression xpathExpr = xpath.compile(xpathString); return (NodeList) xpathExpr.evaluate(doc, XPathConstants.NODESET); } private String convertNodeToString(Node replacementTarget) throws TransformerException { DOMSource targetSource = new DOMSource(replacementTarget); StringWriter stringWriter = new StringWriter(); Result stringResult = new StreamResult(stringWriter); transformer.transform(targetSource, stringResult); return stringWriter.toString(); } private Node convertXmlToNode(String xml) throws Exception { InputSource docSource = new InputSource(new StringReader(xml)); Document doc = docBuilder.parse(docSource); return doc.getFirstChild(); } private String writeXml(Document doc) throws Exception { OutputFormat of = new OutputFormat(doc); of.setPreserveSpace(true); of.setEncoding(doc.getXmlEncoding()); StringWriter sw = new StringWriter(); XMLSerializer serializer = new XMLSerializer(sw, of); serializer.serialize(doc); return sw.toString(); } }