1 package com.atlassian.plugins.codegen;
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 = "xmlMatchersDummyNamespace";
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 String dump()
51 {
52 return "\nDocument being matched:\n" + root.asXML();
53 }
54 }
55
56 public static XmlWrapper xml(Element root)
57 {
58 return new XmlWrapper(root);
59 }
60
61 public static XmlWrapper xml(Element root, String defaultNamespacePrefix)
62 {
63 return new XmlWrapper(root, some(defaultNamespacePrefix));
64 }
65
66 public static XmlWrapper xml(String content) throws DocumentException
67 {
68 Document doc = DocumentHelper.parseText(content);
69 String defaultNamespace = doc.getRootElement().getNamespace().getURI();
70 if ((defaultNamespace != null) && !defaultNamespace.equals(""))
71 {
72
73 doc.getRootElement().addNamespace(DUMMY_NAMESPACE_PREFIX, defaultNamespace);
74 return xml(doc.getRootElement(), DUMMY_NAMESPACE_PREFIX);
75 }
76 else
77 {
78 return xml(doc.getRootElement());
79 }
80 }
81
82 public static XmlWrapper xml(String content, String rootElementName) throws DocumentException
83 {
84 XmlWrapper xml = xml(content);
85 assertEquals("Wrong root element type", rootElementName, xml.root.getName());
86 return xml;
87 }
88
89 public static Matcher<XmlWrapper> node(final String xpath, final Matcher<? super Node> nodeMatcher)
90 {
91 return new TypeSafeDiagnosingMatcher<XmlWrapper>()
92 {
93 protected boolean matchesSafely(XmlWrapper xml, Description mismatchDescription)
94 {
95 Node node = xml.root.selectSingleNode(xml.xpath(xpath));
96 if (!nodeMatcher.matches(node))
97 {
98 nodeMatcher.describeMismatch(node, mismatchDescription);
99 mismatchDescription.appendText(xml.dump());
100 return false;
101 }
102 return true;
103 }
104
105 public void describeTo(Description description)
106 {
107 description.appendText("node at [" + xpath + "] ");
108 nodeMatcher.describeTo(description);
109 }
110 };
111 }
112
113 public static Matcher<XmlWrapper> nodes(final String xpath, final Matcher<Iterable<Node>> nodesMatcher)
114 {
115 return new TypeSafeDiagnosingMatcher<XmlWrapper>()
116 {
117 @SuppressWarnings("unchecked")
118 protected boolean matchesSafely(XmlWrapper xml, Description mismatchDescription)
119 {
120 List<Node> nodes = (List<Node>) xml.root.selectNodes(xml.xpath(xpath));
121 if (!nodesMatcher.matches(nodes))
122 {
123 nodesMatcher.describeMismatch(nodes, mismatchDescription);
124 mismatchDescription.appendText(xml.dump());
125 return false;
126 }
127 return true;
128 }
129
130 public void describeTo(Description description)
131 {
132 description.appendText("nodes at [" + xpath + "] ");
133 nodesMatcher.describeTo(description);
134 }
135 };
136 }
137
138 public static Matcher<Node> nodeText(final Matcher<? super String> textMatcher)
139 {
140 return new TypeSafeDiagnosingMatcher<Node>()
141 {
142 protected boolean matchesSafely(Node node, Description mismatchDescription)
143 {
144 if (node == null)
145 {
146 mismatchDescription.appendText("node did not exist");
147 return false;
148 }
149 else if (!textMatcher.matches(node.getText().trim()))
150 {
151 textMatcher.describeMismatch(node.getText().trim(), mismatchDescription);
152 return false;
153 }
154 return true;
155 }
156
157 public void describeTo(Description description)
158 {
159 description.appendText("with text ");
160 textMatcher.describeTo(description);
161 }
162 };
163 }
164
165 public static Matcher<Node> nodeName(final Matcher<? super String> nameMatcher)
166 {
167 return new TypeSafeDiagnosingMatcher<Node>()
168 {
169 protected boolean matchesSafely(Node node, Description mismatchDescription)
170 {
171 if (node == null)
172 {
173 mismatchDescription.appendText("node did not exist");
174 return false;
175 }
176 else if (!nameMatcher.matches(node.getName()))
177 {
178 nameMatcher.describeMismatch(node.getText().trim(), mismatchDescription);
179 return false;
180 }
181 return true;
182 }
183
184 public void describeTo(Description description)
185 {
186 description.appendText("with name ");
187 nameMatcher.describeTo(description);
188 }
189 };
190 }
191
192 public static Matcher<Node> nodeTextEquals(final String text)
193 {
194 return nodeText(equalTo(text));
195 }
196
197 public static Matcher<Iterable<Node>> nodeCount(final int count)
198 {
199 return iterableWithSize(count);
200 }
201 }