/*
* Copyright (c) 2013 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.apidiff.util;
import org.jdom2.Attribute;
import org.jdom2.Comment;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.output.Format;
import org.jdom2.output.XMLOutputter;
import java.util.Iterator;
import java.util.List;
/**
* Utility class to help decompose, order and compare xml document with Jdom.
*/
public class XmlDiff {
/**
* Compares two documents and outputs different part info.
* <p>
* 1. ignore sequence 2. ignore element text value 3. ignore xml comment element
* </p>
*
* @param oldDocument
* The old xml document
* @param newDocument
* The new xml document
* @return
* The instance of diff
*/
public static Pair<String, String> compareXml(Document oldDocument, Document newDocument) {
Element oldRootElement = oldDocument.getRootElement();
Element newRootElement = newDocument.getRootElement();
if (compareElement(oldRootElement, newRootElement)) {
return null;
}
XMLOutputter xmlOutputter = new XMLOutputter(Format.getPrettyFormat());
return new Pair<String, String>(xmlOutputter.outputString(oldRootElement),
xmlOutputter.outputString(newRootElement));
}
/**
* Compares two XML Elements and their children recursively
*
* @param oldElement
* The old element to compare
* @param newElement
* The new element to compare
* @return true if two elements are same, else false
*/
public static boolean compareElement(Element oldElement, Element newElement) {
// Check element name
if (!oldElement.getName().equals(newElement.getName())) {
return false;
}
// Check attributes
if (!compareAttributes(oldElement.getAttributes(), newElement.getAttributes())) {
return false;
}
// Check children number
if (oldElement.getChildren().isEmpty() && newElement.getChildren().isEmpty()) {
return true;
}
// Check non leaf element
Iterator<Element> oldIter = oldElement.getChildren().iterator();
while (oldIter.hasNext()) {
Element oldChild = oldIter.next();
boolean found = false;
Iterator<Element> newIter = newElement.getChildren().iterator();
while (newIter.hasNext()) {
Element newChild = newIter.next();
if (newChild.getName().equals(oldChild.getName())) {
found = compareElement(oldChild, newChild);
if (found) {
break;
}
}
}
if (found) {
oldIter.remove();
newIter.remove();
}
}
// Check children
if (oldElement.getChildren().isEmpty() && newElement.getChildren().isEmpty()) {
return true;
} else if (oldElement.getChildren().size() != newElement.getChildren().size()) {
// Add comments to stand for same tags.
if (!(oldElement.getContent(0) instanceof Comment)
&& !(newElement.getContent(0) instanceof Comment)) {
oldElement.addContent(0, new Comment("..."));
newElement.addContent(0, new Comment("..."));
}
}
return false;
}
/**
* Compares all attributes of old/new element
*
* @param oldAttributes
* The old attribute list to compare
* @param newAttributes
* The new attribute list to compare
* @return true if two lists are same, else false
*/
public static boolean compareAttributes(List<Attribute> oldAttributes, List<Attribute> newAttributes) {
Iterator<Attribute> oldIter = oldAttributes.iterator();
while (oldIter.hasNext()) {
Attribute oldAttr = oldIter.next();
Iterator<Attribute> newIter = newAttributes.iterator();
while (newIter.hasNext()) {
Attribute newAttr = newIter.next();
if (newAttr.getName().equals(oldAttr.getName())) {
oldIter.remove();
newIter.remove();
break;
}
}
}
return oldAttributes.isEmpty() && newAttributes.isEmpty();
}
}