1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.springframework.xml.stream;
18
19 import java.io.IOException;
20 import java.io.InputStream;
21 import java.util.Arrays;
22 import javax.xml.stream.XMLInputFactory;
23 import javax.xml.stream.XMLStreamException;
24
25 import junit.framework.TestCase;
26 import org.easymock.AbstractMatcher;
27 import org.easymock.MockControl;
28 import org.xml.sax.Attributes;
29 import org.xml.sax.ContentHandler;
30 import org.xml.sax.InputSource;
31 import org.xml.sax.Locator;
32 import org.xml.sax.SAXException;
33 import org.xml.sax.XMLReader;
34 import org.xml.sax.ext.LexicalHandler;
35 import org.xml.sax.helpers.AttributesImpl;
36 import org.xml.sax.helpers.XMLReaderFactory;
37
38 import org.springframework.core.io.ClassPathResource;
39 import org.springframework.core.io.Resource;
40 import org.springframework.xml.sax.SaxUtils;
41
42 public abstract class AbstractStaxXmlReaderTestCase extends TestCase {
43
44 protected static XMLInputFactory inputFactory;
45
46 private Resource testContentHandler;
47
48 private XMLReader standardReader;
49
50 private MockControl contentHandlerControl;
51
52 private ContentHandler contentHandler;
53
54 protected void setUp() throws Exception {
55 inputFactory = XMLInputFactory.newInstance();
56 standardReader = XMLReaderFactory.createXMLReader();
57 contentHandlerControl = MockControl.createStrictControl(ContentHandler.class);
58 contentHandlerControl.setDefaultMatcher(new SaxArgumentMatcher());
59 ContentHandler contentHandlerMock = (ContentHandler) contentHandlerControl.getMock();
60 contentHandler = new CopyingContentHandler(contentHandlerMock);
61 standardReader.setContentHandler(contentHandler);
62
63 testContentHandler = new ClassPathResource("testContentHandler.xml", getClass());
64 }
65
66 public void testContentHandlerNamespacesNoPrefixes() throws SAXException, IOException, XMLStreamException {
67 standardReader.setFeature("http://xml.org/sax/features/namespaces", true);
68 standardReader.setFeature("http://xml.org/sax/features/namespace-prefixes", false);
69
70 standardReader.parse(SaxUtils.createInputSource(testContentHandler));
71 contentHandlerControl.replay();
72
73 AbstractStaxXmlReader staxXmlReader = createStaxXmlReader(testContentHandler.getInputStream());
74 staxXmlReader.setFeature("http://xml.org/sax/features/namespaces", true);
75 staxXmlReader.setFeature("http://xml.org/sax/features/namespace-prefixes", false);
76
77 staxXmlReader.setContentHandler(contentHandler);
78 staxXmlReader.parse(new InputSource());
79 contentHandlerControl.verify();
80 }
81
82 public void testContentHandlerNamespacesPrefixes() throws SAXException, IOException, XMLStreamException {
83 standardReader.setFeature("http://xml.org/sax/features/namespaces", true);
84 standardReader.setFeature("http://xml.org/sax/features/namespace-prefixes", true);
85
86 standardReader.parse(SaxUtils.createInputSource(testContentHandler));
87 contentHandlerControl.replay();
88
89 AbstractStaxXmlReader staxXmlReader = createStaxXmlReader(testContentHandler.getInputStream());
90 staxXmlReader.setFeature("http://xml.org/sax/features/namespaces", true);
91 staxXmlReader.setFeature("http://xml.org/sax/features/namespace-prefixes", true);
92
93 staxXmlReader.setContentHandler(contentHandler);
94 staxXmlReader.parse(new InputSource());
95 contentHandlerControl.verify();
96 }
97
98 public void testContentHandlerNoNamespacesPrefixes() throws SAXException, IOException, XMLStreamException {
99 standardReader.setFeature("http://xml.org/sax/features/namespaces", false);
100 standardReader.setFeature("http://xml.org/sax/features/namespace-prefixes", true);
101
102 standardReader.parse(SaxUtils.createInputSource(testContentHandler));
103 contentHandlerControl.replay();
104
105 AbstractStaxXmlReader staxXmlReader = createStaxXmlReader(testContentHandler.getInputStream());
106 staxXmlReader.setFeature("http://xml.org/sax/features/namespaces", false);
107 staxXmlReader.setFeature("http://xml.org/sax/features/namespace-prefixes", true);
108
109 staxXmlReader.setContentHandler(contentHandler);
110 staxXmlReader.parse(new InputSource());
111 contentHandlerControl.verify();
112 }
113
114 public void testLexicalHandler() throws SAXException, IOException, XMLStreamException {
115
116 MockControl lexicalHandlerControl = MockControl.createStrictControl(LexicalHandler.class);
117 lexicalHandlerControl.setDefaultMatcher(new SaxArgumentMatcher());
118 LexicalHandler lexicalHandlerMock = (LexicalHandler) lexicalHandlerControl.getMock();
119 LexicalHandler lexicalHandler = new CopyingLexicalHandler(lexicalHandlerMock);
120
121 Resource testLexicalHandlerXml = new ClassPathResource("testLexicalHandler.xml", getClass());
122
123 standardReader.setContentHandler(null);
124 standardReader.setProperty("http://xml.org/sax/properties/lexical-handler", lexicalHandler);
125 standardReader.parse(SaxUtils.createInputSource(testLexicalHandlerXml));
126 lexicalHandlerControl.replay();
127
128 inputFactory.setProperty("javax.xml.stream.isCoalescing", Boolean.FALSE);
129 inputFactory.setProperty("http://java.sun.com/xml/stream/properties/report-cdata-event", Boolean.TRUE);
130 inputFactory.setProperty("javax.xml.stream.isReplacingEntityReferences", Boolean.FALSE);
131 inputFactory.setProperty("javax.xml.stream.isSupportingExternalEntities", Boolean.FALSE);
132
133 AbstractStaxXmlReader staxXmlReader = createStaxXmlReader(testLexicalHandlerXml.getInputStream());
134
135 staxXmlReader.setProperty("http://xml.org/sax/properties/lexical-handler", lexicalHandler);
136 staxXmlReader.parse(new InputSource());
137 lexicalHandlerControl.verify();
138 }
139
140 protected abstract AbstractStaxXmlReader createStaxXmlReader(InputStream inputStream) throws XMLStreamException;
141
142
143 protected static class SaxArgumentMatcher extends AbstractMatcher {
144
145 public boolean matches(Object[] expected, Object[] actual) {
146 if (expected == actual) {
147 return true;
148 }
149 if (expected == null || actual == null) {
150 return false;
151 }
152 if (expected.length != actual.length) {
153 throw new IllegalArgumentException("Expected and actual arguments must have the same size");
154 }
155 if (expected.length == 3 && expected[0] instanceof char[] && expected[1] instanceof Integer &&
156 expected[2] instanceof Integer) {
157
158 String expectedString = new String((char[]) expected[0], ((Integer) expected[1]).intValue(),
159 ((Integer) expected[2]).intValue());
160 String actualString = new String((char[]) actual[0], ((Integer) actual[1]).intValue(),
161 ((Integer) actual[2]).intValue());
162 return expectedString.equals(actualString);
163 }
164 else if (expected.length == 1 && (expected[0] instanceof Locator)) {
165 return true;
166 }
167 else {
168 return super.matches(expected, actual);
169 }
170 }
171
172 protected boolean argumentMatches(Object expected, Object actual) {
173 if (expected instanceof char[]) {
174 return Arrays.equals((char[]) expected, (char[]) actual);
175 }
176 else if (expected instanceof Attributes) {
177 Attributes expectedAttributes = (Attributes) expected;
178 Attributes actualAttributes = (Attributes) actual;
179 if (expectedAttributes.getLength() != actualAttributes.getLength()) {
180 return false;
181 }
182 for (int i = 0; i < expectedAttributes.getLength(); i++) {
183 boolean found = false;
184 for (int j = 0; j < actualAttributes.getLength(); j++) {
185 if (expectedAttributes.getURI(i).equals(actualAttributes.getURI(j)) &&
186 expectedAttributes.getQName(i).equals(actualAttributes.getQName(j)) &&
187 expectedAttributes.getType(i).equals(actualAttributes.getType(j)) &&
188 expectedAttributes.getValue(i).equals(actualAttributes.getValue(j))) {
189 found = true;
190 break;
191 }
192 }
193 if (!found) {
194 return false;
195 }
196 }
197 return true;
198 }
199 else {
200 return super.argumentMatches(expected, actual);
201 }
202 }
203
204 public String toString(Object[] arguments) {
205 if (arguments != null && arguments.length == 3 && arguments[0] instanceof char[] &&
206 arguments[1] instanceof Integer && arguments[2] instanceof Integer) {
207 return new String((char[]) arguments[0], ((Integer) arguments[1]).intValue(),
208 ((Integer) arguments[2]).intValue());
209 }
210 else {
211 return super.toString(arguments);
212 }
213 }
214
215 protected String argumentToString(Object argument) {
216 if (argument instanceof char[]) {
217 char[] array = (char[]) argument;
218 StringBuffer buffer = new StringBuffer();
219 for (int i = 0; i < array.length; i++) {
220 buffer.append(array[i]);
221 }
222 return buffer.toString();
223 }
224 else if (argument instanceof Attributes) {
225 Attributes attributes = (Attributes) argument;
226 StringBuffer buffer = new StringBuffer("[");
227 for (int i = 0; i < attributes.getLength(); i++) {
228 if (attributes.getURI(i).length() != 0) {
229 buffer.append('{');
230 buffer.append(attributes.getURI(i));
231 buffer.append('}');
232 }
233
234
235
236
237
238 if (attributes.getQName(i).length() != 0) {
239 buffer.append(attributes.getQName(i));
240 }
241 buffer.append('=');
242 buffer.append(attributes.getValue(i));
243 if (i < attributes.getLength() - 1) {
244 buffer.append(", ");
245 }
246 }
247 buffer.append(']');
248 return buffer.toString();
249 }
250 else if (argument instanceof Locator) {
251 Locator locator = (Locator) argument;
252 StringBuffer buffer = new StringBuffer("[");
253 buffer.append(locator.getLineNumber());
254 buffer.append(',');
255 buffer.append(locator.getColumnNumber());
256 buffer.append(']');
257 return buffer.toString();
258 }
259 else {
260 return super.argumentToString(argument);
261 }
262 }
263 }
264
265 private static class CopyingContentHandler implements ContentHandler {
266
267 private final ContentHandler wrappee;
268
269 private CopyingContentHandler(ContentHandler wrappee) {
270 this.wrappee = wrappee;
271 }
272
273 public void setDocumentLocator(Locator locator) {
274 wrappee.setDocumentLocator(locator);
275 }
276
277 public void startDocument() throws SAXException {
278 wrappee.startDocument();
279 }
280
281 public void endDocument() throws SAXException {
282 wrappee.endDocument();
283 }
284
285 public void startPrefixMapping(String prefix, String uri) throws SAXException {
286 wrappee.startPrefixMapping(prefix, uri);
287 }
288
289 public void endPrefixMapping(String prefix) throws SAXException {
290 wrappee.endPrefixMapping(prefix);
291 }
292
293 public void startElement(String uri, String localName, String qName, Attributes attributes)
294 throws SAXException {
295 wrappee.startElement(uri, localName, qName, new AttributesImpl(attributes));
296 }
297
298 public void endElement(String uri, String localName, String qName) throws SAXException {
299 wrappee.endElement(uri, localName, qName);
300 }
301
302 public void characters(char ch[], int start, int length) throws SAXException {
303 wrappee.characters(copy(ch), start, length);
304 }
305
306 public void ignorableWhitespace(char ch[], int start, int length) throws SAXException {
307 }
308
309 public void processingInstruction(String target, String data) throws SAXException {
310 wrappee.processingInstruction(target, data);
311 }
312
313 public void skippedEntity(String name) throws SAXException {
314 wrappee.skippedEntity(name);
315 }
316 }
317
318 private static class CopyingLexicalHandler implements LexicalHandler {
319
320 private final LexicalHandler wrappee;
321
322 private CopyingLexicalHandler(LexicalHandler wrappee) {
323 this.wrappee = wrappee;
324 }
325
326 public void startDTD(String name, String publicId, String systemId) throws SAXException {
327 wrappee.startDTD("element", publicId, systemId);
328 }
329
330 public void endDTD() throws SAXException {
331 wrappee.endDTD();
332 }
333
334 public void startEntity(String name) throws SAXException {
335 wrappee.startEntity(name);
336 }
337
338 public void endEntity(String name) throws SAXException {
339 wrappee.endEntity(name);
340 }
341
342 public void startCDATA() throws SAXException {
343 wrappee.startCDATA();
344 }
345
346 public void endCDATA() throws SAXException {
347 wrappee.endCDATA();
348 }
349
350 public void comment(char ch[], int start, int length) throws SAXException {
351 wrappee.comment(copy(ch), start, length);
352 }
353 }
354
355 private static char[] copy(char[] ch) {
356 char[] copy = new char[ch.length];
357 System.arraycopy(ch, 0, copy, 0, ch.length);
358 return copy;
359 }
360
361 }