001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.commons.el;
018    
019    import java.beans.PropertyEditor;
020    import java.beans.PropertyEditorManager;
021    import java.math.BigDecimal;
022    import java.math.BigInteger;
023    
024    import javax.servlet.jsp.el.ELException;
025    
026    import org.apache.commons.logging.Log;
027    import org.apache.commons.logging.LogFactory;
028    
029    /**
030     *
031     * <p>This class contains the logic for coercing data types before
032     * operators are applied to them.
033     *
034     * <p>The following is the list of rules applied for various type
035     * conversions.
036     *
037     * <ul><pre>
038     * Applying arithmetic operator
039     *   Binary operator - A {+,-,*} B
040     *     if A and B are null
041     *       return 0
042     *     if A or B is BigDecimal, coerce both to BigDecimal and then:
043     *       if operator is +, return <code>A.add(B)</code>
044     *       if operator is -, return <code>A.subtract(B)</code>
045     *       if operator is *, return <code>A.multiply(B)</code>
046     *     if A or B is Float, Double, or String containing ".", "e", or "E"
047     *       if A or B is BigInteger, coerce both A and B to BigDecimal and apply operator
048     *       coerce both A and B to Double and apply operator
049     *     if A or B is BigInteger, coerce both to BigInteger and then:
050     *       if operator is +, return <code>A.add(B)</code>
051     *       if operator is -, return <code>A.subtract(B)</code>
052     *       if operator is *, return <code>A.multiply(B)</code>
053     *     otherwise
054     *       coerce both A and B to Long
055     *       apply operator
056     *     if operator results in exception (such as divide by 0), error
057     * 
058     *   Binary operator - A {/,div} B
059     *     if A and B are null
060     *       return 0
061     *     if A or B is a BigDecimal or BigInteger, coerce both to BigDecimal and
062     *      return <code>A.divide(B, BigDecimal.ROUND_HALF_UP)</code>
063     *     otherwise
064     *       coerce both A and B to Double
065     *       apply operator
066     *     if operator results in exception (such as divide by 0), error
067     * 
068     *   Binary operator - A {%,mod} B
069     *     if A and B are null
070     *       return 0
071     *     if A or B is BigDecimal, Float, Double, or String containing ".", "e" or "E"
072     *       coerce both to Double
073     *       apply operator
074     *     if A or B is BigInteger, coerce both to BigInteger and return
075     *      <code>A.remainder(B)</code>
076     *     otherwise
077     *       coerce both A and B to Long
078     *       apply operator
079     *     if operator results in exception (such as divide by 0), error
080     * 
081     *   Unary minus operator - -A
082     *     if A is null
083     *       return 0
084     *     if A is BigInteger or BigDecimal, return <code>A.negate()</code>
085     *     if A is String
086     *       if A contains ".", "e", or "E"
087     *         coerce to Double, apply operator
088     *       otherwise
089     *         coerce to a Long and apply operator
090     *     if A is Byte,Short,Integer,Long,Float,Double
091     *       retain type, apply operator
092     *     if operator results in exception, error
093     *     otherwise
094     *       error
095     *
096     * Applying "empty" operator - empty A
097     *   if A is null
098     *     return true
099     *   if A is zero-length String
100     *     return true
101     *   if A is zero-length array
102     *     return true
103     *   if A is List and ((List) A).isEmpty()
104     *     return true
105     *   if A is Map and ((Map) A).isEmpty()
106     *     return true
107     *   if A is Collection an ((Collection) A).isEmpty()
108     *     return true
109     *   otherwise
110     *     return false
111     * 
112     * Applying logical operators
113     *   Binary operator - A {and,or} B
114     *     coerce both A and B to Boolean, apply operator
115     *   NOTE - operator stops as soon as expression can be determined, i.e.,
116     *     A and B and C and D - if B is false, then only A and B is evaluated
117     *   Unary not operator - not A
118     *     coerce A to Boolean, apply operator
119     * 
120     * Applying relational operator
121     *   A {<,>,<=,>=,lt,gt,lte,gte} B
122     *     if A==B
123     *       if operator is >= or <=
124     *         return true
125     *       otherwise
126     *         return false
127     *     if A or B is null
128     *       return false
129     *     if A or B is BigDecimal, coerce both A and B to BigDecimal and use the
130     *      return value of <code>A.compareTo(B)</code>
131     *     if A or B is Float or Double
132     *       coerce both A and B to Double
133     *       apply operator
134     *     if A or B is BigInteger, coerce both A and B to BigInteger and use the
135     *      return value of <code>A.compareTo(B)</code>
136     *     if A or B is Byte,Short,Character,Integer,Long
137     *       coerce both A and B to Long
138     *       apply operator
139     *     if A or B is String
140     *       coerce both A and B to String, compare lexically
141     *     if A is Comparable
142     *       if A.compareTo (B) throws exception
143     *         error
144     *       otherwise
145     *         use result of A.compareTo(B)
146     *     if B is Comparable
147     *       if B.compareTo (A) throws exception
148     *         error
149     *       otherwise
150     *         use result of B.compareTo(A)
151     *     otherwise
152     *       error
153     * 
154     * Applying equality operator
155     *   A {==,!=} B
156     *     if A==B
157     *       apply operator
158     *     if A or B is null
159     *       return false for ==, true for !=
160     *     if A or B is BigDecimal, coerce both A and B to BigDecimal and then:
161     *       if operator is == or eq, return <code>A.equals(B)</code>
162     *       if operator is != or ne, return <code>!A.equals(B)</code>
163     *     if A or B is Float or Double
164     *       coerce both A and B to Double
165     *       apply operator
166     *     if A or B is BigInteger, coerce both A and B to BigInteger and then:
167     *       if operator is == or eq, return <code>A.equals(B)</code>
168     *       if operator is != or ne, return <code>!A.equals(B)</code>
169     *     if A or B is Byte,Short,Character,Integer,Long
170     *       coerce both A and B to Long
171     *       apply operator
172     *     if A or B is Boolean
173     *       coerce both A and B to Boolean
174     *       apply operator
175     *     if A or B is String
176     *       coerce both A and B to String, compare lexically
177     *     otherwise
178     *       if an error occurs while calling A.equals(B)
179     *         error
180     *       apply operator to result of A.equals(B)
181     * 
182     * coercions
183     * 
184     *   coerce A to String
185     *     A is String
186     *       return A
187     *     A is null
188     *       return ""
189     *     A.toString throws exception
190     *       error
191     *     otherwise
192     *       return A.toString
193     * 
194     *   coerce A to Number type N
195     *     A is null or ""
196     *       return 0
197     *     A is Character
198     *       convert to short, apply following rules
199     *     A is Boolean
200     *       error
201     *     A is Number type N
202     *       return A
203     *     A is Number, coerce quietly to type N using the following algorithm
204     *         If N is BigInteger
205     *             If A is BigDecimal, return <code>A.toBigInteger()</code>
206     *             Otherwise, return <code>BigInteger.valueOf(A.longValue())</code>
207     *        if N is BigDecimal
208     *             If A is a BigInteger, return <code>new BigDecimal(A)</code>
209     *             Otherwise, return <code>new BigDecimal(A.doubleValue())</code>
210     *        If N is Byte, return <code>new Byte(A.byteValue())</code>
211     *        If N is Short, return <code>new Short(A.shortValue())</code>
212     *        If N is Integer, return <code>new Integer(A.integerValue())</code>
213     *        If N is Long, return <code>new Long(A.longValue())</code>
214     *        If N is Float, return <code>new Float(A.floatValue())</code>
215     *        If N is Double, return <code>new Double(A.doubleValue())</code>
216     *        otherwise ERROR
217     *     A is String
218     *       If N is BigDecimal then:
219     *            If <code>new BigDecimal(A)</code> throws an exception then ERROR
220     *            Otherwise, return <code>new BigDecimal(A)</code>
221     *       If N is BigInteger then:
222     *            If <code>new BigInteger(A)</code> throws an exception, then ERROR
223     *            Otherwise, return <code>new BigInteger(A)</code>
224     *       new <code>N.valueOf(A)</code> throws exception
225     *         error
226     *       return <code>N.valueOf(A)</code>
227     *     otherwise
228     *       error
229     * 
230     *   coerce A to Character should be
231     *     A is null or ""
232     *       return (char) 0
233     *     A is Character
234     *       return A
235     *     A is Boolean
236     *       error
237     *     A is Number with less precision than short
238     *       coerce quietly - return (char) A
239     *     A is Number with greater precision than short
240     *       coerce quietly - return (char) A
241     *     A is String
242     *       return A.charAt (0)
243     *     otherwise
244     *       error
245     * 
246     *   coerce A to Boolean
247     *     A is null or ""
248     *       return false
249     *     A is Boolean
250     *       return A
251     *     A is String
252     *       Boolean.valueOf(A) throws exception
253     *         error
254     *       return Boolean.valueOf(A)
255     *     otherwise
256     *       error
257     * 
258     *   coerce A to any other type T
259     *     A is null
260     *       return null
261     *     A is assignable to T
262     *       coerce quietly
263     *     A is String
264     *       T has no PropertyEditor
265     *         if A is "", return null
266     *         otherwise error
267     *       T's PropertyEditor throws exception
268     *         if A is "", return null
269     *         otherwise error
270     *       otherwise
271     *         apply T's PropertyEditor
272     *     otherwise
273     *       error
274     * </pre></ul>
275     *
276     * @author Nathan Abramson - Art Technology Group
277     * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: bayard $
278     **/
279    
280    public class Coercions
281    {
282        //-------------------------------------
283        // Constants
284        //-------------------------------------
285       private static final Number ZERO = new Integer(0);
286        private static Log log = LogFactory.getLog(Coercions.class);
287        
288      //-------------------------------------
289      /**
290       *
291       * Coerces the given value to the specified class.
292       **/
293      public static Object coerce (Object pValue,
294                                   Class pClass)
295        throws ELException
296      {
297        if (pClass == String.class) {
298          return coerceToString (pValue);
299        }
300        else if (isNumberClass (pClass)) {
301          return coerceToPrimitiveNumber (pValue, pClass);
302        }
303        else if (pClass == Character.class ||
304                 pClass == Character.TYPE) {
305          return coerceToCharacter (pValue);
306        }
307        else if (pClass == Boolean.class ||
308                 pClass == Boolean.TYPE) {
309          return coerceToBoolean (pValue);
310        }
311        else {
312          return coerceToObject (pValue, pClass);
313        }
314      }
315    
316      //-------------------------------------
317      /**
318       *
319       * Returns true if the given class is Byte, Short, Integer, Long,
320       * Float, Double, BigInteger, or BigDecimal
321       **/
322      static boolean isNumberClass (Class pClass)
323      {
324        return
325          pClass == Byte.class ||
326          pClass == Byte.TYPE ||
327          pClass == Short.class ||
328          pClass == Short.TYPE ||
329          pClass == Integer.class ||
330          pClass == Integer.TYPE ||
331          pClass == Long.class ||
332          pClass == Long.TYPE ||
333          pClass == Float.class ||
334          pClass == Float.TYPE ||
335          pClass == Double.class ||
336          pClass == Double.TYPE ||
337          pClass == BigInteger.class ||
338          pClass == BigDecimal.class;
339      }
340    
341      //-------------------------------------
342      /**
343       *
344       * Coerces the specified value to a String
345       **/
346      public static String coerceToString (Object pValue)
347        throws ELException
348      {
349        if (pValue == null) {
350          return "";
351        }
352        else if (pValue instanceof String) {
353          return (String) pValue;
354        }
355        else {
356          try {
357            return pValue.toString ();
358          }
359          catch (Exception exc) {          
360              if (log.isErrorEnabled()) {
361                  String message = MessageUtil.getMessageWithArgs(
362                      Constants.TOSTRING_EXCEPTION,
363                      pValue.getClass().getName());
364                  log.error(message, exc);
365                  throw new ELException(exc);
366              }
367              return "";    
368          }
369        }
370      }
371    
372      //-------------------------------------
373      /**
374       *
375       * Coerces a value to the given primitive number class
376       **/
377      public static Number coerceToPrimitiveNumber (Object pValue,
378                                                    Class pClass)
379        throws ELException
380      {
381        if (pValue == null ||
382            "".equals (pValue)) {
383          return coerceToPrimitiveNumber (ZERO, pClass);
384        }
385        else if (pValue instanceof Character) {
386          char val = ((Character) pValue).charValue ();
387          return coerceToPrimitiveNumber (new Short((short) val), pClass);
388        }
389        else if (pValue instanceof Boolean) {
390            if (log.isErrorEnabled()) {
391                String message = MessageUtil.getMessageWithArgs(
392                    Constants.BOOLEAN_TO_NUMBER, pValue, pClass.getName());
393                log.error(message);
394                throw new ELException(message);
395            }
396            return coerceToPrimitiveNumber(ZERO, pClass);     
397        }
398        else if (pValue.getClass () == pClass) {
399          return (Number) pValue;
400        }
401        else if (pValue instanceof Number) {
402          return coerceToPrimitiveNumber ((Number) pValue, pClass);
403        }
404        else if (pValue instanceof String) {
405          try {
406            return coerceToPrimitiveNumber ((String) pValue, pClass);
407          }
408          catch (Exception exc) {
409              if (log.isErrorEnabled()) {
410                  String message = MessageUtil.getMessageWithArgs(
411                      Constants.STRING_TO_NUMBER_EXCEPTION,
412                      (String) pValue, pClass.getName());
413                  log.error(message);
414                  throw new ELException(message);
415              }     
416                return coerceToPrimitiveNumber (ZERO, pClass);
417          }
418        }
419        else {
420            if (log.isErrorEnabled()) {
421                String message = MessageUtil.getMessageWithArgs(
422                    Constants.COERCE_TO_NUMBER,
423                    pValue.getClass().getName(),
424                    pClass.getName());
425                log.error(message);
426                throw new ELException(message);
427            }      
428          return coerceToPrimitiveNumber (0, pClass);
429        }
430      }
431    
432      //-------------------------------------
433      /**
434       *
435       * Coerces a value to an Integer, returning null if the coercion
436       * isn't possible.
437       **/
438      public static Integer coerceToInteger (Object pValue)
439        throws ELException
440      {
441        if (pValue == null) {
442          return null;
443        }
444        else if (pValue instanceof Character) {
445          return PrimitiveObjects.getInteger 
446            ((int) (((Character) pValue).charValue ()));
447        }
448        else if (pValue instanceof Boolean) {
449            if (log.isWarnEnabled()) {
450                log.warn(
451                    MessageUtil.getMessageWithArgs(
452                        Constants.BOOLEAN_TO_NUMBER, pValue, Integer.class.getName()));            
453            }     
454          return PrimitiveObjects.getInteger
455            (((Boolean) pValue).booleanValue () ? 1 : 0);
456        }
457        else if (pValue instanceof Integer) {
458          return (Integer) pValue;
459        }
460        else if (pValue instanceof Number) {
461          return PrimitiveObjects.getInteger (((Number) pValue).intValue ());
462        }
463        else if (pValue instanceof String) {
464          try {
465            return Integer.valueOf ((String) pValue);
466          }
467          catch (Exception exc) {
468              if (log.isWarnEnabled()) {
469                  log.warn(
470                      MessageUtil.getMessageWithArgs(
471                          Constants.STRING_TO_NUMBER_EXCEPTION,
472                          (String) pValue,
473                          Integer.class.getName()));            
474              }     
475            return null;
476          }
477        }
478        else {
479            if (log.isWarnEnabled()) {
480                log.warn(
481                    MessageUtil.getMessageWithArgs(
482                        Constants.COERCE_TO_NUMBER,
483                        pValue.getClass().getName(),
484                        Integer.class.getName()));
485            }
486          return null;
487        }
488      }
489    
490      //-------------------------------------
491      /**
492       *
493       * Coerces a long to the given primitive number class
494       **/
495      static Number coerceToPrimitiveNumber (long pValue,
496                                             Class pClass)
497        throws ELException
498      {
499        if (pClass == Byte.class || pClass == Byte.TYPE) {
500          return PrimitiveObjects.getByte ((byte) pValue);
501        }
502        else if (pClass == Short.class || pClass == Short.TYPE) {
503          return PrimitiveObjects.getShort ((short) pValue);
504        }
505        else if (pClass == Integer.class || pClass == Integer.TYPE) {
506          return PrimitiveObjects.getInteger ((int) pValue);
507        }
508        else if (pClass == Long.class || pClass == Long.TYPE) {
509          return PrimitiveObjects.getLong (pValue);
510        }
511        else if (pClass == Float.class || pClass == Float.TYPE) {
512          return PrimitiveObjects.getFloat ((float) pValue);
513        }
514        else if (pClass == Double.class || pClass == Double.TYPE) {
515          return PrimitiveObjects.getDouble ((double) pValue);
516        }
517        else {
518          return PrimitiveObjects.getInteger (0);
519        }
520      }
521    
522      //-------------------------------------
523      /**
524       *
525       * Coerces a double to the given primitive number class
526       **/
527      static Number coerceToPrimitiveNumber (double pValue,
528                                             Class pClass)
529        throws ELException
530      {
531        if (pClass == Byte.class || pClass == Byte.TYPE) {
532          return PrimitiveObjects.getByte ((byte) pValue);
533        }
534        else if (pClass == Short.class || pClass == Short.TYPE) {
535          return PrimitiveObjects.getShort ((short) pValue);
536        }
537        else if (pClass == Integer.class || pClass == Integer.TYPE) {
538          return PrimitiveObjects.getInteger ((int) pValue);
539        }
540        else if (pClass == Long.class || pClass == Long.TYPE) {
541          return PrimitiveObjects.getLong ((long) pValue);
542        }
543        else if (pClass == Float.class || pClass == Float.TYPE) {
544          return PrimitiveObjects.getFloat ((float) pValue);
545        }
546        else if (pClass == Double.class || pClass == Double.TYPE) {
547          return PrimitiveObjects.getDouble (pValue);
548        }
549        else {
550          return PrimitiveObjects.getInteger (0);
551        }
552      }
553    
554      //-------------------------------------
555      /**
556       *
557       * Coerces a Number to the given primitive number class
558       **/
559      static Number coerceToPrimitiveNumber (Number pValue, Class pClass)
560        throws ELException
561      {
562        if (pClass == Byte.class || pClass == Byte.TYPE) {
563          return PrimitiveObjects.getByte (pValue.byteValue ());
564        }
565        else if (pClass == Short.class || pClass == Short.TYPE) {
566          return PrimitiveObjects.getShort (pValue.shortValue ());
567        }
568        else if (pClass == Integer.class || pClass == Integer.TYPE) {
569          return PrimitiveObjects.getInteger (pValue.intValue ());
570        }
571        else if (pClass == Long.class || pClass == Long.TYPE) {
572          return PrimitiveObjects.getLong (pValue.longValue ());
573        }
574        else if (pClass == Float.class || pClass == Float.TYPE) {
575          return PrimitiveObjects.getFloat (pValue.floatValue ());
576        }
577        else if (pClass == Double.class || pClass == Double.TYPE) {
578          return PrimitiveObjects.getDouble (pValue.doubleValue ());
579        }
580        else if (pClass == BigInteger.class) {
581            if (pValue instanceof BigDecimal)
582                return ((BigDecimal) pValue).toBigInteger();
583            else
584                return BigInteger.valueOf(pValue.longValue());
585        }
586        else if (pClass == BigDecimal.class) {
587            if (pValue instanceof BigInteger)
588                return new BigDecimal((BigInteger) pValue);
589            else
590                return new BigDecimal(pValue.doubleValue());
591        }
592        else {
593          return PrimitiveObjects.getInteger (0);
594        }
595      }
596    
597      //-------------------------------------
598      /**
599       *
600       * Coerces a String to the given primitive number class
601       **/
602      static Number coerceToPrimitiveNumber (String pValue, Class pClass)
603        throws ELException
604      {
605        if (pClass == Byte.class || pClass == Byte.TYPE) {
606          return Byte.valueOf (pValue);
607        }
608        else if (pClass == Short.class || pClass == Short.TYPE) {
609          return Short.valueOf (pValue);
610        }
611        else if (pClass == Integer.class || pClass == Integer.TYPE) {
612          return Integer.valueOf (pValue);
613        }
614        else if (pClass == Long.class || pClass == Long.TYPE) {
615          return Long.valueOf (pValue);
616        }
617        else if (pClass == Float.class || pClass == Float.TYPE) {
618          return Float.valueOf (pValue);
619        }
620        else if (pClass == Double.class || pClass == Double.TYPE) {
621          return Double.valueOf (pValue);
622        }
623        else if (pClass == BigInteger.class) {
624            return new BigInteger(pValue);
625        }
626        else if (pClass == BigDecimal.class) {
627            return new BigDecimal(pValue);
628        }
629        else {
630          return PrimitiveObjects.getInteger (0);
631        }
632      }
633    
634      //-------------------------------------
635      /**
636       *
637       * Coerces a value to a Character
638       **/
639      public static Character coerceToCharacter (Object pValue)
640        throws ELException
641      {
642        if (pValue == null ||
643            "".equals (pValue)) {
644          return PrimitiveObjects.getCharacter ((char) 0);
645        }
646        else if (pValue instanceof Character) {
647          return (Character) pValue;
648        }
649        else if (pValue instanceof Boolean) {
650            if (log.isErrorEnabled()) {
651                String message = MessageUtil.getMessageWithArgs(
652                    Constants.BOOLEAN_TO_CHARACTER, pValue);
653                log.error(message);
654                throw new ELException(message);
655            }     
656          return PrimitiveObjects.getCharacter ((char) 0);
657        }
658        else if (pValue instanceof Number) {
659          return PrimitiveObjects.getCharacter 
660            ((char) ((Number) pValue).shortValue ());
661        }
662        else if (pValue instanceof String) {
663          String str = (String) pValue;
664          return PrimitiveObjects.getCharacter (str.charAt (0));
665        }
666        else {
667            if (log.isErrorEnabled()) {
668                String message = MessageUtil.getMessageWithArgs(
669                    Constants.COERCE_TO_CHARACTER,
670                    pValue.getClass().getName());
671                log.error(message);
672                throw new ELException(message);
673            }     
674          return PrimitiveObjects.getCharacter ((char) 0);
675        }
676      }
677    
678      //-------------------------------------
679      /**
680       *
681       * Coerces a value to a Boolean
682       **/
683      public static Boolean coerceToBoolean (Object pValue)
684        throws ELException
685      {
686        if (pValue == null ||
687            "".equals (pValue)) {
688          return Boolean.FALSE;
689        }
690        else if (pValue instanceof Boolean) {
691          return (Boolean) pValue;
692        }
693        else if (pValue instanceof String) {
694          String str = (String) pValue;
695          try {
696            return Boolean.valueOf (str);
697          }
698          catch (Exception exc) {
699              if (log.isErrorEnabled()) {
700                  String message = MessageUtil.getMessageWithArgs(
701                      Constants.STRING_TO_BOOLEAN, (String) pValue);
702                  log.error(message, exc);
703                  throw new ELException(message, exc);
704              }     
705            return Boolean.FALSE;
706          }
707        }
708        else {
709            if (log.isErrorEnabled()) {
710                String message = MessageUtil.getMessageWithArgs(
711                    Constants.COERCE_TO_BOOLEAN,
712                    pValue.getClass().getName());
713                log.error(message);
714                throw new ELException(message);
715            }     
716          return Boolean.TRUE;
717        }
718      }
719    
720      //-------------------------------------
721      /**
722       *
723       * Coerces a value to the specified Class that is not covered by any
724       * of the above cases
725       **/
726      public static Object coerceToObject (Object pValue, Class pClass)
727        throws ELException
728      {
729        if (pValue == null) {
730          return null;
731        }
732        else if (pClass.isAssignableFrom (pValue.getClass ())) {
733          return pValue;
734        }
735        else if (pValue instanceof String) {
736          String str = (String) pValue;
737          PropertyEditor pe = PropertyEditorManager.findEditor (pClass);
738          if (pe == null) {
739            if ("".equals (str)) {
740              return null;
741            }
742            else {
743            if (log.isErrorEnabled()) {
744                String message = MessageUtil.getMessageWithArgs(
745                    Constants.NO_PROPERTY_EDITOR,
746                    str, pClass.getName());
747                log.error(message);
748                throw new ELException(message);
749            }         
750              return null;
751            }
752          }
753          try {
754            pe.setAsText (str);
755            return pe.getValue ();
756          }
757          catch (IllegalArgumentException exc) {
758            if ("".equals (str)) {
759              return null;
760            }
761            else {
762            if (log.isErrorEnabled()) {
763                String message = MessageUtil.getMessageWithArgs(
764                    Constants.PROPERTY_EDITOR_ERROR,
765                    pValue,
766                    pClass.getName());
767                log.error(message, exc);
768                throw new ELException(message, exc);
769            }         
770              return null;
771            }
772          }
773        }
774        else {
775            if (log.isErrorEnabled()) {
776                String message = MessageUtil.getMessageWithArgs(
777                    Constants.COERCE_TO_OBJECT,
778                    pValue.getClass().getName(),
779                    pClass.getName());
780                log.error(message);
781                throw new ELException(message);
782            }     
783          return null;
784        }
785      }
786    
787      //-------------------------------------
788      // Applying operators
789      //-------------------------------------
790      /**
791       *
792       * Performs all of the necessary type conversions, then calls on the
793       * appropriate operator.
794       **/
795      public static Object applyArithmeticOperator 
796        (Object pLeft,
797         Object pRight,
798         ArithmeticOperator pOperator)
799        throws ELException
800      {
801        if (pLeft == null &&
802            pRight == null) {
803            if (log.isWarnEnabled()) {
804                log.warn(
805                    MessageUtil.getMessageWithArgs(
806                        Constants.ARITH_OP_NULL,
807                        pOperator.getOperatorSymbol()));
808            }    
809          return PrimitiveObjects.getInteger (0);
810        }
811    
812        else if (isBigDecimal(pLeft) || isBigDecimal(pRight)) {
813            BigDecimal left = (BigDecimal)
814                coerceToPrimitiveNumber(pLeft, BigDecimal.class);
815            BigDecimal right = (BigDecimal)
816                coerceToPrimitiveNumber(pRight, BigDecimal.class);
817            return pOperator.apply(left, right);
818        }
819    
820        else if (isFloatingPointType(pLeft) ||
821            isFloatingPointType(pRight) ||
822            isFloatingPointString(pLeft) ||
823            isFloatingPointString(pRight)) {
824            if (isBigInteger(pLeft) || isBigInteger(pRight)) {
825                BigDecimal left = (BigDecimal)
826                    coerceToPrimitiveNumber(pLeft, BigDecimal.class);
827                BigDecimal right = (BigDecimal)
828                    coerceToPrimitiveNumber(pRight, BigDecimal.class);
829                return pOperator.apply(left, right);
830            } else {
831                double left =
832                    coerceToPrimitiveNumber(pLeft, Double.class).
833                    doubleValue();
834                double right =
835                    coerceToPrimitiveNumber(pRight, Double.class).
836                    doubleValue();
837                return
838                    PrimitiveObjects.getDouble(pOperator.apply(left, right));
839            }
840        }
841    
842        else if (isBigInteger(pLeft) || isBigInteger(pRight)) {
843            BigInteger left = (BigInteger)
844                coerceToPrimitiveNumber(pLeft, BigInteger.class);
845            BigInteger right = (BigInteger)
846                coerceToPrimitiveNumber(pRight, BigInteger.class);
847            return pOperator.apply(left, right);
848        }
849    
850        else {
851          long left =
852            coerceToPrimitiveNumber (pLeft, Long.class).
853            longValue ();
854          long right =
855            coerceToPrimitiveNumber (pRight, Long.class).
856            longValue ();
857          return
858            PrimitiveObjects.getLong (pOperator.apply (left, right));
859        }
860      }
861    
862      //-------------------------------------
863      /**
864       *
865       * Performs all of the necessary type conversions, then calls on the
866       * appropriate operator.
867       **/
868      public static Object applyRelationalOperator 
869        (Object pLeft,
870         Object pRight,
871         RelationalOperator pOperator)
872        throws ELException
873      {
874        if (isBigDecimal(pLeft) || isBigDecimal(pRight)) {
875            BigDecimal left = (BigDecimal)
876                coerceToPrimitiveNumber(pLeft, BigDecimal.class);
877            BigDecimal right = (BigDecimal)
878                coerceToPrimitiveNumber(pRight, BigDecimal.class);
879            return PrimitiveObjects.getBoolean(pOperator.apply(left, right));
880        }
881    
882        else if (isFloatingPointType (pLeft) ||
883            isFloatingPointType (pRight)) {
884          double left =
885            coerceToPrimitiveNumber (pLeft, Double.class).
886            doubleValue ();
887          double right =
888            coerceToPrimitiveNumber (pRight, Double.class).
889            doubleValue ();
890          return 
891            PrimitiveObjects.getBoolean (pOperator.apply (left, right));
892        }
893    
894        else if (isBigInteger(pLeft) || isBigInteger(pRight)) {
895            BigInteger left = (BigInteger)
896                coerceToPrimitiveNumber(pLeft, BigInteger.class);
897            BigInteger right = (BigInteger)
898                coerceToPrimitiveNumber(pRight, BigInteger.class);
899            return PrimitiveObjects.getBoolean(pOperator.apply(left, right));
900        }
901    
902        else if (isIntegerType (pLeft) ||
903                 isIntegerType (pRight)) {
904          long left =
905            coerceToPrimitiveNumber (pLeft, Long.class).
906            longValue ();
907          long right =
908            coerceToPrimitiveNumber (pRight, Long.class).
909            longValue ();
910          return
911            PrimitiveObjects.getBoolean (pOperator.apply (left, right));
912        }
913    
914        else if (pLeft instanceof String ||
915                 pRight instanceof String) {
916          String left = coerceToString (pLeft);
917          String right = coerceToString (pRight);
918          return
919            PrimitiveObjects.getBoolean (pOperator.apply (left, right));
920        }
921    
922        else if (pLeft instanceof Comparable) {
923          try {
924            int result = ((Comparable) pLeft).compareTo (pRight);
925            return
926              PrimitiveObjects.getBoolean 
927              (pOperator.apply (result, -result));
928          }
929          catch (Exception exc) {
930              if (log.isErrorEnabled()) {
931                  String message = MessageUtil.getMessageWithArgs(
932                      Constants.COMPARABLE_ERROR,
933                      pLeft.getClass().getName(),
934                      (pRight == null) ? "null" : pRight.getClass().getName(),
935                      pOperator.getOperatorSymbol());
936                  log.error(message, exc);
937                  throw new ELException(message, exc);
938              }     
939            return Boolean.FALSE;
940          }
941        }
942    
943        else if (pRight instanceof Comparable) {
944          try {
945            int result = ((Comparable) pRight).compareTo (pLeft);
946            return
947              PrimitiveObjects.getBoolean 
948              (pOperator.apply (-result, result));
949          }
950          catch (Exception exc) {
951              if (log.isErrorEnabled()) {
952                  String message = MessageUtil.getMessageWithArgs(
953                      Constants.COMPARABLE_ERROR,
954                      pRight.getClass().getName(),
955                      (pLeft == null) ? "null" : pLeft.getClass().getName(),
956                      pOperator.getOperatorSymbol());
957                  log.error(message, exc);
958                  throw new ELException(message, exc);
959              }             
960            return Boolean.FALSE;
961          }
962        }
963    
964        else {
965            if (log.isErrorEnabled()) {
966                String message = MessageUtil.getMessageWithArgs(
967                    Constants.ARITH_OP_BAD_TYPE,
968                    pOperator.getOperatorSymbol(),
969                    pLeft.getClass().getName(),
970                    pRight.getClass().getName());
971                log.error(message);
972                throw new ELException(message);
973            }     
974          return Boolean.FALSE;
975        }
976      }
977    
978      //-------------------------------------
979      /**
980       *
981       * Performs all of the necessary type conversions, then calls on the
982       * appropriate operator.
983       **/
984      public static Object applyEqualityOperator 
985        (Object pLeft,
986         Object pRight,
987         EqualityOperator pOperator)
988        throws ELException
989      {
990        if (pLeft == pRight) {
991          return PrimitiveObjects.getBoolean (pOperator.apply (true));
992        }
993    
994        else if (pLeft == null ||
995                 pRight == null) {
996          return PrimitiveObjects.getBoolean (pOperator.apply (false));
997        }
998    
999        else if (isBigDecimal(pLeft) || isBigDecimal(pRight)) {
1000            BigDecimal left = (BigDecimal)
1001                coerceToPrimitiveNumber(pLeft, BigDecimal.class);
1002            BigDecimal right = (BigDecimal)
1003                coerceToPrimitiveNumber(pRight, BigDecimal.class);
1004            return PrimitiveObjects.getBoolean(pOperator.apply(left.equals(right)));
1005        }
1006    
1007        else if (isFloatingPointType (pLeft) ||
1008                 isFloatingPointType (pRight)) {
1009          double left =
1010            coerceToPrimitiveNumber (pLeft, Double.class).
1011            doubleValue ();
1012          double right =
1013            coerceToPrimitiveNumber (pRight, Double.class).
1014            doubleValue ();
1015          return 
1016            PrimitiveObjects.getBoolean 
1017            (pOperator.apply (left == right));
1018        }
1019    
1020        else if (isBigInteger(pLeft) || isBigInteger(pRight)) {
1021            BigInteger left = (BigInteger)
1022                coerceToPrimitiveNumber(pLeft, BigInteger.class);
1023            BigInteger right = (BigInteger)
1024                coerceToPrimitiveNumber(pRight, BigInteger.class);
1025            return PrimitiveObjects.getBoolean(pOperator.apply(left.equals(right)));
1026        }
1027    
1028        else if (isIntegerType (pLeft) ||
1029                 isIntegerType (pRight)) {
1030          long left =
1031            coerceToPrimitiveNumber (pLeft, Long.class).
1032            longValue ();
1033          long right =
1034            coerceToPrimitiveNumber (pRight, Long.class).
1035            longValue ();
1036          return
1037            PrimitiveObjects.getBoolean 
1038            (pOperator.apply (left == right));
1039        }
1040    
1041        else if (pLeft instanceof Boolean ||
1042                 pRight instanceof Boolean) {
1043          boolean left = coerceToBoolean (pLeft).booleanValue ();
1044          boolean right = coerceToBoolean (pRight).booleanValue ();
1045          return
1046            PrimitiveObjects.getBoolean 
1047            (pOperator.apply (left == right));
1048        }
1049    
1050        else if (pLeft instanceof String ||
1051                 pRight instanceof String) {
1052          String left = coerceToString (pLeft);
1053          String right = coerceToString (pRight);
1054          return
1055            PrimitiveObjects.getBoolean 
1056            (pOperator.apply (left.equals (right)));
1057        }
1058    
1059        else {
1060          try {
1061          return
1062            PrimitiveObjects.getBoolean
1063            (pOperator.apply (pLeft.equals (pRight)));
1064          }
1065          catch (Exception exc) {
1066              if (log.isErrorEnabled()) {
1067                  String message = MessageUtil.getMessageWithArgs(
1068                      Constants.ERROR_IN_EQUALS,
1069                      pLeft.getClass().getName(),
1070                      pRight.getClass().getName(),
1071                      pOperator.getOperatorSymbol());
1072                  log.error(message, exc);
1073                  throw new ELException(message, exc);
1074              }     
1075            return Boolean.FALSE;
1076          }
1077        }
1078      }
1079    
1080      //-------------------------------------
1081      /**
1082       *
1083       * Returns true if the given Object is of a floating point type
1084       **/
1085      public static boolean isFloatingPointType (Object pObject)
1086      {
1087        return 
1088          pObject != null &&
1089          isFloatingPointType (pObject.getClass ());
1090      }
1091    
1092      //-------------------------------------
1093      /**
1094       *
1095       * Returns true if the given class is of a floating point type
1096       **/
1097      public static boolean isFloatingPointType (Class pClass)
1098      {
1099        return
1100          pClass == Float.class ||
1101          pClass == Float.TYPE ||
1102          pClass == Double.class ||
1103          pClass == Double.TYPE;
1104      }
1105    
1106      //-------------------------------------
1107      /**
1108       *
1109       * Returns true if the given string might contain a floating point
1110       * number - i.e., it contains ".", "e", or "E"
1111       **/
1112      public static boolean isFloatingPointString (Object pObject)
1113      {
1114        if (pObject instanceof String) {
1115          String str = (String) pObject;
1116          int len = str.length ();
1117          for (int i = 0; i < len; i++) {
1118            char ch = str.charAt (i);
1119            if (ch == '.' ||
1120                ch == 'e' ||
1121                ch == 'E') {
1122              return true;
1123            }
1124          }
1125          return false;
1126        }
1127        else {
1128          return false;
1129        }
1130      }
1131    
1132      //-------------------------------------
1133      /**
1134       *
1135       * Returns true if the given Object is of an integer type
1136       **/
1137      public static boolean isIntegerType (Object pObject)
1138      {
1139        return 
1140          pObject != null &&
1141          isIntegerType (pObject.getClass ());
1142      }
1143    
1144      //-------------------------------------
1145      /**
1146       *
1147       * Returns true if the given class is of an integer type
1148       **/
1149      public static boolean isIntegerType (Class pClass)
1150      {
1151        return
1152          pClass == Byte.class ||
1153          pClass == Byte.TYPE ||
1154          pClass == Short.class ||
1155          pClass == Short.TYPE ||
1156          pClass == Character.class ||
1157          pClass == Character.TYPE ||
1158          pClass == Integer.class ||
1159          pClass == Integer.TYPE ||
1160          pClass == Long.class ||
1161          pClass == Long.TYPE;
1162      }
1163    
1164      //-------------------------------------
1165    
1166      /**
1167       * Returns true if the given object is BigInteger.
1168       * @param pObject - Object to evaluate
1169       * @return - true if the given object is BigInteger
1170       */
1171      public static boolean isBigInteger(Object pObject) {
1172          return
1173              pObject != null && pObject instanceof BigInteger;
1174      }
1175    
1176      /**
1177       * Returns true if the given object is BigDecimal.
1178       * @param pObject - Object to evaluate
1179       * @return - true if the given object is BigDecimal
1180       */
1181      public static boolean isBigDecimal(Object pObject) {
1182          return
1183              pObject != null && pObject instanceof BigDecimal;
1184      }
1185    }