1 /*
2  * Copyright 2002-2007 the original author or authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package org.springframework.web.servlet.view.xslt;
18 
19 import java.util.ArrayList;
20 import java.util.HashMap;
21 import java.util.Iterator;
22 import java.util.List;
23 import java.util.Map;
24 import java.util.Properties;
25 
26 import javax.servlet.http.HttpServletRequest;
27 import javax.servlet.http.HttpServletResponse;
28 import javax.xml.parsers.DocumentBuilderFactory;
29 import javax.xml.transform.ErrorListener;
30 import javax.xml.transform.Source;
31 import javax.xml.transform.TransformerException;
32 import javax.xml.transform.dom.DOMSource;
33 
34 import junit.framework.TestCase;
35 import org.w3c.dom.Document;
36 import org.w3c.dom.Element;
37 import org.w3c.dom.Text;
38 
39 import org.springframework.context.ApplicationContextException;
40 import org.springframework.core.JdkVersion;
41 import org.springframework.core.io.ClassPathResource;
42 import org.springframework.core.io.FileSystemResource;
43 import org.springframework.mock.web.MockHttpServletRequest;
44 import org.springframework.mock.web.MockHttpServletResponse;
45 import org.springframework.web.servlet.ModelAndView;
46 
47 /**
48  * Unit tests for the {@link AbstractXsltView} class.
49  *
50  * @author Darren Davison
51  * @author Rick Evans
52  * @author Juergen Hoeller
53  * @since 11.03.2005
54  */
55 public class TestXsltViewTests extends TestCase {
56 
57 	private TestXsltView view;
58 
59 	private int warnings = 0;
60 
61 	private int errors = 0;
62 
63 	private int fatal = 0;
64 
65 
setUp()66 	public void setUp() {
67 		this.view = new TestXsltView();
68 	}
69 
70 
testNoSuchStylesheet()71 	public void testNoSuchStylesheet() {
72 		view.setStylesheetLocation(new FileSystemResource("/does/not/exist.xsl"));
73 		try {
74 			view.initApplicationContext();
75 			fail("Must have thrown ApplicationContextException");
76 		}
77 		catch (ApplicationContextException expected) {
78 		}
79 	}
80 
testCustomErrorListener()81 	public void testCustomErrorListener() {
82 		view.setErrorListener(new ErrorListener() {
83 			public void warning(TransformerException ex) {
84 				incWarnings();
85 			}
86 			public void error(TransformerException ex) {
87 				incErrors();
88 			}
89 			public void fatalError(TransformerException ex) {
90 				incFatals();
91 			}
92 		});
93 
94 		// loaded stylesheet is not well formed
95 		view.setStylesheetLocation(new ClassPathResource("org/springframework/web/servlet/view/xslt/errors.xsl"));
96 		try {
97 			view.initApplicationContext();
98 		}
99 		catch (ApplicationContextException ex) {
100 			// shouldn't really happen, but can be let through by XSLT engine
101 			assertTrue(ex.getCause() instanceof TransformerException);
102 		}
103 		assertEquals(1, fatal);
104 		assertEquals(1, errors);
105 		assertEquals(0, warnings);
106 	}
107 
testRender()108 	public void testRender() throws Exception {
109 		MockHttpServletRequest request = new MockHttpServletRequest();
110 		MockHttpServletResponse response = new MockHttpServletResponse();
111 
112 		AbstractXsltView view = new AbstractXsltView() {
113 			protected Source createXsltSource(Map model, String root, HttpServletRequest request, HttpServletResponse response) throws Exception {
114 				Hero hero = (Hero) model.get("hero");
115 				Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
116 				Element node = document.createElement(root);
117 				node.setAttribute("name", hero.getName());
118 				node.setAttribute("age", hero.getAge() + "");
119 				node.setAttribute("catchphrase", hero.getCatchphrase());
120 				return new DOMSource(node);
121 			}
122 		};
123 
124 		view.setStylesheetLocation(new ClassPathResource("org/springframework/web/servlet/view/xslt/sunnyDay.xsl"));
125 		view.setIndent(true);
126 		view.initApplicationContext();
127 
128 		view.render(new ModelAndView().addObject("hero", new Hero("Jet", 24, "BOOM")).getModel(), request, response);
129 		assertEquals("text/html;charset=ISO-8859-1", response.getContentType());
130 		String text = response.getContentAsString();
131 		assertEquals("<hero name=\"Jet\" age=\"24\" catchphrase=\"BOOM\" sex=\"Female\"/>", text.trim());
132 	}
133 
testRenderWithCustomContentType()134 	public void testRenderWithCustomContentType() throws Exception {
135 		MockHttpServletRequest request = new MockHttpServletRequest();
136 		MockHttpServletResponse response = new MockHttpServletResponse();
137 
138 		AbstractXsltView view = new AbstractXsltView() {
139 			protected Source createXsltSource(Map model, String root, HttpServletRequest request, HttpServletResponse response) throws Exception {
140 				Hero hero = (Hero) model.get("hero");
141 				Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
142 				Element node = document.createElement(root);
143 				node.setAttribute("name", hero.getName());
144 				node.setAttribute("age", hero.getAge() + "");
145 				node.setAttribute("catchphrase", hero.getCatchphrase());
146 				return new DOMSource(node);
147 			}
148 		};
149 
150 		view.setContentType("text/plain");
151 		view.setStylesheetLocation(new ClassPathResource("org/springframework/web/servlet/view/xslt/sunnyDay.xsl"));
152 		view.setIndent(true);
153 		view.initApplicationContext();
154 
155 		view.render(new ModelAndView().addObject("hero", new Hero("Jet", 24, "BOOM")).getModel(), request, response);
156 		assertEquals("text/plain", response.getContentType());
157 		String text = response.getContentAsString();
158 		assertEquals("<hero name=\"Jet\" age=\"24\" catchphrase=\"BOOM\" sex=\"Female\"/>", text.trim());
159 	}
160 
testRenderWithSingleSourceInModel()161 	public void testRenderWithSingleSourceInModel() throws Exception {
162 		MockHttpServletRequest request = new MockHttpServletRequest();
163 		MockHttpServletResponse response = new MockHttpServletResponse();
164 
165 		AbstractXsltView view = new AbstractXsltView() {
166 			protected Map getParameters(HttpServletRequest request) {
167 				Map parameters = new HashMap();
168 				parameters.put("sex", "Male");
169 				return parameters;
170 			}
171 		};
172 
173 		view.setStylesheetLocation(new ClassPathResource("org/springframework/web/servlet/view/xslt/sunnyDay.xsl"));
174 		Properties outputProperties = new Properties();
175 		outputProperties.setProperty("indent", "false");
176 		view.setOutputProperties(outputProperties);
177 		view.initApplicationContext();
178 
179 		Hero hero = new Hero("Jet", 24, "BOOM");
180 		Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
181 		Element node = document.createElement("hero");
182 		node.setAttribute("name", hero.getName());
183 		node.setAttribute("age", hero.getAge() + "");
184 		node.setAttribute("catchphrase", hero.getCatchphrase());
185 
186 		view.render(new ModelAndView().addObject("hero", new DOMSource(node)).getModel(), request, response);
187 		assertEquals("text/html;charset=ISO-8859-1", response.getContentType());
188 		String text = response.getContentAsString();
189 		assertEquals("<hero name=\"Jet\" age=\"24\" catchphrase=\"BOOM\" sex=\"Male\"/>", text.trim());
190 	}
191 
testRenderWithSingleNodeInModel()192 	public void testRenderWithSingleNodeInModel() throws Exception {
193 		MockHttpServletRequest request = new MockHttpServletRequest();
194 		MockHttpServletResponse response = new MockHttpServletResponse();
195 		response.setWriterAccessAllowed(false);
196 
197 		AbstractXsltView view = new AbstractXsltView() {
198 			protected Map getParameters(HttpServletRequest request) {
199 				Map parameters = new HashMap();
200 				parameters.put("sex", "Male");
201 				return parameters;
202 			}
203 		};
204 		view.setStylesheetLocation(new ClassPathResource("org/springframework/web/servlet/view/xslt/sunnyDay.xsl"));
205 		view.initApplicationContext();
206 
207 		Hero hero = new Hero("Jet", 24, "BOOM");
208 		Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
209 		Element node = document.createElement("hero");
210 		node.setAttribute("name", hero.getName());
211 		node.setAttribute("age", hero.getAge() + "");
212 		node.setAttribute("catchphrase", hero.getCatchphrase());
213 
214 		view.render(new ModelAndView().addObject("hero", node).getModel(), request, response);
215 		String text = response.getContentAsString();
216 		assertEquals("<hero name=\"Jet\" age=\"24\" catchphrase=\"BOOM\" sex=\"Male\"/>", text.trim());
217 	}
218 
testRenderWithNoStylesheetSpecified()219 	public void testRenderWithNoStylesheetSpecified() throws Exception {
220 		MockHttpServletRequest request = new MockHttpServletRequest();
221 		MockHttpServletResponse response = new MockHttpServletResponse();
222 
223 		AbstractXsltView view = new AbstractXsltView() {
224 			protected Map getParameters(HttpServletRequest request) {
225 				Map parameters = new HashMap();
226 				parameters.put("sex", "Male");
227 				return parameters;
228 			}
229 		};
230 
231 		Properties outputProperties = new Properties();
232 		outputProperties.setProperty("indent", "false");
233 		view.setOutputProperties(outputProperties);
234 		view.initApplicationContext();
235 
236 		Hero hero = new Hero("Jet", 24, "BOOM");
237 		Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
238 		Element node = document.createElement("hero");
239 		node.setAttribute("name", hero.getName());
240 		node.setAttribute("age", hero.getAge() + "");
241 		node.setAttribute("catchphrase", hero.getCatchphrase());
242 
243 		view.render(new ModelAndView().addObject("hero", new DOMSource(node)).getModel(), request, response);
244 		assertEquals("text/xml;charset=ISO-8859-1", response.getContentType());
245 		String text = response.getContentAsString().trim();
246 		assertTrue(text.startsWith("<?xml"));
247 		assertTrue(text.indexOf("<hero") != -1);
248 		assertTrue(text.indexOf("age=\"24\"") != -1);
249 		assertTrue(text.indexOf("catchphrase=\"BOOM\"") != -1);
250 		assertTrue(text.indexOf("name=\"Jet\"") != -1);
251 	}
252 
testRenderSingleNodeInModelWithExplicitDocRootName()253 	public void testRenderSingleNodeInModelWithExplicitDocRootName() throws Exception {
254 		MockHttpServletRequest request = new MockHttpServletRequest();
255 		MockHttpServletResponse response = new MockHttpServletResponse();
256 		response.setOutputStreamAccessAllowed(false);
257 
258 		AbstractXsltView view = new AbstractXsltView() {
259 			protected Source createXsltSource(Map model, String root, HttpServletRequest request, HttpServletResponse response) throws Exception {
260 				Hero hero = (Hero) model.get("hero");
261 				Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
262 				Element node = document.createElement(root);
263 				node.setAttribute("name", hero.getName());
264 				node.setAttribute("age", hero.getAge() + "");
265 				node.setAttribute("catchphrase", hero.getCatchphrase());
266 				return new DOMSource(node);
267 			}
268 			protected Map getParameters(HttpServletRequest request) {
269 				Map parameters = new HashMap();
270 				parameters.put("sex", "Male");
271 				return parameters;
272 			}
273 			protected boolean useWriter() {
274 				return true;
275 			}
276 		};
277 
278 		view.setStylesheetLocation(new ClassPathResource("org/springframework/web/servlet/view/xslt/sunnyDayExplicitRoot.xsl"));
279 		view.setUseSingleModelNameAsRoot(false);
280 		view.setRoot("baddie");
281 		view.initApplicationContext();
282 		view.render(new ModelAndView().addObject("hero", new Hero("Jet", 24, "BOOM")).getModel(), request, response);
283 		String text = response.getContentAsString();
284 		assertTrue(text.trim().startsWith("<baddie "));
285 	}
286 
287 	/**
288 	 * Not a test per-se, but rather only here to validate the example
289 	 * given in the reference documentation.
290 	 */
testMyFirstWordsExampleFromTheReferenceDocumentation()291 	public void testMyFirstWordsExampleFromTheReferenceDocumentation() throws Exception {
292 		// TODO: Why does this test not even work on JDK 1.4?
293 		// Maybe because of the Xalan version there?
294 		if (JdkVersion.getMajorJavaVersion() < JdkVersion.JAVA_15) {
295 			return;
296 		}
297 
298 		MockHttpServletRequest request = new MockHttpServletRequest();
299 		MockHttpServletResponse response = new MockHttpServletResponse();
300 
301 		AbstractXsltView view = new AbstractXsltView() {
302 			protected Source createXsltSource(
303 					Map model, String rootName, HttpServletRequest request, HttpServletResponse response)
304 					throws Exception {
305 				Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
306 				Element root = document.createElement(rootName);
307 				List words = (List) model.get("wordList");
308 				for (Iterator it = words.iterator(); it.hasNext();) {
309 					String nextWord = (String) it.next();
310 					Element wordNode = document.createElement("word");
311 					Text textNode = document.createTextNode(nextWord);
312 					wordNode.appendChild(textNode);
313 					root.appendChild(wordNode);
314 				}
315 				return new DOMSource(root);
316 			}
317 		};
318 
319 		view.setStylesheetLocation(new ClassPathResource("org/springframework/web/servlet/view/xslt/firstWords.xsl"));
320 		view.setIndent(true);
321 		view.initApplicationContext();
322 
323 		Map map = new HashMap();
324 		List wordList = new ArrayList();
325 		wordList.add("hello");
326 		wordList.add("world");
327 		map.put("wordList", wordList);
328 
329 		view.render(new ModelAndView("home", map).getModel(), request, response);
330 		String text = response.getContentAsString();
331 		assertTrue(text.trim().startsWith("<html"));
332 	}
333 
334 
incWarnings()335 	private void incWarnings() {
336 		warnings++;
337 	}
338 
incErrors()339 	private void incErrors() {
340 		errors++;
341 	}
342 
incFatals()343 	private void incFatals() {
344 		fatal++;
345 	}
346 
347 
348 	private static final class TestXsltView extends AbstractXsltView {
349 	}
350 
351 
352 	private static final class Hero {
353 
354 		private String name;
355 		private int age;
356 		private String catchphrase;
357 
Hero()358 		public Hero() {
359 		}
360 
Hero(String name, int age, String catchphrase)361 		public Hero(String name, int age, String catchphrase) {
362 			this.name = name;
363 			this.age = age;
364 			this.catchphrase = catchphrase;
365 		}
366 
getCatchphrase()367 		public String getCatchphrase() {
368 			return catchphrase;
369 		}
370 
setCatchphrase(String catchphrase)371 		public void setCatchphrase(String catchphrase) {
372 			this.catchphrase = catchphrase;
373 		}
374 
getName()375 		public String getName() {
376 			return name;
377 		}
378 
setName(String name)379 		public void setName(String name) {
380 			this.name = name;
381 		}
382 
getAge()383 		public int getAge() {
384 			return age;
385 		}
386 
setAge(int age)387 		public void setAge(int age) {
388 			this.age = age;
389 		}
390 	}
391 
392 }
393