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 */
017package org.apache.commons.el;
018
019import java.beans.PropertyEditor;
020import java.beans.PropertyEditorManager;
021import java.math.BigDecimal;
022import java.math.BigInteger;
023
024import javax.servlet.jsp.el.ELException;
025
026import org.apache.commons.logging.Log;
027import 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
280public 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}