blob: 2a5d1ee7e0053fa623476b92b485e364b5d33e7f [file] [log] [blame]
/* Copyright 2004 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package tools.xml;
import org.apache.xmlbeans.impl.common.QNameHelper;
import org.apache.xmlbeans.impl.common.XmlWhitespace;
import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlException;
public class XmlComparator
{
public static class Diagnostic
{
private StringBuilder message = null;
private void add(String s)
{
if (message == null)
message = new StringBuilder();
message.append(s).append("\n");
}
public boolean hasMessage()
{
return message != null;
}
public String toString()
{
return (message == null ? null : message.toString());
}
public static void add(Diagnostic diag, String s)
{
if ( diag!=null)
diag.add(s);
}
}
public static boolean wsCollapseEqual(String s1, String s2)
{
String s1c = XmlWhitespace.collapse(s1);
String s2c = XmlWhitespace.collapse(s2);
return (s1c.equals(s2c));
}
public static boolean compareNamesAndAttributes(XmlCursor cur1, XmlCursor cur2, Diagnostic diag)
{
if (!cur1.getName().equals(cur2.getName()))
{
Diagnostic.add(diag, "Element names " + QNameHelper.pretty(cur1.getName()) + " and " + QNameHelper.pretty(cur2.getName()) + " do not match");
return false;
}
boolean more = cur1.toFirstAttribute();
if (more)
{
for (; more; more = cur1.toNextAttribute())
{
String text1 = cur1.getTextValue();
String text2 = cur2.getAttributeText(cur1.getName());
if (text2 == null)
{
Diagnostic.add(diag, "Attribute " + QNameHelper.pretty(cur1.getName()) + " not present");
return false;
}
if (!wsCollapseEqual(text1, text2))
{
Diagnostic.add(diag, "Attribute values for " + QNameHelper.pretty(cur1.getName()) + " do not match");
return false;
}
}
cur1.toParent();
}
more = cur2.toFirstAttribute();
if (more)
{
for (; more; more = cur2.toNextAttribute())
{
String text1 = cur1.getAttributeText(cur2.getName());
if (text1 == null)
{
Diagnostic.add(diag, "Attribute " + QNameHelper.pretty(cur2.getName()) + " not present");
return false;
}
}
cur2.toParent();
}
return true;
}
public static Diagnostic lenientlyCompareTwoXmlStrings(String actual, String expect)
throws XmlException
{
Diagnostic diag = new Diagnostic();
lenientlyCompareTwoXmlStrings(actual, expect, diag);
return diag;
}
/**
* Provides an utility to compare the xml inside the two strings
* @return true if the xml inside the two strings is leniently the same
* otherwise false
*/
public static boolean lenientlyCompareTwoXmlStrings(String actual, String expect, Diagnostic diag)
throws XmlException
{
XmlObject xobj1 = XmlObject.Factory.parse(actual);
XmlObject xobj2 = XmlObject.Factory.parse(expect);
XmlCursor cur1 = xobj1.newCursor();
XmlCursor cur2 = xobj2.newCursor();
cur1.toFirstChild();
cur2.toFirstChild();
return lenientlyCompareTwoXmlStrings(cur1, cur2, diag);
}
/**
* Provides an utility to compare the xml inside the two cursors
* @return true if the xml inside the two strings is leniently the same
* otherwise false
*/
public static boolean lenientlyCompareTwoXmlStrings(XmlCursor cur1, XmlCursor cur2, Diagnostic diag)
{
boolean match = true;
int depth = 0;
while (cur1.currentTokenType() != XmlCursor.TokenType.STARTDOC)
{
if (!compareNamesAndAttributes(cur1, cur2, diag))
{
match = false;
}
boolean hasChildren1 = cur1.toFirstChild();
boolean hasChildren2 = cur2.toFirstChild();
depth++;
if (hasChildren1 != hasChildren2)
{
Diagnostic.add(diag, "Topology differs: one document has children where the other does not (" + QNameHelper.pretty(cur1.getName()) + ", " + QNameHelper.pretty(cur2.getName()) + ")"); // TODO: where?
match = false;
if (hasChildren1)
{
cur1.toParent();
hasChildren1 = false;
}
if (hasChildren2)
{
cur2.toParent();
hasChildren2 = false;
}
}
else if (hasChildren1 == false)
{
if (!wsCollapseEqual(cur1.getTextValue(), cur2.getTextValue()))
{
Diagnostic.add(diag, "Value " + cur1.getTextValue() + " differs from value " + cur2.getTextValue());
match = false;
}
}
if (hasChildren1)
continue;
for (;;)
{
boolean hasSibling1 = cur1.toNextSibling();
boolean hasSibling2 = cur2.toNextSibling();
if (hasSibling1 != hasSibling2)
{
Diagnostic.add(diag, "Topology differs: one document has siblings where the other does not"); // TODO: where?
hasSibling1 = false;
hasSibling2 = false;
}
if (hasSibling1)
break;
cur1.toParent();
cur2.toParent();
depth--;
if (cur1.currentTokenType() == XmlCursor.TokenType.STARTDOC || depth<=0)
break;
}
}
return match;
}
}