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