001: /*
002: * This file is part of PFIXCORE.
003: *
004: * PFIXCORE is free software; you can redistribute it and/or modify
005: * it under the terms of the GNU Lesser General Public License as published by
006: * the Free Software Foundation; either version 2 of the License, or
007: * (at your option) any later version.
008: *
009: * PFIXCORE is distributed in the hope that it will be useful,
010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
012: * GNU Lesser General Public License for more details.
013: *
014: * You should have received a copy of the GNU Lesser General Public License
015: * along with PFIXCORE; if not, write to the Free Software
016: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
017: *
018: */
019: package de.schlund.pfixxml.util;
020:
021: import java.util.ArrayList;
022: import java.util.List;
023:
024: import org.w3c.dom.Attr;
025: import org.w3c.dom.Document;
026: import org.w3c.dom.Element;
027: import org.w3c.dom.NamedNodeMap;
028: import org.w3c.dom.Node;
029: import org.w3c.dom.NodeList;
030: import org.w3c.dom.Text;
031:
032: /**
033: * This class contains some utility methods for working with w3c DOMs:
034: * - creation of a XPath expression denoting the absolute node position
035: * - assertEquals JUnit test implementation for comparison of DOM nodes
036: *
037: * @author mleidig
038: *
039: */
040: public class XMLUtils {
041:
042: /**
043: * Create a XPath expression denoting the node's absolute position.
044: */
045: public static String getXPath(Node node) {
046: StringBuilder sb = new StringBuilder();
047: buildXPath(node, sb);
048: return sb.toString();
049: }
050:
051: /**
052: * Build XPath expression by bottom-up traversing the node's ancestors.
053: */
054: private static void buildXPath(Node node, StringBuilder builder) {
055: if (node != null) {
056: if (node.getNodeType() == Node.ELEMENT_NODE) {
057: Element elem = (Element) node;
058: Node parentNode = elem.getParentNode();
059: if (parentNode != null
060: && parentNode.getNodeType() == Node.ELEMENT_NODE) {
061: int pos = 1;
062: Node prevNode = elem.getPreviousSibling();
063: while (prevNode != null) {
064: if (prevNode.getNodeType() == Node.ELEMENT_NODE) {
065: if (prevNode.getNodeName().equals(
066: elem.getNodeName()))
067: pos++;
068: }
069: prevNode = prevNode.getPreviousSibling();
070: }
071: builder.insert(0, "/" + elem.getNodeName() + "["
072: + pos + "]");
073: buildXPath(parentNode, builder);
074: } else {
075: builder.insert(0, "/" + elem.getNodeName());
076: }
077: } else if (node.getNodeType() == Node.ATTRIBUTE_NODE) {
078: builder.insert(0, "/@" + node.getNodeName());
079: buildXPath(((Attr) node).getOwnerElement(), builder);
080: } else if (node.getNodeType() == Node.TEXT_NODE) {
081: int pos = 1;
082: Node prevNode = node.getPreviousSibling();
083: while (prevNode != null) {
084: if (prevNode.getNodeType() == Node.TEXT_NODE)
085: pos++;
086: prevNode = prevNode.getPreviousSibling();
087: }
088: builder.insert(0, "/text()[" + pos + "]");
089: buildXPath(node.getParentNode(), builder);
090: }
091: }
092: }
093:
094: /**
095: * Compare expected and actual node in-depth and throw AssertionError if a
096: * difference is found.
097: */
098: public static void assertEquals(Node expected, Node actual)
099: throws AssertionError {
100: if (expected == null && actual == null)
101: return;
102:
103: int expNodeType = expected.getNodeType();
104: int actNodeType = actual.getNodeType();
105: if (expNodeType != actNodeType)
106: fail("Different node type.", getXPath(expected),
107: getXPath(actual));
108: if (expNodeType == actNodeType) {
109: if (expNodeType == Node.ATTRIBUTE_NODE) {
110: String expNodeName = expected.getNodeName();
111: String actNodeName = actual.getNodeName();
112: if (!expNodeName.equals(actNodeName))
113: fail("Different attribute node name.", expected,
114: actual);
115: } else if (expNodeType == Node.ELEMENT_NODE) {
116: Element expElem = (Element) expected;
117: Element actElem = (Element) actual;
118: String expNodeName = expected.getNodeName();
119: String actNodeName = actual.getNodeName();
120: if (!expNodeName.equals(actNodeName))
121: fail("Different element node name.", expected,
122: actual);
123: NamedNodeMap expAttrMap = expElem.getAttributes();
124: NamedNodeMap actAttrMap = actElem.getAttributes();
125: for (int i = 0; i < expAttrMap.getLength(); i++) {
126: Attr expAttr = (Attr) expAttrMap.item(i);
127: Attr actAttr = (Attr) actAttrMap
128: .getNamedItem(expAttr.getName());
129: if (actAttr == null)
130: fail("Missing attribute.", getXPath(expAttr),
131: null);
132: String expVal = expAttr.getValue();
133: String actVal = actAttr.getValue();
134: if (!expVal.equals(actVal))
135: fail(
136: "Different attribute values",
137: getXPath(expAttr) + "='" + expVal + "'",
138: getXPath(actAttr) + "='" + actVal + "'");
139: }
140: for (int i = 0; i < actAttrMap.getLength(); i++) {
141: Attr actAttr = (Attr) actAttrMap.item(i);
142: Attr expAttr = (Attr) expAttrMap
143: .getNamedItem(actAttr.getName());
144: if (expAttr == null)
145: fail("Additional attribute.", null,
146: getXPath(actAttr));
147: }
148: NodeList expChildren = expected.getChildNodes();
149: NodeList actChildren = actual.getChildNodes();
150: if (expChildren.getLength() != actChildren.getLength())
151: fail("Different number of children.", "count("
152: + getXPath(expected) + "/child::node())="
153: + expected.getChildNodes().getLength(),
154: "count("
155: + getXPath(actual)
156: + "/child::node())="
157: + actual.getChildNodes()
158: .getLength());
159: for (int i = 0; i < expChildren.getLength(); i++) {
160: assertEquals(expChildren.item(i), actChildren
161: .item(i));
162: }
163: } else if (expNodeType == Node.TEXT_NODE) {
164: Text expTextNode = (Text) expected;
165: Text actTextNode = (Text) actual;
166: String expText = expTextNode.getTextContent();
167: String actText = actTextNode.getTextContent();
168: if (!expText.equals(actText))
169: fail("Different text content.", getXPath(expected)
170: + "='" + getTextContent(expTextNode, 20)
171: + "'", getXPath(actual) + "='"
172: + getTextContent(actTextNode, 20) + "'");
173: } else if (expNodeType == Node.DOCUMENT_NODE) {
174: Document expDoc = (Document) expected;
175: Document actDoc = (Document) actual;
176: Element expElem = expDoc.getDocumentElement();
177: Element actElem = actDoc.getDocumentElement();
178: assertEquals(expElem, actElem);
179: }
180: }
181: }
182:
183: private static String getTextContent(Text text, int maxLen) {
184: String str = text.getTextContent();
185: str = str.replaceAll("\n", "\\\\n");
186: str = str.replaceAll("\r", "\\\\r");
187: if (str.length() > maxLen)
188: str = str.substring(0, maxLen - 3) + "...";
189: return str;
190: }
191:
192: private static void fail(String message, Node expected, Node actual)
193: throws AssertionError {
194: fail(message, getXPath(expected), getXPath(actual));
195: }
196:
197: private static void fail(String message, String expected,
198: String actual) throws AssertionError {
199: StringBuilder sb = new StringBuilder();
200: sb.append(message);
201: sb.append(" Expected: \"");
202: sb.append(expected);
203: sb.append("\" but was: \"");
204: sb.append(actual);
205: sb.append("\"");
206: throw new AssertionError(sb.toString());
207: }
208:
209: /**
210: * Strips whitespace (empty text nodes) from DOM
211: */
212: public static void stripWhitespace(Node node) {
213: if (node.getNodeType() == Node.ELEMENT_NODE) {
214: Element elem = (Element) node;
215: NodeList nl = elem.getChildNodes();
216: List<Node> nodes = new ArrayList<Node>();
217: for (int i = 0; i < nl.getLength(); i++)
218: nodes.add(nl.item(i));
219: for (Node n : nodes)
220: stripWhitespace(n);
221: } else if (node.getNodeType() == Node.TEXT_NODE) {
222: Text text = (Text) node;
223: String content = text.getTextContent();
224: if (content.trim().equals(""))
225: node.getParentNode().removeChild(node);
226: } else if (node.getNodeType() == Node.DOCUMENT_NODE) {
227: stripWhitespace(node.getFirstChild());
228: }
229: }
230:
231: }
|