/*
* Copyright 2010 the original author or authors.
*
* 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 org.xmlmatchers.equivalence;
import java.io.IOException;
import javax.xml.transform.Source;
import org.custommonkey.xmlunit.Diff;
import org.custommonkey.xmlunit.XMLUnit;
import org.hamcrest.Description;
import org.hamcrest.Factory;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;
import org.xml.sax.SAXException;
import org.xmlmatchers.transform.IdentityTransformer;
import org.xmlmatchers.transform.StringResult;
/**
* <p>
* Matchers for comparing XML for "equivalence" and "similarity." Two documents
* are considered to be "equivalent" if they contain the same elements in the
* same order. Also, namespace prefixes must be the same. Attribute order,
* comments, whitespace, CDATA vs. text usage is not considered.
* <p>
* </p>
* Two documents are considered to be "similar" if the the content of the nodes
* in the documents are the same, but minor differences exist e.g. sequencing of
* sibling elements, values of namespace prefixes, use of implied attribute
* values.
* </p>
*
* @author David Ehringer
*/
public class IsEquivalentTo extends TypeSafeMatcher<Source> {
// TODO change to extending TypeSafeDiagnosingMatcher
private final IdentityTransformer identity = new IdentityTransformer();
private final String control;
private final boolean onlySimilar;
private IsEquivalentTo(Source control) {
this(control, false);
}
private IsEquivalentTo(Source control, boolean onlySimilar) {
this.control = convertToString(control);
this.onlySimilar = onlySimilar;
}
@Override
public boolean matchesSafely(Source source) {
String test = convertToString(source);
XMLUnit.setIgnoreAttributeOrder(true);
XMLUnit.setIgnoreComments(true);
XMLUnit.setIgnoreDiffBetweenTextAndCDATA(true);
XMLUnit.setIgnoreWhitespace(true);
XMLUnit.setNormalize(true);
XMLUnit.setNormalizeWhitespace(true);
try {
if (onlySimilar) {
return new Diff(control, test).similar();
}
return new Diff(control, test).identical();
} catch (SAXException e) {
return false;
} catch (IOException e) {
return false;
}
}
private String convertToString(Source source) {
StringResult result = new StringResult();
identity.transform(source, result);
return result.toString();
}
public void describeTo(Description description) {
description.appendText("an XML document equivalent to " + control);
}
@Factory
public static Matcher<Source> isEquivalentTo(Source control) {
return new IsEquivalentTo(control);
}
@Factory
public static Matcher<Source> equivalentTo(Source control) {
return new IsEquivalentTo(control);
}
@Factory
public static Matcher<Source> isSimilarTo(Source control) {
return new IsEquivalentTo(control, true);
}
@Factory
public static Matcher<Source> similarTo(Source control) {
return new IsEquivalentTo(control, true);
}
}