1 package com.atlassian.maven.plugins.amps;
2
3 import java.util.List;
4
5 import com.atlassian.fugue.Option;
6
7 import org.dom4j.Document;
8 import org.dom4j.DocumentException;
9 import org.dom4j.DocumentHelper;
10 import org.dom4j.Element;
11 import org.dom4j.Node;
12 import org.hamcrest.Description;
13 import org.hamcrest.Matcher;
14 import org.hamcrest.TypeSafeDiagnosingMatcher;
15
16 import static com.atlassian.fugue.Option.some;
17 import static org.hamcrest.Matchers.equalTo;
18 import static org.hamcrest.Matchers.iterableWithSize;
19 import static org.junit.Assert.assertEquals;
20
21 public class XmlMatchers
22 {
23 private static final String DUMMY_NAMESPACE_PREFIX = "x";
24
25 public static class XmlWrapper
26 {
27 private final Element root;
28 private final Option<String> defaultNamespacePrefix;
29
30 private XmlWrapper(Element root)
31 {
32 this(root, Option.none(String.class));
33 }
34
35 private XmlWrapper(Element root, Option<String> defaultNamespacePrefix)
36 {
37 this.root = root;
38 this.defaultNamespacePrefix = defaultNamespacePrefix;
39 }
40
41 private String xpath(String path)
42 {
43 for (String ns : defaultNamespacePrefix)
44 {
45 return path.replaceAll("/(?!/)", "/" + ns + ":");
46 }
47 return path;
48 }
49
50 private void dump()
51 {
52 System.err.println("Document being matched:");
53 System.err.println(root.asXML());
54 }
55 }
56
57 public static XmlWrapper xml(Element root)
58 {
59 return new XmlWrapper(root);
60 }
61
62 public static XmlWrapper xml(Element root, String defaultNamespacePrefix)
63 {
64 return new XmlWrapper(root, some(defaultNamespacePrefix));
65 }
66
67 public static XmlWrapper xml(String content) throws DocumentException
68 {
69 Document doc = DocumentHelper.parseText(content);
70 String defaultNamespace = doc.getRootElement().getNamespace().getURI();
71 if (defaultNamespace != null)
72 {
73
74 doc.getRootElement().addNamespace(DUMMY_NAMESPACE_PREFIX, defaultNamespace);
75 return xml(doc.getRootElement(), DUMMY_NAMESPACE_PREFIX);
76 }
77 else
78 {
79 return xml(doc.getRootElement());
80 }
81 }
82
83 public static XmlWrapper xml(String content, String rootElementName) throws DocumentException
84 {
85 XmlWrapper xml = xml(content);
86 assertEquals("Wrong root element type", rootElementName, xml.root.getName());
87 return xml;
88 }
89
90 public static Matcher<XmlWrapper> node(final String xpath, final Matcher<? super Node> nodeMatcher)
91 {
92 return new TypeSafeDiagnosingMatcher<XmlWrapper>()
93 {
94 protected boolean matchesSafely(XmlWrapper xml, Description mismatchDescription)
95 {
96 Node node = xml.root.selectSingleNode(xml.xpath(xpath));
97 if (!nodeMatcher.matches(node))
98 {
99 nodeMatcher.describeMismatch(node, mismatchDescription);
100 System.err.println("Document being matched:");
101 System.err.println(xml.root.asXML());
102 return false;
103 }
104 return true;
105 }
106
107 public void describeTo(Description description)
108 {
109 description.appendText("node at [" + xpath + "] ");
110 nodeMatcher.describeTo(description);
111 }
112 };
113 }
114
115 public static Matcher<XmlWrapper> nodes(final String xpath, final Matcher<Iterable<Node>> nodesMatcher)
116 {
117 return new TypeSafeDiagnosingMatcher<XmlWrapper>()
118 {
119 @SuppressWarnings("unchecked")
120 protected boolean matchesSafely(XmlWrapper xml, Description mismatchDescription)
121 {
122 List<Node> nodes = (List<Node>) xml.root.selectNodes(xml.xpath(xpath));
123 if (!nodesMatcher.matches(nodes))
124 {
125 nodesMatcher.describeMismatch(nodes, mismatchDescription);
126 xml.dump();
127 return false;
128 }
129 return true;
130 }
131
132 public void describeTo(Description description)
133 {
134 description.appendText("nodes at [" + xpath + "] ");
135 nodesMatcher.describeTo(description);
136 }
137 };
138 }
139
140 public static Matcher<Node> nodeText(final Matcher<? super String> textMatcher)
141 {
142 return new TypeSafeDiagnosingMatcher<Node>()
143 {
144 protected boolean matchesSafely(Node node, Description mismatchDescription)
145 {
146 if (node == null)
147 {
148 mismatchDescription.appendText("node did not exist");
149 return false;
150 }
151 else if (!textMatcher.matches(node.getText().trim()))
152 {
153 textMatcher.describeMismatch(node.getText().trim(), mismatchDescription);
154 return false;
155 }
156 return true;
157 }
158
159 public void describeTo(Description description)
160 {
161 description.appendText("with text ");
162 textMatcher.describeTo(description);
163 }
164 };
165 }
166
167 public static Matcher<Node> nodeTextEquals(final String text)
168 {
169 return nodeText(equalTo(text));
170 }
171
172 public static Matcher<Iterable<Node>> nodeCount(final int count)
173 {
174 return iterableWithSize(count);
175 }
176 }