Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/apache/struts/taglib/TagUtils.java


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