1. /*
  2. * Copyright 2002-2004 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. package org.springframework.web.servlet.support;
  17. import java.util.HashMap;
  18. import java.util.Locale;
  19. import java.util.Map;
  20. import javax.servlet.ServletException;
  21. import javax.servlet.http.HttpServletRequest;
  22. import org.springframework.context.MessageSourceResolvable;
  23. import org.springframework.context.NoSuchMessageException;
  24. import org.springframework.ui.context.Theme;
  25. import org.springframework.validation.BindException;
  26. import org.springframework.validation.Errors;
  27. import org.springframework.web.bind.EscapedErrors;
  28. import org.springframework.web.context.WebApplicationContext;
  29. import org.springframework.web.util.HtmlUtils;
  30. /**
  31. * Context holder for request-specific state, like current web application
  32. * context, current locale, current theme, and potential binding errors.
  33. * Provides easy access to localized messages and Errors instances.
  34. *
  35. * <p>Suitable for exposition to views, and usage within JSP's "useBean" tag,
  36. * JSP scriptlets, JSTL EL, Velocity templates, etc. Necessary for views
  37. * that do not have access to the servlet request, like Velocity templates.
  38. *
  39. * <p>Can be instantiated manually, or automatically exposed to views as
  40. * model attribute via AbstractView's requestContextAttribute property.
  41. *
  42. * @author Juergen Hoeller
  43. * @since 03.03.2003
  44. * @see org.springframework.web.servlet.view.AbstractView#setRequestContextAttribute
  45. * @see org.springframework.web.servlet.view.UrlBasedViewResolver#setRequestContextAttribute
  46. */
  47. public class RequestContext {
  48. private final HttpServletRequest request;
  49. private final Map model;
  50. private final WebApplicationContext webApplicationContext;
  51. private final Locale locale;
  52. private final Theme theme;
  53. private boolean defaultHtmlEscape;
  54. private Map errorsMap;
  55. /**
  56. * Create a new RequestContext for the given request,
  57. * using the request attributes for Errors retrieval.
  58. * <p>This only works with InternalResourceViews, as Errors instances
  59. * are part of the model and not normally exposed as request attributes.
  60. * It will typically be used within JSPs or custom tags.
  61. * @param request current HTTP request
  62. */
  63. public RequestContext(HttpServletRequest request) throws ServletException {
  64. this(request, null);
  65. }
  66. /**
  67. * Create a new RequestContext for the given request,
  68. * using the given model attributes for Errors retrieval.
  69. * <p>This works with all View implementations.
  70. * It will typically be used by View implementations.
  71. * @param request current HTTP request
  72. * @param model the model attributes for the current view
  73. */
  74. public RequestContext(HttpServletRequest request, Map model)
  75. throws ServletException {
  76. this.request = request;
  77. this.webApplicationContext = RequestContextUtils.getWebApplicationContext(request);
  78. this.locale = RequestContextUtils.getLocale(request);
  79. this.theme = RequestContextUtils.getTheme(request);
  80. this.model = model;
  81. }
  82. /**
  83. * Return the context path of the current request,
  84. * i.e. the path that indicates the current web application.
  85. * @see javax.servlet.http.HttpServletRequest#getContextPath
  86. */
  87. public String getContextPath() {
  88. return this.request.getContextPath();
  89. }
  90. /**
  91. * Return the current WebApplicationContext.
  92. */
  93. public WebApplicationContext getWebApplicationContext() {
  94. return webApplicationContext;
  95. }
  96. /**
  97. * Return the current locale.
  98. */
  99. public Locale getLocale() {
  100. return locale;
  101. }
  102. /**
  103. * Return the current theme.
  104. */
  105. public Theme getTheme() {
  106. return theme;
  107. }
  108. /**
  109. * (De)activate default HTML escaping for messages and errors.
  110. */
  111. public void setDefaultHtmlEscape(boolean defaultHtmlEscape) {
  112. this.defaultHtmlEscape = defaultHtmlEscape;
  113. }
  114. /**
  115. * Is default HTML escaping active?
  116. */
  117. public boolean isDefaultHtmlEscape() {
  118. return defaultHtmlEscape;
  119. }
  120. /**
  121. * Retrieve the message for the given code, using the defaultHtmlEscape setting.
  122. * @param code code of the message
  123. * @param defaultMessage String to return if the lookup fails
  124. * @return the message
  125. */
  126. public String getMessage(String code, String defaultMessage) {
  127. return getMessage(code, null, defaultMessage, this.defaultHtmlEscape);
  128. }
  129. /**
  130. * Retrieve the message for the given code, using the defaultHtmlEscape setting.
  131. * @param code code of the message
  132. * @param args arguments for the message, or null if none
  133. * @param defaultMessage String to return if the lookup fails
  134. * @return the message
  135. */
  136. public String getMessage(String code, Object[] args, String defaultMessage) {
  137. return getMessage(code, args, defaultMessage, this.defaultHtmlEscape);
  138. }
  139. /**
  140. * Retrieve the message for the given code.
  141. * @param code code of the message
  142. * @param args arguments for the message, or null if none
  143. * @param defaultMessage String to return if the lookup fails
  144. * @param htmlEscape HTML escape the message?
  145. * @return the message
  146. */
  147. public String getMessage(String code, Object[] args, String defaultMessage, boolean htmlEscape) {
  148. String msg = this.webApplicationContext.getMessage(code, args, defaultMessage, this.locale);
  149. return (htmlEscape ? HtmlUtils.htmlEscape(msg) : msg);
  150. }
  151. /**
  152. * Retrieve the message for the given code, using the defaultHtmlEscape setting.
  153. * @param code code of the message
  154. * @return the message
  155. * @throws org.springframework.context.NoSuchMessageException if not found
  156. */
  157. public String getMessage(String code) throws NoSuchMessageException {
  158. return getMessage(code, null, this.defaultHtmlEscape);
  159. }
  160. /**
  161. * Retrieve the message for the given code, using the defaultHtmlEscape setting.
  162. * @param code code of the message
  163. * @param args arguments for the message, or null if none
  164. * @return the message
  165. * @throws org.springframework.context.NoSuchMessageException if not found
  166. */
  167. public String getMessage(String code, Object[] args) throws NoSuchMessageException {
  168. return getMessage(code, args, this.defaultHtmlEscape);
  169. }
  170. /**
  171. * Retrieve the message for the given code.
  172. * @param code code of the message
  173. * @param args arguments for the message, or null if none
  174. * @param htmlEscape HTML escape the message?
  175. * @return the message
  176. * @throws org.springframework.context.NoSuchMessageException if not found
  177. */
  178. public String getMessage(String code, Object[] args, boolean htmlEscape) throws NoSuchMessageException {
  179. String msg = this.webApplicationContext.getMessage(code, args, this.locale);
  180. return (htmlEscape ? HtmlUtils.htmlEscape(msg) : msg);
  181. }
  182. /**
  183. * Retrieve the given MessageSourceResolvable (e.g. an ObjectError instance),
  184. * using the defaultHtmlEscape setting.
  185. * @param resolvable the MessageSourceResolvable
  186. * @return the message
  187. * @throws org.springframework.context.NoSuchMessageException if not found
  188. */
  189. public String getMessage(MessageSourceResolvable resolvable) throws NoSuchMessageException {
  190. return getMessage(resolvable, this.defaultHtmlEscape);
  191. }
  192. /**
  193. * Retrieve the given MessageSourceResolvable (e.g. an ObjectError instance).
  194. * @param resolvable the MessageSourceResolvable
  195. * @param htmlEscape HTML escape the message?
  196. * @return the message
  197. * @throws org.springframework.context.NoSuchMessageException if not found
  198. */
  199. public String getMessage(MessageSourceResolvable resolvable, boolean htmlEscape) throws NoSuchMessageException {
  200. String msg = this.webApplicationContext.getMessage(resolvable, this.locale);
  201. return (htmlEscape ? HtmlUtils.htmlEscape(msg) : msg);
  202. }
  203. /**
  204. * Retrieve the theme message for the given code.
  205. * <p>Note that theme messages are never HTML-escaped, as they typically
  206. * denote theme-specific resource paths and not client-visible messages.
  207. * @param code code of the message
  208. * @param defaultMessage String to return if the lookup fails
  209. * @return the message
  210. */
  211. public String getThemeMessage(String code, String defaultMessage) {
  212. return this.theme.getMessageSource().getMessage(code, null, defaultMessage, this.locale);
  213. }
  214. /**
  215. * Retrieve the theme message for the given code.
  216. * <p>Note that theme messages are never HTML-escaped, as they typically
  217. * denote theme-specific resource paths and not client-visible messages.
  218. * @param code code of the message
  219. * @param args arguments for the message, or null if none
  220. * @param defaultMessage String to return if the lookup fails
  221. * @return the message
  222. */
  223. public String getThemeMessage(String code, String[] args, String defaultMessage) {
  224. return this.theme.getMessageSource().getMessage(code, args, defaultMessage, this.locale);
  225. }
  226. /**
  227. * Retrieve the theme message for the given code.
  228. * <p>Note that theme messages are never HTML-escaped, as they typically
  229. * denote theme-specific resource paths and not client-visible messages.
  230. * @param code code of the message
  231. * @return the message
  232. * @throws org.springframework.context.NoSuchMessageException if not found
  233. */
  234. public String getThemeMessage(String code) throws NoSuchMessageException {
  235. return this.theme.getMessageSource().getMessage(code, null, this.locale);
  236. }
  237. /**
  238. * Retrieve the theme message for the given code.
  239. * <p>Note that theme messages are never HTML-escaped, as they typically
  240. * denote theme-specific resource paths and not client-visible messages.
  241. * @param code code of the message
  242. * @param args arguments for the message, or null if none
  243. * @return the message
  244. * @throws org.springframework.context.NoSuchMessageException if not found
  245. */
  246. public String getThemeMessage(String code, String[] args) throws NoSuchMessageException {
  247. return this.theme.getMessageSource().getMessage(code, args, this.locale);
  248. }
  249. /**
  250. * Retrieve the given MessageSourceResolvable in the current theme.
  251. * <p>Note that theme messages are never HTML-escaped, as they typically
  252. * denote theme-specific resource paths and not client-visible messages.
  253. * @param resolvable the MessageSourceResolvable
  254. * @return the message
  255. * @throws org.springframework.context.NoSuchMessageException if not found
  256. */
  257. public String getThemeMessage(MessageSourceResolvable resolvable) throws NoSuchMessageException {
  258. return this.theme.getMessageSource().getMessage(resolvable, this.locale);
  259. }
  260. /**
  261. * Retrieve the Errors instance for the given bind object,
  262. * using the defaultHtmlEscape setting.
  263. * @param name name of the bind object
  264. * @return the Errors instance, or null if not found
  265. */
  266. public Errors getErrors(String name) {
  267. return getErrors(name, this.defaultHtmlEscape);
  268. }
  269. /**
  270. * Retrieve the Errors instance for the given bind object.
  271. * @param name name of the bind object
  272. * @param htmlEscape create an Errors instance with automatic HTML escaping?
  273. * @return the Errors instance, or null if not found
  274. */
  275. public Errors getErrors(String name, boolean htmlEscape) {
  276. if (this.errorsMap == null) {
  277. this.errorsMap = new HashMap();
  278. }
  279. Errors errors = (Errors) this.errorsMap.get(name);
  280. boolean put = false;
  281. if (errors == null) {
  282. errors = retrieveErrors(name);
  283. if (errors == null) {
  284. return null;
  285. }
  286. put = true;
  287. }
  288. if (htmlEscape && !(errors instanceof EscapedErrors)) {
  289. errors = new EscapedErrors(errors);
  290. put = true;
  291. }
  292. else if (!htmlEscape && errors instanceof EscapedErrors) {
  293. errors = ((EscapedErrors) errors).getSource();
  294. put = true;
  295. }
  296. if (put) {
  297. this.errorsMap.put(name, errors);
  298. }
  299. return errors;
  300. }
  301. /**
  302. * Retrieve the Errors instance for the given bind object,
  303. * either from the model or from the request attributes.
  304. */
  305. private Errors retrieveErrors(String name) {
  306. String key = BindException.ERROR_KEY_PREFIX + name;
  307. if (this.model != null) {
  308. return (Errors) this.model.get(key);
  309. }
  310. else {
  311. return (Errors) this.request.getAttribute(key);
  312. }
  313. }
  314. }