View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    * 
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   * 
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.struts2.jasper.runtime;
19  
20  import org.apache.struts2.jasper.JasperException;
21  import org.apache.struts2.jasper.compiler.Localizer;
22  
23  import javax.servlet.RequestDispatcher;
24  import javax.servlet.ServletException;
25  import javax.servlet.ServletRequest;
26  import javax.servlet.ServletResponse;
27  import javax.servlet.http.HttpServletRequest;
28  import javax.servlet.jsp.JspWriter;
29  import javax.servlet.jsp.PageContext;
30  import javax.servlet.jsp.tagext.BodyContent;
31  import java.beans.PropertyEditor;
32  import java.beans.PropertyEditorManager;
33  import java.io.ByteArrayOutputStream;
34  import java.io.IOException;
35  import java.io.OutputStreamWriter;
36  import java.lang.reflect.Method;
37  import java.security.AccessController;
38  import java.security.PrivilegedActionException;
39  import java.security.PrivilegedExceptionAction;
40  import java.util.Enumeration;
41  
42  /***
43   * Bunch of util methods that are used by code generated for useBean,
44   * getProperty and setProperty.
45   * <p/>
46   * The __begin, __end stuff is there so that the JSP engine can
47   * actually parse this file and inline them if people don't want
48   * runtime dependencies on this class. However, I'm not sure if that
49   * works so well right now. It got forgotten at some point. -akv
50   *
51   * @author Mandar Raje
52   * @author Shawn Bayern
53   */
54  public class JspRuntimeLibrary {
55  
56      private static final String SERVLET_EXCEPTION
57              = "javax.servlet.error.exception";
58      private static final String JSP_EXCEPTION
59              = "javax.servlet.jsp.jspException";
60  
61      protected static class PrivilegedIntrospectHelper
62              implements PrivilegedExceptionAction {
63  
64          private Object bean;
65          private String prop;
66          private String value;
67          private ServletRequest request;
68          private String param;
69          private boolean ignoreMethodNF;
70  
71          PrivilegedIntrospectHelper(Object bean, String prop,
72                                     String value, ServletRequest request,
73                                     String param, boolean ignoreMethodNF) {
74              this.bean = bean;
75              this.prop = prop;
76              this.value = value;
77              this.request = request;
78              this.param = param;
79              this.ignoreMethodNF = ignoreMethodNF;
80          }
81  
82          public Object run() throws JasperException {
83              internalIntrospecthelper(
84                      bean, prop, value, request, param, ignoreMethodNF);
85              return null;
86          }
87      }
88  
89      /***
90       * Returns the value of the javax.servlet.error.exception request
91       * attribute value, if present, otherwise the value of the
92       * javax.servlet.jsp.jspException request attribute value.
93       * <p/>
94       * This method is called at the beginning of the generated servlet code
95       * for a JSP error page, when the "exception" implicit scripting language
96       * variable is initialized.
97       */
98      public static Throwable getThrowable(ServletRequest request) {
99          Throwable error = (Throwable) request.getAttribute(SERVLET_EXCEPTION);
100         if (error == null) {
101             error = (Throwable) request.getAttribute(JSP_EXCEPTION);
102             if (error != null) {
103                 /*
104                  * The only place that sets JSP_EXCEPTION is
105                  * PageContextImpl.handlePageException(). It really should set
106                  * SERVLET_EXCEPTION, but that would interfere with the 
107                  * ErrorReportValve. Therefore, if JSP_EXCEPTION is set, we
108                  * need to set SERVLET_EXCEPTION.
109                  */
110                 request.setAttribute(SERVLET_EXCEPTION, error);
111             }
112         }
113 
114         return error;
115     }
116 
117     public static boolean coerceToBoolean(String s) {
118         if (s == null || s.length() == 0)
119             return false;
120         else
121             return Boolean.valueOf(s).booleanValue();
122     }
123 
124     public static byte coerceToByte(String s) {
125         if (s == null || s.length() == 0)
126             return (byte) 0;
127         else
128             return Byte.valueOf(s).byteValue();
129     }
130 
131     public static char coerceToChar(String s) {
132         if (s == null || s.length() == 0) {
133             return (char) 0;
134         } else {
135             // this trick avoids escaping issues
136             return (char) (int) s.charAt(0);
137         }
138     }
139 
140     public static double coerceToDouble(String s) {
141         if (s == null || s.length() == 0)
142             return (double) 0;
143         else
144             return Double.valueOf(s).doubleValue();
145     }
146 
147     public static float coerceToFloat(String s) {
148         if (s == null || s.length() == 0)
149             return (float) 0;
150         else
151             return Float.valueOf(s).floatValue();
152     }
153 
154     public static int coerceToInt(String s) {
155         if (s == null || s.length() == 0)
156             return 0;
157         else
158             return Integer.valueOf(s).intValue();
159     }
160 
161     public static short coerceToShort(String s) {
162         if (s == null || s.length() == 0)
163             return (short) 0;
164         else
165             return Short.valueOf(s).shortValue();
166     }
167 
168     public static long coerceToLong(String s) {
169         if (s == null || s.length() == 0)
170             return (long) 0;
171         else
172             return Long.valueOf(s).longValue();
173     }
174 
175     public static Object coerce(String s, Class target) {
176 
177         boolean isNullOrEmpty = (s == null || s.length() == 0);
178 
179         if (target == Boolean.class) {
180             if (isNullOrEmpty) {
181                 s = "false";
182             }
183             return new Boolean(s);
184         } else if (target == Byte.class) {
185             if (isNullOrEmpty)
186                 return new Byte((byte) 0);
187             else
188                 return new Byte(s);
189         } else if (target == Character.class) {
190             if (isNullOrEmpty)
191                 return new Character((char) 0);
192             else
193                 return new Character(s.charAt(0));
194         } else if (target == Double.class) {
195             if (isNullOrEmpty)
196                 return new Double(0);
197             else
198                 return new Double(s);
199         } else if (target == Float.class) {
200             if (isNullOrEmpty)
201                 return new Float(0);
202             else
203                 return new Float(s);
204         } else if (target == Integer.class) {
205             if (isNullOrEmpty)
206                 return new Integer(0);
207             else
208                 return new Integer(s);
209         } else if (target == Short.class) {
210             if (isNullOrEmpty)
211                 return new Short((short) 0);
212             else
213                 return new Short(s);
214         } else if (target == Long.class) {
215             if (isNullOrEmpty)
216                 return new Long(0);
217             else
218                 return new Long(s);
219         } else {
220             return null;
221         }
222     }
223 
224     // __begin convertMethod
225     public static Object convert(String propertyName, String s, Class t,
226                                  Class propertyEditorClass)
227             throws JasperException {
228         try {
229             if (s == null) {
230                 if (t.equals(Boolean.class) || t.equals(Boolean.TYPE))
231                     s = "false";
232                 else
233                     return null;
234             }
235             if (propertyEditorClass != null) {
236                 return getValueFromBeanInfoPropertyEditor(
237                         t, propertyName, s, propertyEditorClass);
238             } else if (t.equals(Boolean.class) || t.equals(Boolean.TYPE)) {
239                 if (s.equalsIgnoreCase("on") || s.equalsIgnoreCase("true"))
240                     s = "true";
241                 else
242                     s = "false";
243                 return new Boolean(s);
244             } else if (t.equals(Byte.class) || t.equals(Byte.TYPE)) {
245                 return new Byte(s);
246             } else if (t.equals(Character.class) || t.equals(Character.TYPE)) {
247                 return s.length() > 0 ? new Character(s.charAt(0)) : null;
248             } else if (t.equals(Short.class) || t.equals(Short.TYPE)) {
249                 return new Short(s);
250             } else if (t.equals(Integer.class) || t.equals(Integer.TYPE)) {
251                 return new Integer(s);
252             } else if (t.equals(Float.class) || t.equals(Float.TYPE)) {
253                 return new Float(s);
254             } else if (t.equals(Long.class) || t.equals(Long.TYPE)) {
255                 return new Long(s);
256             } else if (t.equals(Double.class) || t.equals(Double.TYPE)) {
257                 return new Double(s);
258             } else if (t.equals(String.class)) {
259                 return s;
260             } else if (t.equals(java.io.File.class)) {
261                 return new java.io.File(s);
262             } else if (t.getName().equals("java.lang.Object")) {
263                 return new Object[]{s};
264             } else {
265                 return getValueFromPropertyEditorManager(
266                         t, propertyName, s);
267             }
268         } catch (Exception ex) {
269             throw new JasperException(ex);
270         }
271     }
272     // __end convertMethod
273 
274     // __begin introspectMethod
275 
276     public static void introspect(Object bean, ServletRequest request)
277             throws JasperException {
278         Enumeration e = request.getParameterNames();
279         while (e.hasMoreElements()) {
280             String name = (String) e.nextElement();
281             String value = request.getParameter(name);
282             introspecthelper(bean, name, value, request, name, true);
283         }
284     }
285     // __end introspectMethod
286 
287     // __begin introspecthelperMethod
288 
289     public static void introspecthelper(Object bean, String prop,
290                                         String value, ServletRequest request,
291                                         String param, boolean ignoreMethodNF)
292             throws JasperException {
293         if (System.getSecurityManager() != null) {
294             try {
295                 PrivilegedIntrospectHelper dp =
296                         new PrivilegedIntrospectHelper(
297                                 bean, prop, value, request, param, ignoreMethodNF);
298                 AccessController.doPrivileged(dp);
299             } catch (PrivilegedActionException pe) {
300                 Exception e = pe.getException();
301                 throw (JasperException) e;
302             }
303         } else {
304             internalIntrospecthelper(
305                     bean, prop, value, request, param, ignoreMethodNF);
306         }
307     }
308 
309     private static void internalIntrospecthelper(Object bean, String prop,
310                                                  String value, ServletRequest request,
311                                                  String param, boolean ignoreMethodNF)
312             throws JasperException {
313         Method method = null;
314         Class type = null;
315         Class propertyEditorClass = null;
316         try {
317             java.beans.BeanInfo info
318                     = java.beans.Introspector.getBeanInfo(bean.getClass());
319             if (info != null) {
320                 java.beans.PropertyDescriptor pd[]
321                         = info.getPropertyDescriptors();
322                 for (int i = 0; i < pd.length; i++) {
323                     if (pd[i].getName().equals(prop)) {
324                         method = pd[i].getWriteMethod();
325                         type = pd[i].getPropertyType();
326                         propertyEditorClass = pd[i].getPropertyEditorClass();
327                         break;
328                     }
329                 }
330             }
331             if (method != null) {
332                 if (type.isArray()) {
333                     if (request == null) {
334                         throw new JasperException(
335                                 Localizer.getMessage("jsp.error.beans.setproperty.noindexset"));
336                     }
337                     Class t = type.getComponentType();
338                     String[] values = request.getParameterValues(param);
339                     //XXX Please check.
340                     if (values == null) return;
341                     if (t.equals(String.class)) {
342                         method.invoke(bean, new Object[]{values});
343                     } else {
344                         createTypedArray(prop, bean, method, values, t,
345                                 propertyEditorClass);
346                     }
347                 } else {
348                     if (value == null || (param != null && value.equals(""))) return;
349                     Object oval = convert(prop, value, type, propertyEditorClass);
350                     if (oval != null)
351                         method.invoke(bean, new Object[]{oval});
352                 }
353             }
354         } catch (Exception ex) {
355             throw new JasperException(ex);
356         }
357         if (!ignoreMethodNF && (method == null)) {
358             if (type == null) {
359                 throw new JasperException(
360                         Localizer.getMessage("jsp.error.beans.noproperty",
361                                 prop,
362                                 bean.getClass().getName()));
363             } else {
364                 throw new JasperException(
365                         Localizer.getMessage("jsp.error.beans.nomethod.setproperty",
366                                 prop,
367                                 type.getName(),
368                                 bean.getClass().getName()));
369             }
370         }
371     }
372     // __end introspecthelperMethod
373 
374     //-------------------------------------------------------------------
375     // functions to convert builtin Java data types to string.
376     //-------------------------------------------------------------------
377     // __begin toStringMethod
378 
379     public static String toString(Object o) {
380         return String.valueOf(o);
381     }
382 
383     public static String toString(byte b) {
384         return new Byte(b).toString();
385     }
386 
387     public static String toString(boolean b) {
388         return new Boolean(b).toString();
389     }
390 
391     public static String toString(short s) {
392         return new Short(s).toString();
393     }
394 
395     public static String toString(int i) {
396         return new Integer(i).toString();
397     }
398 
399     public static String toString(float f) {
400         return new Float(f).toString();
401     }
402 
403     public static String toString(long l) {
404         return new Long(l).toString();
405     }
406 
407     public static String toString(double d) {
408         return new Double(d).toString();
409     }
410 
411     public static String toString(char c) {
412         return new Character(c).toString();
413     }
414     // __end toStringMethod
415 
416 
417     /***
418      * Create a typed array.
419      * This is a special case where params are passed through
420      * the request and the property is indexed.
421      */
422     public static void createTypedArray(String propertyName,
423                                         Object bean,
424                                         Method method,
425                                         String[] values,
426                                         Class t,
427                                         Class propertyEditorClass)
428             throws JasperException {
429 
430         try {
431             if (propertyEditorClass != null) {
432                 Object[] tmpval = new Integer[values.length];
433                 for (int i = 0; i < values.length; i++) {
434                     tmpval[i] = getValueFromBeanInfoPropertyEditor(
435                             t, propertyName, values[i], propertyEditorClass);
436                 }
437                 method.invoke(bean, new Object[]{tmpval});
438             } else if (t.equals(Integer.class)) {
439                 Integer[] tmpval = new Integer[values.length];
440                 for (int i = 0; i < values.length; i++)
441                     tmpval[i] = new Integer(values[i]);
442                 method.invoke(bean, new Object[]{tmpval});
443             } else if (t.equals(Byte.class)) {
444                 Byte[] tmpval = new Byte[values.length];
445                 for (int i = 0; i < values.length; i++)
446                     tmpval[i] = new Byte(values[i]);
447                 method.invoke(bean, new Object[]{tmpval});
448             } else if (t.equals(Boolean.class)) {
449                 Boolean[] tmpval = new Boolean[values.length];
450                 for (int i = 0; i < values.length; i++)
451                     tmpval[i] = new Boolean(values[i]);
452                 method.invoke(bean, new Object[]{tmpval});
453             } else if (t.equals(Short.class)) {
454                 Short[] tmpval = new Short[values.length];
455                 for (int i = 0; i < values.length; i++)
456                     tmpval[i] = new Short(values[i]);
457                 method.invoke(bean, new Object[]{tmpval});
458             } else if (t.equals(Long.class)) {
459                 Long[] tmpval = new Long[values.length];
460                 for (int i = 0; i < values.length; i++)
461                     tmpval[i] = new Long(values[i]);
462                 method.invoke(bean, new Object[]{tmpval});
463             } else if (t.equals(Double.class)) {
464                 Double[] tmpval = new Double[values.length];
465                 for (int i = 0; i < values.length; i++)
466                     tmpval[i] = new Double(values[i]);
467                 method.invoke(bean, new Object[]{tmpval});
468             } else if (t.equals(Float.class)) {
469                 Float[] tmpval = new Float[values.length];
470                 for (int i = 0; i < values.length; i++)
471                     tmpval[i] = new Float(values[i]);
472                 method.invoke(bean, new Object[]{tmpval});
473             } else if (t.equals(Character.class)) {
474                 Character[] tmpval = new Character[values.length];
475                 for (int i = 0; i < values.length; i++)
476                     tmpval[i] = new Character(values[i].charAt(0));
477                 method.invoke(bean, new Object[]{tmpval});
478             } else if (t.equals(int.class)) {
479                 int[] tmpval = new int[values.length];
480                 for (int i = 0; i < values.length; i++)
481                     tmpval[i] = Integer.parseInt(values[i]);
482                 method.invoke(bean, new Object[]{tmpval});
483             } else if (t.equals(byte.class)) {
484                 byte[] tmpval = new byte[values.length];
485                 for (int i = 0; i < values.length; i++)
486                     tmpval[i] = Byte.parseByte(values[i]);
487                 method.invoke(bean, new Object[]{tmpval});
488             } else if (t.equals(boolean.class)) {
489                 boolean[] tmpval = new boolean[values.length];
490                 for (int i = 0; i < values.length; i++)
491                     tmpval[i] = (Boolean.valueOf(values[i])).booleanValue();
492                 method.invoke(bean, new Object[]{tmpval});
493             } else if (t.equals(short.class)) {
494                 short[] tmpval = new short[values.length];
495                 for (int i = 0; i < values.length; i++)
496                     tmpval[i] = Short.parseShort(values[i]);
497                 method.invoke(bean, new Object[]{tmpval});
498             } else if (t.equals(long.class)) {
499                 long[] tmpval = new long[values.length];
500                 for (int i = 0; i < values.length; i++)
501                     tmpval[i] = Long.parseLong(values[i]);
502                 method.invoke(bean, new Object[]{tmpval});
503             } else if (t.equals(double.class)) {
504                 double[] tmpval = new double[values.length];
505                 for (int i = 0; i < values.length; i++)
506                     tmpval[i] = Double.valueOf(values[i]).doubleValue();
507                 method.invoke(bean, new Object[]{tmpval});
508             } else if (t.equals(float.class)) {
509                 float[] tmpval = new float[values.length];
510                 for (int i = 0; i < values.length; i++)
511                     tmpval[i] = Float.valueOf(values[i]).floatValue();
512                 method.invoke(bean, new Object[]{tmpval});
513             } else if (t.equals(char.class)) {
514                 char[] tmpval = new char[values.length];
515                 for (int i = 0; i < values.length; i++)
516                     tmpval[i] = values[i].charAt(0);
517                 method.invoke(bean, new Object[]{tmpval});
518             } else {
519                 Object[] tmpval = new Integer[values.length];
520                 for (int i = 0; i < values.length; i++) {
521                     tmpval[i] =
522                             getValueFromPropertyEditorManager(
523                                     t, propertyName, values[i]);
524                 }
525                 method.invoke(bean, new Object[]{tmpval});
526             }
527         } catch (Exception ex) {
528             throw new JasperException("error in invoking method", ex);
529         }
530     }
531 
532     /***
533      * Escape special shell characters.
534      *
535      * @param unescString The string to shell-escape
536      * @return The escaped shell string.
537      */
538 
539     public static String escapeQueryString(String unescString) {
540         if (unescString == null)
541             return null;
542 
543         String escString = "";
544         String shellSpChars = "&;`'\"|*?~<>^()[]{}$//\n";
545 
546         for (int index = 0; index < unescString.length(); index++) {
547             char nextChar = unescString.charAt(index);
548 
549             if (shellSpChars.indexOf(nextChar) != -1)
550                 escString += "//";
551 
552             escString += nextChar;
553         }
554         return escString;
555     }
556 
557     /***
558      * Decode an URL formatted string.
559      *
560      * @param encoded The string to decode.
561      * @return The decoded string.
562      */
563 
564     public static String decode(String encoded) {
565         // speedily leave if we're not needed
566         if (encoded == null) return null;
567         if (encoded.indexOf('%') == -1 && encoded.indexOf('+') == -1)
568             return encoded;
569 
570         //allocate the buffer - use byte[] to avoid calls to new.
571         byte holdbuffer[] = new byte[encoded.length()];
572 
573         int bufcount = 0;
574 
575         for (int count = 0; count < encoded.length(); count++) {
576             char cur = encoded.charAt(count);
577             if (cur == '%') {
578                 holdbuffer[bufcount++] =
579                         (byte) Integer.parseInt(encoded.substring(count + 1, count + 3), 16);
580                 if (count + 2 >= encoded.length())
581                     count = encoded.length();
582                 else
583                     count += 2;
584             } else if (cur == '+') {
585                 holdbuffer[bufcount++] = (byte) ' ';
586             } else {
587                 holdbuffer[bufcount++] = (byte) cur;
588             }
589         }
590         // REVISIT -- remedy for Deprecated warning.
591         //return new String(holdbuffer,0,0,bufcount);
592         return new String(holdbuffer, 0, bufcount);
593     }
594 
595     // __begin lookupReadMethodMethod
596     public static Object handleGetProperty(Object o, String prop)
597             throws JasperException {
598         if (o == null) {
599             throw new JasperException(
600                     Localizer.getMessage("jsp.error.beans.nullbean"));
601         }
602         Object value = null;
603         try {
604             Method method = getReadMethod(o.getClass(), prop);
605             value = method.invoke(o, null);
606         } catch (Exception ex) {
607             throw new JasperException(ex);
608         }
609         return value;
610     }
611     // __end lookupReadMethodMethod
612 
613     // handles <jsp:setProperty> with EL expression for 'value' attribute
614 
615     /***
616      * Use proprietaryEvaluate
617      * public static void handleSetPropertyExpression(Object bean,
618      * String prop, String expression, PageContext pageContext,
619      * VariableResolver variableResolver, FunctionMapper functionMapper )
620      * throws JasperException
621      * {
622      * try {
623      * Method method = getWriteMethod(bean.getClass(), prop);
624      * method.invoke(bean, new Object[] {
625      * pageContext.getExpressionEvaluator().evaluate(
626      * expression,
627      * method.getParameterTypes()[0],
628      * variableResolver,
629      * functionMapper,
630      * null )
631      * });
632      * } catch (Exception ex) {
633      * throw new JasperException(ex);
634      * }
635      * }
636      */
637     public static void handleSetPropertyExpression(Object bean,
638                                                    String prop, String expression, PageContext pageContext,
639                                                    ProtectedFunctionMapper functionMapper)
640             throws JasperException {
641         try {
642             Method method = getWriteMethod(bean.getClass(), prop);
643             method.invoke(bean, new Object[]{
644                     PageContextImpl.proprietaryEvaluate(
645                             expression,
646                             method.getParameterTypes()[0],
647                             pageContext,
648                             functionMapper,
649                             false)
650             });
651         } catch (Exception ex) {
652             throw new JasperException(ex);
653         }
654     }
655 
656     public static void handleSetProperty(Object bean, String prop,
657                                          Object value)
658             throws JasperException {
659         try {
660             Method method = getWriteMethod(bean.getClass(), prop);
661             method.invoke(bean, new Object[]{value});
662         } catch (Exception ex) {
663             throw new JasperException(ex);
664         }
665     }
666 
667     public static void handleSetProperty(Object bean, String prop,
668                                          int value)
669             throws JasperException {
670         try {
671             Method method = getWriteMethod(bean.getClass(), prop);
672             method.invoke(bean, new Object[]{new Integer(value)});
673         } catch (Exception ex) {
674             throw new JasperException(ex);
675         }
676     }
677 
678     public static void handleSetProperty(Object bean, String prop,
679                                          short value)
680             throws JasperException {
681         try {
682             Method method = getWriteMethod(bean.getClass(), prop);
683             method.invoke(bean, new Object[]{new Short(value)});
684         } catch (Exception ex) {
685             throw new JasperException(ex);
686         }
687     }
688 
689     public static void handleSetProperty(Object bean, String prop,
690                                          long value)
691             throws JasperException {
692         try {
693             Method method = getWriteMethod(bean.getClass(), prop);
694             method.invoke(bean, new Object[]{new Long(value)});
695         } catch (Exception ex) {
696             throw new JasperException(ex);
697         }
698     }
699 
700     public static void handleSetProperty(Object bean, String prop,
701                                          double value)
702             throws JasperException {
703         try {
704             Method method = getWriteMethod(bean.getClass(), prop);
705             method.invoke(bean, new Object[]{new Double(value)});
706         } catch (Exception ex) {
707             throw new JasperException(ex);
708         }
709     }
710 
711     public static void handleSetProperty(Object bean, String prop,
712                                          float value)
713             throws JasperException {
714         try {
715             Method method = getWriteMethod(bean.getClass(), prop);
716             method.invoke(bean, new Object[]{new Float(value)});
717         } catch (Exception ex) {
718             throw new JasperException(ex);
719         }
720     }
721 
722     public static void handleSetProperty(Object bean, String prop,
723                                          char value)
724             throws JasperException {
725         try {
726             Method method = getWriteMethod(bean.getClass(), prop);
727             method.invoke(bean, new Object[]{new Character(value)});
728         } catch (Exception ex) {
729             throw new JasperException(ex);
730         }
731     }
732 
733     public static void handleSetProperty(Object bean, String prop,
734                                          byte value)
735             throws JasperException {
736         try {
737             Method method = getWriteMethod(bean.getClass(), prop);
738             method.invoke(bean, new Object[]{new Byte(value)});
739         } catch (Exception ex) {
740             throw new JasperException(ex);
741         }
742     }
743 
744     public static void handleSetProperty(Object bean, String prop,
745                                          boolean value)
746             throws JasperException {
747         try {
748             Method method = getWriteMethod(bean.getClass(), prop);
749             method.invoke(bean, new Object[]{new Boolean(value)});
750         } catch (Exception ex) {
751             throw new JasperException(ex);
752         }
753     }
754 
755     public static Method getWriteMethod(Class beanClass, String prop)
756             throws JasperException {
757         Method method = null;
758         Class type = null;
759         try {
760             java.beans.BeanInfo info
761                     = java.beans.Introspector.getBeanInfo(beanClass);
762             if (info != null) {
763                 java.beans.PropertyDescriptor pd[]
764                         = info.getPropertyDescriptors();
765                 for (int i = 0; i < pd.length; i++) {
766                     if (pd[i].getName().equals(prop)) {
767                         method = pd[i].getWriteMethod();
768                         type = pd[i].getPropertyType();
769                         break;
770                     }
771                 }
772             } else {
773                 // just in case introspection silently fails.
774                 throw new JasperException(
775                         Localizer.getMessage("jsp.error.beans.nobeaninfo",
776                                 beanClass.getName()));
777             }
778         } catch (Exception ex) {
779             throw new JasperException(ex);
780         }
781         if (method == null) {
782             if (type == null) {
783                 throw new JasperException(
784                         Localizer.getMessage("jsp.error.beans.noproperty",
785                                 prop,
786                                 beanClass.getName()));
787             } else {
788                 throw new JasperException(
789                         Localizer.getMessage("jsp.error.beans.nomethod.setproperty",
790                                 prop,
791                                 type.getName(),
792                                 beanClass.getName()));
793             }
794         }
795         return method;
796     }
797 
798     public static Method getReadMethod(Class beanClass, String prop)
799             throws JasperException {
800 
801         Method method = null;
802         Class type = null;
803         try {
804             java.beans.BeanInfo info
805                     = java.beans.Introspector.getBeanInfo(beanClass);
806             if (info != null) {
807                 java.beans.PropertyDescriptor pd[]
808                         = info.getPropertyDescriptors();
809                 for (int i = 0; i < pd.length; i++) {
810                     if (pd[i].getName().equals(prop)) {
811                         method = pd[i].getReadMethod();
812                         type = pd[i].getPropertyType();
813                         break;
814                     }
815                 }
816             } else {
817                 // just in case introspection silently fails.
818                 throw new JasperException(
819                         Localizer.getMessage("jsp.error.beans.nobeaninfo",
820                                 beanClass.getName()));
821             }
822         } catch (Exception ex) {
823             throw new JasperException(ex);
824         }
825         if (method == null) {
826             if (type == null) {
827                 throw new JasperException(
828                         Localizer.getMessage("jsp.error.beans.noproperty", prop,
829                                 beanClass.getName()));
830             } else {
831                 throw new JasperException(
832                         Localizer.getMessage("jsp.error.beans.nomethod", prop,
833                                 beanClass.getName()));
834             }
835         }
836 
837         return method;
838     }
839 
840     //**********************************************************************
841     // PropertyEditor Support
842 
843     public static Object getValueFromBeanInfoPropertyEditor(
844             Class attrClass, String attrName, String attrValue,
845             Class propertyEditorClass)
846             throws JasperException {
847         try {
848             PropertyEditor pe = (PropertyEditor) propertyEditorClass.newInstance();
849             pe.setAsText(attrValue);
850             return pe.getValue();
851         } catch (Exception ex) {
852             throw new JasperException(
853                     Localizer.getMessage("jsp.error.beans.property.conversion",
854                             attrValue, attrClass.getName(), attrName,
855                             ex.getMessage()));
856         }
857     }
858 
859     public static Object getValueFromPropertyEditorManager(
860             Class attrClass, String attrName, String attrValue)
861             throws JasperException {
862         try {
863             PropertyEditor propEditor =
864                     PropertyEditorManager.findEditor(attrClass);
865             if (propEditor != null) {
866                 propEditor.setAsText(attrValue);
867                 return propEditor.getValue();
868             } else {
869                 throw new IllegalArgumentException(
870                         Localizer.getMessage("jsp.error.beans.propertyeditor.notregistered"));
871             }
872         } catch (IllegalArgumentException ex) {
873             throw new JasperException(
874                     Localizer.getMessage("jsp.error.beans.property.conversion",
875                             attrValue, attrClass.getName(), attrName,
876                             ex.getMessage()));
877         }
878     }
879 
880 
881     // ************************************************************************
882     // General Purpose Runtime Methods
883     // ************************************************************************
884 
885 
886     /**
887      * Convert a possibly relative resource path into a context-relative
888      * resource path that starts with a '/'.
889      *
890      * @param request      The servlet request we are processing
891      * @param relativePath The possibly relative resource path
892      */
893     public static String getContextRelativePath(ServletRequest request,
894                                                 String relativePath) {
895 
896         if (relativePath.startsWith("/"))
897             return (relativePath);
898         if (!(request instanceof HttpServletRequest))
899             return (relativePath);
900         HttpServletRequest hrequest = (HttpServletRequest) request;
901         String uri = (String)
902                 request.getAttribute("javax.servlet.include.servlet_path");
903         if (uri != null) {
904             String pathInfo = (String)
905                     request.getAttribute("javax.servlet.include.path_info");
906             if (pathInfo == null) {
907                 if (uri.lastIndexOf('/') >= 0)
908                     uri = uri.substring(0, uri.lastIndexOf('/'));
909             }
910         } else {
911             uri = hrequest.getServletPath();
912             if (uri.lastIndexOf('/') >= 0)
913                 uri = uri.substring(0, uri.lastIndexOf('/'));
914         }
915         return uri + '/' + relativePath;
916 
917     }
918 
919 
920     /***
921      * Perform a RequestDispatcher.include() operation, with optional flushing
922      * of the response beforehand.
923      *
924      * @param request      The servlet request we are processing
925      * @param response     The servlet response we are processing
926      * @param relativePath The relative path of the resource to be included
927      * @param out          The Writer to whom we are currently writing
928      * @param flush        Should we flush before the include is processed?
929      * @throws IOException      if thrown by the included servlet
930      * @throws ServletException if thrown by the included servlet
931      */
932     public static void include(ServletRequest request,
933                                ServletResponse response,
934                                String relativePath,
935                                JspWriter out,
936                                boolean flush)
937             throws IOException, ServletException {
938 
939         if (flush && !(out instanceof BodyContent))
940             out.flush();
941 
942         // FIXME - It is tempting to use request.getRequestDispatcher() to
943         // resolve a relative path directly, but Catalina currently does not
944         // take into account whether the caller is inside a RequestDispatcher
945         // include or not.  Whether Catalina *should* take that into account
946         // is a spec issue currently under review.  In the mean time,
947         // replicate Jasper's previous behavior
948 
949         String resourcePath = getContextRelativePath(request, relativePath);
950         RequestDispatcher rd = request.getRequestDispatcher(resourcePath);
951 
952         rd.include(request,
953                 new ServletResponseWrapperInclude(response, out));
954 
955     }
956 
957     /***
958      * URL encodes a string, based on the supplied character encoding.
959      * This performs the same function as java.next.URLEncode.encode
960      * in J2SDK1.4, and should be removed if the only platform supported
961      * is 1.4 or higher.
962      *
963      * @param s   The String to be URL encoded.
964      * @param enc The character encoding
965      * @return The URL encoded String
966      */
967     public static String URLEncode(String s, String enc) {
968 
969         if (s == null) {
970             return "null";
971         }
972 
973         if (enc == null) {
974             enc = "ISO-8859-1";        // The default request encoding 
975         }
976 
977         StringBuffer out = new StringBuffer(s.length());
978         ByteArrayOutputStream buf = new ByteArrayOutputStream();
979         OutputStreamWriter writer = null;
980         try {
981             writer = new OutputStreamWriter(buf, enc);
982         } catch (java.io.UnsupportedEncodingException ex) {
983             // Use the default encoding?
984             writer = new OutputStreamWriter(buf);
985         }
986 
987         for (int i = 0; i < s.length(); i++) {
988             int c = s.charAt(i);
989             if (c == ' ') {
990                 out.append('+');
991             } else if (isSafeChar(c)) {
992                 out.append((char) c);
993             } else {
994                 // convert to external encoding before hex conversion
995                 try {
996                     writer.write(c);
997                     writer.flush();
998                 } catch (IOException e) {
999                     buf.reset();
1000                     continue;
1001                 }
1002                 byte[] ba = buf.toByteArray();
1003                 for (int j = 0; j < ba.length; j++) {
1004                     out.append('%');
1005                     // Converting each byte in the buffer
1006                     out.append(Character.forDigit((ba[j] >> 4) & 0xf, 16));
1007                     out.append(Character.forDigit(ba[j] & 0xf, 16));
1008                 }
1009                 buf.reset();
1010             }
1011         }
1012         return out.toString();
1013     }
1014 
1015     private static boolean isSafeChar(int c) {
1016         if (c >= 'a' && c <= 'z') {
1017             return true;
1018         }
1019         if (c >= 'A' && c <= 'Z') {
1020             return true;
1021         }
1022         if (c >= '0' && c <= '9') {
1023             return true;
1024         }
1025         if (c == '-' || c == '_' || c == '.' || c == '!' ||
1026                 c == '~' || c == '*' || c == '\'' || c == '(' || c == ')') {
1027             return true;
1028         }
1029         return false;
1030     }
1031 
1032 }