1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.struts.taglib;
19
20 import org.apache.commons.beanutils.PropertyUtils;
21 import org.apache.commons.logging.Log;
22 import org.apache.commons.logging.LogFactory;
23 import org.apache.struts.Globals;
24 import org.apache.struts.action.ActionErrors;
25 import org.apache.struts.action.ActionMessage;
26 import org.apache.struts.action.ActionMessages;
27 import org.apache.struts.config.ForwardConfig;
28 import org.apache.struts.config.ModuleConfig;
29 import org.apache.struts.taglib.html.Constants;
30 import org.apache.struts.util.MessageResources;
31 import org.apache.struts.util.ModuleUtils;
32 import org.apache.struts.util.RequestUtils;
33 import org.apache.struts.util.ResponseUtils;
34
35 import javax.servlet.http.HttpServletRequest;
36 import javax.servlet.http.HttpServletResponse;
37 import javax.servlet.http.HttpSession;
38 import javax.servlet.jsp.JspException;
39 import javax.servlet.jsp.JspWriter;
40 import javax.servlet.jsp.PageContext;
41 import javax.servlet.jsp.tagext.BodyContent;
42
43 import java.io.IOException;
44
45 import java.lang.reflect.InvocationTargetException;
46
47 import java.net.MalformedURLException;
48
49 import java.util.HashMap;
50 import java.util.Iterator;
51 import java.util.Locale;
52 import java.util.Map;
53
54 /***
55 * Provides helper methods for JSP tags.
56 *
57 * @version $Rev: 421129 $
58 * @since Struts 1.2
59 */
60 public class TagUtils {
61 /***
62 * The Singleton instance.
63 * @since 1.3.5 Changed to non-final so it may be overridden, use at your own risk (you've been warned!!)
64 */
65 private static TagUtils instance = new TagUtils();
66
67 /***
68 * Commons logging instance.
69 */
70 private static final Log log = LogFactory.getLog(TagUtils.class);
71
72 /***
73 * The message resources for this package. TODO We need to move the
74 * relevant messages out of this properties file.
75 */
76 private static final MessageResources messages =
77 MessageResources.getMessageResources(
78 "org.apache.struts.taglib.LocalStrings");
79
80 /***
81 * Maps lowercase JSP scope names to their PageContext integer constant
82 * values.
83 */
84 private static final Map scopes = new HashMap();
85
86 /***
87 * Initialize the scope names map and the encode variable with the
88 * Java 1.4 method if available.
89 */
90 static {
91 scopes.put("page", new Integer(PageContext.PAGE_SCOPE));
92 scopes.put("request", new Integer(PageContext.REQUEST_SCOPE));
93 scopes.put("session", new Integer(PageContext.SESSION_SCOPE));
94 scopes.put("application", new Integer(PageContext.APPLICATION_SCOPE));
95 }
96
97 /***
98 * Constructor for TagUtils.
99 */
100 protected TagUtils() {
101 super();
102 }
103
104 /***
105 * Returns the Singleton instance of TagUtils.
106 */
107 public static TagUtils getInstance() {
108 return instance;
109 }
110
111 /***
112 * Set the instance.
113 * This blatently violates the Singleton pattern, but then some say Singletons are an anti-pattern.
114 * @since 1.3.5 Changed to non-final and added setInstance() so TagUtils may be overridden, use at your own risk (you've been warned!!)
115 * @param instance The instance to set.
116 */
117 public static void setInstance(TagUtils instance){
118 TagUtils.instance = instance;
119 }
120
121 /***
122 * Compute a set of query parameters that will be dynamically added to a
123 * generated URL. The returned Map is keyed by parameter name, and the
124 * values are either null (no value specified), a String (single value
125 * specified), or a String[] array (multiple values specified). Parameter
126 * names correspond to the corresponding attributes of the
127 * <code><html:link></code> tag. If no query parameters are
128 * identified, return <code>null</code>.
129 *
130 * @param pageContext PageContext we are operating in
131 * @param paramId Single-value request parameter name (if any)
132 * @param paramName Bean containing single-value parameter value
133 * @param paramProperty Property (of bean named by <code>paramName</code>
134 * containing single-value parameter value
135 * @param paramScope Scope containing bean named by <code>paramName</code>
136 * @param name Bean containing multi-value parameters Map (if
137 * any)
138 * @param property Property (of bean named by <code>name</code>
139 * containing multi-value parameters Map
140 * @param scope Scope containing bean named by <code>name</code>
141 * @param transaction Should we add our transaction control token?
142 * @return Map of query parameters
143 * @throws JspException if we cannot look up the required beans
144 * @throws JspException if a class cast exception occurs on a looked-up
145 * bean or property
146 */
147 public Map computeParameters(PageContext pageContext, String paramId,
148 String paramName, String paramProperty, String paramScope, String name,
149 String property, String scope, boolean transaction)
150 throws JspException {
151
152 if ((paramId == null) && (name == null) && !transaction) {
153 return (null);
154 }
155
156
157 Map map = null;
158
159 try {
160 if (name != null) {
161 map = (Map) getInstance().lookup(pageContext, name, property,
162 scope);
163 }
164
165
166
167
168
169
170 } catch (JspException e) {
171 saveException(pageContext, e);
172 throw e;
173 }
174
175
176 Map results = null;
177
178 if (map != null) {
179 results = new HashMap(map);
180 } else {
181 results = new HashMap();
182 }
183
184
185 if ((paramId != null) && (paramName != null)) {
186 Object paramValue = null;
187
188 try {
189 paramValue =
190 TagUtils.getInstance().lookup(pageContext, paramName,
191 paramProperty, paramScope);
192 } catch (JspException e) {
193 saveException(pageContext, e);
194 throw e;
195 }
196
197 if (paramValue != null) {
198 String paramString = null;
199
200 if (paramValue instanceof String) {
201 paramString = (String) paramValue;
202 } else {
203 paramString = paramValue.toString();
204 }
205
206 Object mapValue = results.get(paramId);
207
208 if (mapValue == null) {
209 results.put(paramId, paramString);
210 } else if (mapValue instanceof String[]) {
211 String[] oldValues = (String[]) mapValue;
212 String[] newValues = new String[oldValues.length + 1];
213
214 System.arraycopy(oldValues, 0, newValues, 0,
215 oldValues.length);
216 newValues[oldValues.length] = paramString;
217 results.put(paramId, newValues);
218 } else {
219 String[] newValues = new String[2];
220
221 newValues[0] = mapValue.toString();
222 newValues[1] = paramString;
223 results.put(paramId, newValues);
224 }
225 }
226 }
227
228
229 if (transaction) {
230 HttpSession session = pageContext.getSession();
231 String token = null;
232
233 if (session != null) {
234 token =
235 (String) session.getAttribute(Globals.TRANSACTION_TOKEN_KEY);
236 }
237
238 if (token != null) {
239 results.put(Constants.TOKEN_KEY, token);
240 }
241 }
242
243
244 return (results);
245 }
246
247 public String computeURL(PageContext pageContext, String forward,
248 String href, String page, String action, String module, Map params,
249 String anchor, boolean redirect)
250 throws MalformedURLException {
251 return this.computeURLWithCharEncoding(pageContext, forward, href,
252 page, action, module, params, anchor, redirect, false);
253 }
254
255 /***
256 * Compute a hyperlink URL based on the <code>forward</code>,
257 * <code>href</code>, <code>action</code> or <code>page</code> parameter
258 * that is not null. The returned URL will have already been passed to
259 * <code>response.encodeURL()</code> for adding a session identifier.
260 *
261 * @param pageContext PageContext for the tag making this call
262 * @param forward Logical forward name for which to look up the
263 * context-relative URI (if specified)
264 * @param href URL to be utilized unmodified (if specified)
265 * @param page Module-relative page for which a URL should be
266 * created (if specified)
267 * @param action Logical action name for which to look up the
268 * context-relative URI (if specified)
269 * @param params Map of parameters to be dynamically included (if
270 * any)
271 * @param anchor Anchor to be dynamically included (if any)
272 * @param redirect Is this URL for a <code>response.sendRedirect()</code>?
273 * @return URL with session identifier
274 * @throws java.net.MalformedURLException if a URL cannot be created for
275 * the specified parameters
276 */
277 public String computeURLWithCharEncoding(PageContext pageContext,
278 String forward, String href, String page, String action, String module,
279 Map params, String anchor, boolean redirect, boolean useLocalEncoding)
280 throws MalformedURLException {
281 return computeURLWithCharEncoding(pageContext, forward, href, page,
282 action, module, params, anchor, redirect, true, useLocalEncoding);
283 }
284
285 public String computeURL(PageContext pageContext, String forward,
286 String href, String page, String action, String module, Map params,
287 String anchor, boolean redirect, boolean encodeSeparator)
288 throws MalformedURLException {
289 return computeURLWithCharEncoding(pageContext, forward, href, page,
290 action, module, params, anchor, redirect, encodeSeparator, false);
291 }
292
293 /***
294 * Compute a hyperlink URL based on the <code>forward</code>,
295 * <code>href</code>, <code>action</code> or <code>page</code> parameter
296 * that is not null. The returned URL will have already been passed to
297 * <code>response.encodeURL()</code> for adding a session identifier.
298 *
299 * @param pageContext PageContext for the tag making this call
300 * @param forward Logical forward name for which to look up the
301 * context-relative URI (if specified)
302 * @param href URL to be utilized unmodified (if specified)
303 * @param page Module-relative page for which a URL should be
304 * created (if specified)
305 * @param action Logical action name for which to look up the
306 * context-relative URI (if specified)
307 * @param params Map of parameters to be dynamically included
308 * (if any)
309 * @param anchor Anchor to be dynamically included (if any)
310 * @param redirect Is this URL for a <code>response.sendRedirect()</code>?
311 * @param encodeSeparator This is only checked if redirect is set to
312 * false (never encoded for a redirect). If true,
313 * query string parameter separators are encoded
314 * as >amp;, else & is used.
315 * @param useLocalEncoding If set to true, urlencoding is done on the
316 * bytes of character encoding from
317 * ServletResponse#getCharacterEncoding. Use UTF-8
318 * otherwise.
319 * @return URL with session identifier
320 * @throws java.net.MalformedURLException if a URL cannot be created for
321 * the specified parameters
322 */
323 public String computeURLWithCharEncoding(PageContext pageContext,
324 String forward, String href, String page, String action, String module,
325 Map params, String anchor, boolean redirect, boolean encodeSeparator,
326 boolean useLocalEncoding)
327 throws MalformedURLException {
328 String charEncoding = "UTF-8";
329
330 if (useLocalEncoding) {
331 charEncoding = pageContext.getResponse().getCharacterEncoding();
332 }
333
334
335
336 int n = 0;
337
338 if (forward != null) {
339 n++;
340 }
341
342 if (href != null) {
343 n++;
344 }
345
346 if (page != null) {
347 n++;
348 }
349
350 if (action != null) {
351 n++;
352 }
353
354 if (n != 1) {
355 throw new MalformedURLException(messages.getMessage(
356 "computeURL.specifier"));
357 }
358
359
360 ModuleConfig moduleConfig = getModuleConfig(module, pageContext);
361
362
363 StringBuffer url = new StringBuffer();
364 HttpServletRequest request =
365 (HttpServletRequest) pageContext.getRequest();
366
367 if (forward != null) {
368 ForwardConfig forwardConfig =
369 moduleConfig.findForwardConfig(forward);
370
371 if (forwardConfig == null) {
372 throw new MalformedURLException(messages.getMessage(
373 "computeURL.forward", forward));
374 }
375
376
377
378
379
380
381 if (forwardConfig.getPath().startsWith("/")) {
382 url.append(request.getContextPath());
383 url.append(RequestUtils.forwardURL(request, forwardConfig,
384 moduleConfig));
385 } else {
386 url.append(forwardConfig.getPath());
387 }
388 } else if (href != null) {
389 url.append(href);
390 } else if (action != null) {
391 url.append(instance.getActionMappingURL(action, module,
392 pageContext, false));
393 } else
394 {
395 url.append(request.getContextPath());
396 url.append(this.pageURL(request, page, moduleConfig));
397 }
398
399
400 if (anchor != null) {
401 String temp = url.toString();
402 int hash = temp.indexOf('#');
403
404 if (hash >= 0) {
405 url.setLength(hash);
406 }
407
408 url.append('#');
409 url.append(this.encodeURL(anchor, charEncoding));
410 }
411
412
413 if ((params != null) && (params.size() > 0)) {
414
415 String temp = url.toString();
416 int hash = temp.indexOf('#');
417
418 if (hash >= 0) {
419 anchor = temp.substring(hash + 1);
420 url.setLength(hash);
421 temp = url.toString();
422 } else {
423 anchor = null;
424 }
425
426
427 String separator = null;
428
429 if (redirect) {
430 separator = "&";
431 } else if (encodeSeparator) {
432 separator = "&";
433 } else {
434 separator = "&";
435 }
436
437
438 boolean question = temp.indexOf('?') >= 0;
439 Iterator keys = params.keySet().iterator();
440
441 while (keys.hasNext()) {
442 String key = (String) keys.next();
443 Object value = params.get(key);
444
445 if (value == null) {
446 if (!question) {
447 url.append('?');
448 question = true;
449 } else {
450 url.append(separator);
451 }
452
453 url.append(this.encodeURL(key, charEncoding));
454 url.append('=');
455 } else if (value instanceof String) {
456 if (!question) {
457 url.append('?');
458 question = true;
459 } else {
460 url.append(separator);
461 }
462
463 url.append(this.encodeURL(key, charEncoding));
464 url.append('=');
465 url.append(this.encodeURL((String) value, charEncoding));
466 } else if (value instanceof String[]) {
467 String[] values = (String[]) value;
468
469 for (int i = 0; i < values.length; i++) {
470 if (!question) {
471 url.append('?');
472 question = true;
473 } else {
474 url.append(separator);
475 }
476
477 url.append(this.encodeURL(key, charEncoding));
478 url.append('=');
479 url.append(this.encodeURL(values[i], charEncoding));
480 }
481 } else
482 {
483 if (!question) {
484 url.append('?');
485 question = true;
486 } else {
487 url.append(separator);
488 }
489
490 url.append(this.encodeURL(key, charEncoding));
491 url.append('=');
492 url.append(this.encodeURL(value.toString(), charEncoding));
493 }
494 }
495
496
497 if (anchor != null) {
498 url.append('#');
499 url.append(this.encodeURL(anchor, charEncoding));
500 }
501 }
502
503
504
505 if ((href == null) && (pageContext.getSession() != null)) {
506 HttpServletResponse response =
507 (HttpServletResponse) pageContext.getResponse();
508
509 if (redirect) {
510 return (response.encodeRedirectURL(url.toString()));
511 }
512
513 return (response.encodeURL(url.toString()));
514 }
515
516 return (url.toString());
517 }
518
519 /***
520 * URLencodes a string assuming the character encoding is UTF-8.
521 *
522 * @param url
523 * @return String The encoded url in UTF-8
524 */
525 public String encodeURL(String url) {
526 return encodeURL(url, "UTF-8");
527 }
528
529 /***
530 * Use the new URLEncoder.encode() method from Java 1.4 if available, else
531 * use the old deprecated version. This method uses reflection to find
532 * the appropriate method; if the reflection operations throw exceptions,
533 * this will return the url encoded with the old URLEncoder.encode()
534 * method.
535 *
536 * @param enc The character encoding the urlencode is performed on.
537 * @return String The encoded url.
538 */
539 public String encodeURL(String url, String enc) {
540 return ResponseUtils.encodeURL(url, enc);
541 }
542
543 /***
544 * Filter the specified string for characters that are senstive to HTML
545 * interpreters, returning the string with these characters replaced by
546 * the corresponding character entities.
547 *
548 * @param value The string to be filtered and returned
549 */
550 public String filter(String value) {
551 return ResponseUtils.filter(value);
552 }
553
554 /***
555 * Return the form action converted into an action mapping path. The
556 * value of the <code>action</code> property is manipulated as follows in
557 * computing the name of the requested mapping:
558 *
559 * <ul>
560 *
561 * <li>Any filename extension is removed (on the theory that extension
562 * mapping is being used to select the controller servlet).</li>
563 *
564 * <li>If the resulting value does not start with a slash, then a slash is
565 * prepended.</li>
566 *
567 * </ul>
568 */
569 public String getActionMappingName(String action) {
570 String value = action;
571 int question = action.indexOf("?");
572
573 if (question >= 0) {
574 value = value.substring(0, question);
575 }
576
577 int pound = value.indexOf("#");
578
579 if (pound >= 0) {
580 value = value.substring(0, pound);
581 }
582
583 int slash = value.lastIndexOf("/");
584 int period = value.lastIndexOf(".");
585
586 if ((period >= 0) && (period > slash)) {
587 value = value.substring(0, period);
588 }
589
590 return value.startsWith("/") ? value : ("/" + value);
591 }
592
593 /***
594 * Return the form action converted into a server-relative URL.
595 */
596 public String getActionMappingURL(String action, PageContext pageContext) {
597 return getActionMappingURL(action, null, pageContext, false);
598 }
599
600 /***
601 * Return the form action converted into a server-relative URL.
602 */
603 public String getActionMappingURL(String action, String module,
604 PageContext pageContext, boolean contextRelative) {
605 HttpServletRequest request =
606 (HttpServletRequest) pageContext.getRequest();
607
608 String contextPath = request.getContextPath();
609 StringBuffer value = new StringBuffer();
610
611
612
613
614 if (contextPath.length() > 1) {
615 value.append(contextPath);
616 }
617
618 ModuleConfig moduleConfig = getModuleConfig(module, pageContext);
619
620 if ((moduleConfig != null) && (!contextRelative)) {
621 value.append(moduleConfig.getPrefix());
622 }
623
624
625 String servletMapping =
626 (String) pageContext.getAttribute(Globals.SERVLET_KEY,
627 PageContext.APPLICATION_SCOPE);
628
629 if (servletMapping != null) {
630 String queryString = null;
631 int question = action.indexOf("?");
632
633 if (question >= 0) {
634 queryString = action.substring(question);
635 }
636
637 String actionMapping = getActionMappingName(action);
638
639 if (servletMapping.startsWith("*.")) {
640 value.append(actionMapping);
641 value.append(servletMapping.substring(1));
642 } else if (servletMapping.endsWith("/*")) {
643 value.append(servletMapping.substring(0,
644 servletMapping.length() - 2));
645 value.append(actionMapping);
646 } else if (servletMapping.equals("/")) {
647 value.append(actionMapping);
648 }
649
650 if (queryString != null) {
651 value.append(queryString);
652 }
653 }
654
655
656 else {
657 if (!action.startsWith("/")) {
658 value.append("/");
659 }
660
661 value.append(action);
662 }
663
664 return value.toString();
665 }
666
667 /***
668 * Retrieves the value from request scope and if it isn't already an
669 * <code>ActionMessages</code>, some classes are converted to one.
670 *
671 * @param pageContext The PageContext for the current page
672 * @param paramName Key for parameter value
673 * @return ActionErrors in page context.
674 * @throws JspException
675 */
676 public ActionMessages getActionMessages(PageContext pageContext,
677 String paramName) throws JspException {
678 ActionMessages am = new ActionMessages();
679
680 Object value = pageContext.findAttribute(paramName);
681
682 if (value != null) {
683 try {
684 if (value instanceof String) {
685 am.add(ActionMessages.GLOBAL_MESSAGE,
686 new ActionMessage((String) value));
687 } else if (value instanceof String[]) {
688 String[] keys = (String[]) value;
689
690 for (int i = 0; i < keys.length; i++) {
691 am.add(ActionMessages.GLOBAL_MESSAGE,
692 new ActionMessage(keys[i]));
693 }
694 } else if (value instanceof ActionErrors) {
695 ActionMessages m = (ActionMessages) value;
696
697 am.add(m);
698 } else if (value instanceof ActionMessages) {
699 am = (ActionMessages) value;
700 } else {
701 throw new JspException(messages.getMessage(
702 "actionMessages.errors", value.getClass().getName()));
703 }
704 } catch (JspException e) {
705 throw e;
706 } catch (Exception e) {
707 log.warn("Unable to retieve ActionMessage for paramName : "
708 + paramName, e);
709 }
710 }
711
712 return am;
713 }
714
715 /***
716 * Return the default ModuleConfig object if it exists, null if
717 * otherwise.
718 *
719 * @param pageContext The page context.
720 * @return the ModuleConfig object
721 */
722 public ModuleConfig getModuleConfig(PageContext pageContext) {
723 return getModuleConfig(null, pageContext);
724 }
725
726 /***
727 * Return the specified ModuleConfig object for the given prefix if it
728 * exists, otherwise a NullPointerException will be thrown.
729 *
730 * @param module The module prefix
731 * @param pageContext The page context.
732 * @return the ModuleConfig object
733 * @throws NullPointerException Thrown when module cannot be found
734 */
735 public ModuleConfig getModuleConfig(String module, PageContext pageContext) {
736 ModuleConfig config =
737 ModuleUtils.getInstance().getModuleConfig(module,
738 (HttpServletRequest) pageContext.getRequest(),
739 pageContext.getServletContext());
740
741
742 if (config == null) {
743 throw new NullPointerException("Module '" + module + "' not found.");
744 }
745
746 return config;
747 }
748
749 /***
750 * Converts the scope name into its corresponding PageContext constant
751 * value.
752 *
753 * @param scopeName Can be "page", "request", "session", or "application"
754 * in any case.
755 * @return The constant representing the scope (ie. PageContext.REQUEST_SCOPE).
756 * @throws JspException if the scopeName is not a valid name.
757 */
758 public int getScope(String scopeName)
759 throws JspException {
760 Integer scope = (Integer) scopes.get(scopeName.toLowerCase());
761
762 if (scope == null) {
763 throw new JspException(messages.getMessage("lookup.scope", scope));
764 }
765
766 return scope.intValue();
767 }
768
769 /***
770 * Look up and return current user locale, based on the specified
771 * parameters.
772 *
773 * @param pageContext The PageContext associated with this request
774 * @param locale Name of the session attribute for our user's Locale.
775 * If this is <code>null</code>, the default locale key
776 * is used for the lookup.
777 * @return current user locale
778 */
779 public Locale getUserLocale(PageContext pageContext, String locale) {
780 return RequestUtils.getUserLocale((HttpServletRequest) pageContext
781 .getRequest(), locale);
782 }
783
784 /***
785 * Returns true if the custom tags are in XHTML mode.
786 */
787 public boolean isXhtml(PageContext pageContext) {
788 String xhtml =
789 (String) pageContext.getAttribute(Globals.XHTML_KEY,
790 PageContext.PAGE_SCOPE);
791
792 return "true".equalsIgnoreCase(xhtml);
793 }
794
795 /***
796 * Locate and return the specified bean, from an optionally specified
797 * scope, in the specified page context. If no such bean is found, return
798 * <code>null</code> instead. If an exception is thrown, it will have
799 * already been saved via a call to <code>saveException()</code>.
800 *
801 * @param pageContext Page context to be searched
802 * @param name Name of the bean to be retrieved
803 * @param scopeName Scope to be searched (page, request, session,
804 * application) or <code>null</code> to use
805 * <code>findAttribute()</code> instead
806 * @return JavaBean in the specified page context
807 * @throws JspException if an invalid scope name is requested
808 */
809 public Object lookup(PageContext pageContext, String name, String scopeName)
810 throws JspException {
811 if (scopeName == null) {
812 return pageContext.findAttribute(name);
813 }
814
815 try {
816 return pageContext.getAttribute(name, instance.getScope(scopeName));
817 } catch (JspException e) {
818 saveException(pageContext, e);
819 throw e;
820 }
821 }
822
823 /***
824 * Locate and return the specified property of the specified bean, from an
825 * optionally specified scope, in the specified page context. If an
826 * exception is thrown, it will have already been saved via a call to
827 * <code>saveException()</code>.
828 *
829 * @param pageContext Page context to be searched
830 * @param name Name of the bean to be retrieved
831 * @param property Name of the property to be retrieved, or
832 * <code>null</code> to retrieve the bean itself
833 * @param scope Scope to be searched (page, request, session,
834 * application) or <code>null</code> to use
835 * <code>findAttribute()</code> instead
836 * @return property of specified JavaBean
837 * @throws JspException if an invalid scope name is requested
838 * @throws JspException if the specified bean is not found
839 * @throws JspException if accessing this property causes an
840 * IllegalAccessException, IllegalArgumentException,
841 * InvocationTargetException, or NoSuchMethodException
842 */
843 public Object lookup(PageContext pageContext, String name, String property,
844 String scope) throws JspException {
845
846 Object bean = lookup(pageContext, name, scope);
847
848 if (bean == null) {
849 JspException e = null;
850
851 if (scope == null) {
852 e = new JspException(messages.getMessage("lookup.bean.any", name));
853 } else {
854 e = new JspException(messages.getMessage("lookup.bean", name,
855 scope));
856 }
857
858 saveException(pageContext, e);
859 throw e;
860 }
861
862 if (property == null) {
863 return bean;
864 }
865
866
867 try {
868 return PropertyUtils.getProperty(bean, property);
869 } catch (IllegalAccessException e) {
870 saveException(pageContext, e);
871 throw new JspException(messages.getMessage("lookup.access",
872 property, name));
873 } catch (IllegalArgumentException e) {
874 saveException(pageContext, e);
875 throw new JspException(messages.getMessage("lookup.argument",
876 property, name));
877 } catch (InvocationTargetException e) {
878 Throwable t = e.getTargetException();
879
880 if (t == null) {
881 t = e;
882 }
883
884 saveException(pageContext, t);
885 throw new JspException(messages.getMessage("lookup.target",
886 property, name));
887 } catch (NoSuchMethodException e) {
888 saveException(pageContext, e);
889
890 String beanName = name;
891
892
893
894
895 if (Constants.BEAN_KEY.equals(name)) {
896 Object obj = pageContext.findAttribute(Constants.BEAN_KEY);
897
898 if (obj != null) {
899 beanName = obj.getClass().getName();
900 }
901 }
902
903 throw new JspException(messages.getMessage("lookup.method",
904 property, beanName));
905 }
906 }
907
908 /***
909 * Look up and return a message string, based on the specified
910 * parameters.
911 *
912 * @param pageContext The PageContext associated with this request
913 * @param bundle Name of the servlet context attribute for our
914 * message resources bundle
915 * @param locale Name of the session attribute for our user's Locale
916 * @param key Message key to be looked up and returned
917 * @return message string
918 * @throws JspException if a lookup error occurs (will have been saved in
919 * the request already)
920 */
921 public String message(PageContext pageContext, String bundle,
922 String locale, String key)
923 throws JspException {
924 return message(pageContext, bundle, locale, key, null);
925 }
926
927 /***
928 * Look up and return a message string, based on the specified
929 * parameters.
930 *
931 * @param pageContext The PageContext associated with this request
932 * @param bundle Name of the servlet context attribute for our
933 * message resources bundle
934 * @param locale Name of the session attribute for our user's Locale
935 * @param key Message key to be looked up and returned
936 * @param args Replacement parameters for this message
937 * @return message string
938 * @throws JspException if a lookup error occurs (will have been saved in
939 * the request already)
940 */
941 public String message(PageContext pageContext, String bundle,
942 String locale, String key, Object[] args)
943 throws JspException {
944 MessageResources resources =
945 retrieveMessageResources(pageContext, bundle, false);
946
947 Locale userLocale = getUserLocale(pageContext, locale);
948 String message = null;
949
950 if (args == null) {
951 message = resources.getMessage(userLocale, key);
952 } else {
953 message = resources.getMessage(userLocale, key, args);
954 }
955
956 if ((message == null) && log.isDebugEnabled()) {
957
958 log.debug(resources.getMessage("message.resources", key, bundle,
959 locale));
960 }
961
962 return message;
963 }
964
965 /***
966 * <p>Return the context-relative URL that corresponds to the specified
967 * <code>page</code> attribute value, calculated based on the
968 * <code>pagePattern</code> property of the current module's {@link
969 * ModuleConfig}.</p>
970 *
971 * @param request The servlet request we are processing
972 * @param page The module-relative URL to be substituted in to the
973 * <code>pagePattern</code> pattern for the current module
974 * (<strong>MUST</strong> start with a slash)
975 * @return context-relative URL
976 */
977 public String pageURL(HttpServletRequest request, String page,
978 ModuleConfig moduleConfig) {
979 StringBuffer sb = new StringBuffer();
980 String pagePattern =
981 moduleConfig.getControllerConfig().getPagePattern();
982
983 if (pagePattern == null) {
984 sb.append(moduleConfig.getPrefix());
985 sb.append(page);
986 } else {
987 boolean dollar = false;
988
989 for (int i = 0; i < pagePattern.length(); i++) {
990 char ch = pagePattern.charAt(i);
991
992 if (dollar) {
993 switch (ch) {
994 case 'M':
995 sb.append(moduleConfig.getPrefix());
996
997 break;
998
999 case 'P':
1000 sb.append(page);
1001
1002 break;
1003
1004 case '$':
1005 sb.append('$');
1006
1007 break;
1008
1009 default:
1010 ;
1011 }
1012
1013 dollar = false;
1014
1015 continue;
1016 } else if (ch == '$') {
1017 dollar = true;
1018 } else {
1019 sb.append(ch);
1020 }
1021 }
1022 }
1023
1024 return sb.toString();
1025 }
1026
1027 /***
1028 * Return true if a message string for the specified message key is
1029 * present for the specified <code>Locale</code> and bundle.
1030 *
1031 * @param pageContext The PageContext associated with this request
1032 * @param bundle Name of the servlet context attribute for our
1033 * message resources bundle
1034 * @param locale Name of the session attribute for our user's Locale
1035 * @param key Message key to be looked up and returned
1036 * @return true if a message string for message key exists
1037 * @throws JspException if a lookup error occurs (will have been saved in
1038 * the request already)
1039 */
1040 public boolean present(PageContext pageContext, String bundle,
1041 String locale, String key)
1042 throws JspException {
1043 MessageResources resources =
1044 retrieveMessageResources(pageContext, bundle, true);
1045
1046 Locale userLocale = getUserLocale(pageContext, locale);
1047
1048 return resources.isPresent(userLocale, key);
1049 }
1050
1051 /***
1052 * Returns the appropriate MessageResources object for the current module
1053 * and the given bundle.
1054 *
1055 * @param pageContext Search the context's scopes for the resources.
1056 * @param bundle The bundle name to look for. If this is
1057 * <code>null</code>, the default bundle name is
1058 * used.
1059 * @param checkPageScope Whether to check page scope
1060 * @return MessageResources The bundle's resources stored in some scope.
1061 * @throws JspException if the MessageResources object could not be
1062 * found.
1063 */
1064 public MessageResources retrieveMessageResources(PageContext pageContext,
1065 String bundle, boolean checkPageScope)
1066 throws JspException {
1067 MessageResources resources = null;
1068
1069 if (bundle == null) {
1070 bundle = Globals.MESSAGES_KEY;
1071 }
1072
1073 if (checkPageScope) {
1074 resources =
1075 (MessageResources) pageContext.getAttribute(bundle,
1076 PageContext.PAGE_SCOPE);
1077 }
1078
1079 if (resources == null) {
1080 resources =
1081 (MessageResources) pageContext.getAttribute(bundle,
1082 PageContext.REQUEST_SCOPE);
1083 }
1084
1085 if (resources == null) {
1086 ModuleConfig moduleConfig = getModuleConfig(pageContext);
1087
1088 resources =
1089 (MessageResources) pageContext.getAttribute(bundle
1090 + moduleConfig.getPrefix(), PageContext.APPLICATION_SCOPE);
1091 }
1092
1093 if (resources == null) {
1094 resources =
1095 (MessageResources) pageContext.getAttribute(bundle,
1096 PageContext.APPLICATION_SCOPE);
1097 }
1098
1099 if (resources == null) {
1100 JspException e =
1101 new JspException(messages.getMessage("message.bundle", bundle));
1102
1103 saveException(pageContext, e);
1104 throw e;
1105 }
1106
1107 return resources;
1108 }
1109
1110 /***
1111 * Save the specified exception as a request attribute for later use.
1112 *
1113 * @param pageContext The PageContext for the current page
1114 * @param exception The exception to be saved
1115 */
1116 public void saveException(PageContext pageContext, Throwable exception) {
1117 pageContext.setAttribute(Globals.EXCEPTION_KEY, exception,
1118 PageContext.REQUEST_SCOPE);
1119 }
1120
1121 /***
1122 * Write the specified text as the response to the writer associated with
1123 * this page. <strong>WARNING</strong> - If you are writing body content
1124 * from the <code>doAfterBody()</code> method of a custom tag class that
1125 * implements <code>BodyTag</code>, you should be calling
1126 * <code>writePrevious()</code> instead.
1127 *
1128 * @param pageContext The PageContext object for this page
1129 * @param text The text to be written
1130 * @throws JspException if an input/output error occurs (already saved)
1131 */
1132 public void write(PageContext pageContext, String text)
1133 throws JspException {
1134 JspWriter writer = pageContext.getOut();
1135
1136 try {
1137 writer.print(text);
1138 } catch (IOException e) {
1139 saveException(pageContext, e);
1140 throw new JspException(messages.getMessage("write.io", e.toString()));
1141 }
1142 }
1143
1144 /***
1145 * Write the specified text as the response to the writer associated with
1146 * the body content for the tag within which we are currently nested.
1147 *
1148 * @param pageContext The PageContext object for this page
1149 * @param text The text to be written
1150 * @throws JspException if an input/output error occurs (already saved)
1151 */
1152 public void writePrevious(PageContext pageContext, String text)
1153 throws JspException {
1154 JspWriter writer = pageContext.getOut();
1155
1156 if (writer instanceof BodyContent) {
1157 writer = ((BodyContent) writer).getEnclosingWriter();
1158 }
1159
1160 try {
1161 writer.print(text);
1162 } catch (IOException e) {
1163 saveException(pageContext, e);
1164 throw new JspException(messages.getMessage("write.io", e.toString()));
1165 }
1166 }
1167 }