001: /*
002: ******************************************************************
003: Copyright (c) 2001-2007, Jeff Martin, Tim Bacon
004: All rights reserved.
005:
006: Redistribution and use in source and binary forms, with or without
007: modification, are permitted provided that the following conditions
008: are met:
009:
010: * Redistributions of source code must retain the above copyright
011: notice, this list of conditions and the following disclaimer.
012: * Redistributions in binary form must reproduce the above
013: copyright notice, this list of conditions and the following
014: disclaimer in the documentation and/or other materials provided
015: with the distribution.
016: * Neither the name of the xmlunit.sourceforge.net nor the names
017: of its contributors may be used to endorse or promote products
018: derived from this software without specific prior written
019: permission.
020:
021: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
022: "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
023: LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
024: FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
025: COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
026: INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
027: BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
028: LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
029: CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
030: LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
031: ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
032: POSSIBILITY OF SUCH DAMAGE.
033:
034: ******************************************************************
035: */
036:
037: package org.custommonkey.xmlunit;
038:
039: import java.util.Arrays;
040: import org.w3c.dom.Attr;
041: import org.w3c.dom.Element;
042: import org.w3c.dom.NamedNodeMap;
043:
044: /**
045: * More complex interface implementation that tests two elements for tag name
046: * and attribute name comparability.
047: * <br />Examples and more at
048: * <a href="http://xmlunit.sourceforge.net"/>xmlunit.sourceforge.net</a>
049: * @see DifferenceEngine#compareNodeList(NodeList, NodeList, int, DifferenceListener, ElementQualifier)
050: * @see Diff#overrideElementQualifier(ElementQualifier)
051: */
052: public class ElementNameAndAttributeQualifier extends
053: ElementNameQualifier {
054: private static final String[] ALL_ATTRIBUTES = { "*" };
055:
056: private final String[] qualifyingAttrNames;
057:
058: /**
059: * No-args constructor: use all attributes from all elements to determine
060: * whether elements qualify for comparability
061: */
062: public ElementNameAndAttributeQualifier() {
063: this (ALL_ATTRIBUTES);
064: }
065:
066: /**
067: * Simple constructor for a single qualifying attribute name
068: * @param attrName the value to use to qualify whether two elements can be
069: * compared further for differences
070: */
071: public ElementNameAndAttributeQualifier(String attrName) {
072: this (new String[] { attrName });
073: }
074:
075: /**
076: * Extended constructor for multiple qualifying attribute names
077: * @param attrNames the array of values to use to qualify whether two
078: * elements can be compared further for differences
079: */
080: public ElementNameAndAttributeQualifier(String[] attrNames) {
081: this .qualifyingAttrNames = new String[attrNames.length];
082: System.arraycopy(attrNames, 0, qualifyingAttrNames, 0,
083: attrNames.length);
084: }
085:
086: /**
087: * Determine whether two elements qualify for further Difference comparison.
088: * @param differenceEngine the DifferenceEngine instance wanting to
089: * determine if the elements are comparable
090: * @param control
091: * @param test
092: * @return true if the two elements qualify for further comparison based on
093: * both the superclass qualification (namespace URI and non- namespaced tag
094: * name), and the presence of qualifying attributes with the same values;
095: * false otherwise
096: */
097: public boolean qualifyForComparison(Element control, Element test) {
098: if (super .qualifyForComparison(control, test)) {
099: return areAttributesComparable(control, test);
100: }
101: return false;
102: }
103:
104: /**
105: * Determine whether the qualifying attributes are present in both elements
106: * and if so whether their values are the same
107: * @param control
108: * @param test
109: * @return true if all qualifying attributes are present with the same
110: * values, false otherwise
111: */
112: protected boolean areAttributesComparable(Element control,
113: Element test) {
114: String controlValue, testValue;
115: Attr[] qualifyingAttributes;
116: NamedNodeMap namedNodeMap = control.getAttributes();
117: if (matchesAllAttributes(qualifyingAttrNames)) {
118: qualifyingAttributes = new Attr[namedNodeMap.getLength()];
119: for (int n = 0; n < qualifyingAttributes.length; ++n) {
120: qualifyingAttributes[n] = (Attr) namedNodeMap.item(n);
121: }
122: } else {
123: qualifyingAttributes = new Attr[qualifyingAttrNames.length];
124: for (int n = 0; n < qualifyingAttrNames.length; ++n) {
125: qualifyingAttributes[n] = (Attr) namedNodeMap
126: .getNamedItem(qualifyingAttrNames[n]);
127: }
128: }
129:
130: String nsURI, name;
131: for (int i = 0; i < qualifyingAttributes.length; ++i) {
132: if (qualifyingAttributes[i] != null) {
133: nsURI = qualifyingAttributes[i].getNamespaceURI();
134: controlValue = qualifyingAttributes[i].getNodeValue();
135: name = qualifyingAttributes[i].getName();
136: } else {
137: // cannot be "*" case
138: nsURI = controlValue = "";
139: name = qualifyingAttrNames[i];
140: }
141: if (nsURI == null || nsURI.length() == 0) {
142: testValue = test.getAttribute(name);
143: } else {
144: testValue = test.getAttributeNS(nsURI,
145: qualifyingAttributes[i].getLocalName());
146: }
147: if (controlValue == null) {
148: if (testValue != null) {
149: return false;
150: }
151: } else {
152: if (!controlValue.equals(testValue)) {
153: return false;
154: }
155: }
156: }
157: return true;
158: }
159:
160: private static boolean matchesAllAttributes(String[] attributes) {
161: return Arrays.equals(attributes, ALL_ATTRIBUTES);
162: }
163: }
|