Skip to content

Instantly share code, notes, and snippets.

@adamish
Created July 6, 2016 13:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save adamish/ecb59b45befff7d3f7db418f3fc2a115 to your computer and use it in GitHub Desktop.
Save adamish/ecb59b45befff7d3f7db418f3fc2a115 to your computer and use it in GitHub Desktop.
Merge 2 or more spring contexts to find common beans, output them to a common file, then remove them from original files. Take into account formatting differences
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
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.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
public class XmlMerge {
private Transformer transformer;
public XmlMerge() throws Exception {
Document xml1 = load("foo.xml");
Document xml2 = load("bar.xml");
Document xml3 = load("baz.xml");
TransformerFactory transformerFactory = TransformerFactory.newInstance();
transformer = transformerFactory.newTransformer();
List<Node> allCommon = compare(xml1, xml3, xml2);
remove(xml1, allCommon);
remove(xml2, allCommon);
remove(xml3, allCommon);
write(allCommon, "common.xml");
List<Node> common1and2 = compare(xml1, xml3);
remove(xml1, common1and2);
remove(xml3, common1and2);
write(common1and2, "sonar.xml");
write(xml1, "foo.xml");
write(xml2, "bar.xml");
write(xml3, "baz.xml");
}
private void write(Document doc, String string) throws IOException, TransformerException {
FileWriter writer = new FileWriter(new File(string));
StreamResult result = new StreamResult(writer);
transformer.transform(new DOMSource(doc), result);
}
private void write(List<Node> nodes, String string)
throws TransformerException, ParserConfigurationException, IOException {
Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
Element beans = doc.createElement("beans");
doc.appendChild(beans);
for (Node each : nodes) {
Node clone = each.cloneNode(true);
doc.adoptNode(clone);
beans.appendChild(clone);
System.out.println(toXml(clone));
}
FileWriter writer = new FileWriter(new File(string));
StreamResult result = new StreamResult(writer);
transformer.transform(new DOMSource(doc), result);
}
private void remove(Document bscan, List<Node> allCommon) throws Exception {
Map<String, Node> mappings = getBeans(bscan);
for (Node each : allCommon) {
String hash = hashAttributes((Element) each);
Node toRemove = mappings.get(hash);
if (toRemove == null) {
System.err.println("Cannot find " + hash);
continue;
}
Node parentNode = toRemove.getParentNode();
if (parentNode != null) {
parentNode.removeChild(toRemove);
} else {
System.err.println("Cannot remove " + each);
}
}
}
private boolean isEqual(Node a, Node b) throws XPathExpressionException, TransformerException {
Node aClone = a.cloneNode(true);
Node bClone = b.cloneNode(true);
strip(aClone);
strip(bClone);
return toXml(aClone).equals(toXml(bClone));
}
private String toXml(Node node) throws TransformerException {
StringWriter stringWriter = new StringWriter();
StreamResult result = new StreamResult(stringWriter);
transformer.transform(new DOMSource(node), result);
return stringWriter.toString();
}
private void strip(Node doc) throws XPathExpressionException {
XPathFactory xpathFactory = XPathFactory.newInstance();
// XPath to find empty text nodes.
XPathExpression xpathExp = xpathFactory.newXPath().compile("//text()[normalize-space(.) = '']");
NodeList emptyTextNodes = (NodeList) xpathExp.evaluate(doc, XPathConstants.NODESET);
// Remove each empty text node from document.
for (int i = 0; i < emptyTextNodes.getLength(); i++) {
Node emptyTextNode = emptyTextNodes.item(i);
emptyTextNode.getParentNode().removeChild(emptyTextNode);
}
}
private List<Node> compare(Document... docs) throws Exception {
List<Node> nodes = new ArrayList<Node>();
List<Map<String, Node>> mappings = new ArrayList<>();
for (int i = 0; i < docs.length; i++) {
mappings.add(getBeans(docs[i]));
}
for (Entry<String, Node> leftBeans : mappings.get(0).entrySet()) {
System.out.println(leftBeans.getKey());
boolean match = true;
for (int i = 1; i < docs.length; i++) {
Node rightBean = mappings.get(i).get(leftBeans.getKey());
if (rightBean == null) {
match = false;
break;
}
if (!isEqual(rightBean, leftBeans.getValue())) {
match = false;
break;
}
}
if (match) {
System.out.println(leftBeans.getKey() + " found in all");
nodes.add(leftBeans.getValue());
}
}
return nodes;
}
private Map<String, Node> getBeans(Document doc) throws Exception {
Map<String, Node> map = new LinkedHashMap<String, Node>();
XPath xpath = XPathFactory.newInstance().newXPath();
NodeList result = (NodeList) xpath.compile("/beans/bean").evaluate(doc, XPathConstants.NODESET);
for (int i = 0; i < result.getLength(); i++) {
Element element = (Element) result.item(i);
map.put(hashAttributes(element), element);
}
return map;
}
private String hashAttributes(Element element) {
List<String> each = new ArrayList<>();
for (int i = 0; i < element.getAttributes().getLength(); i++) {
each.add(String.format("%s=%s", element.getAttributes().item(i).getNodeName(),
element.getAttributes().item(i).getNodeValue()));
}
Collections.sort(each);
return each.toString();
}
public static void main(String[] args) throws Exception {
new XmlMerge();
}
private Document load(String file) throws Exception {
Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder()
.parse(new FileInputStream(new File(file)));
return document;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment