zel/src/main/java/org/zkoss/zel/impl/lang/ELSupport.java
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.zkoss.zel.impl.lang;
import java.beans.PropertyEditor;
import java.beans.PropertyEditorManager;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.TreeSet;
import java.util.Vector;
import javassist.util.proxy.ProxyFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.zkoss.zel.ELException;
import org.zkoss.zel.impl.util.ClassUtil;
import org.zkoss.zel.impl.util.MessageFactory;
/**
* A helper class that implements the EL Specification
*
* @author Jacob Hookom [jacob@hookom.net]
*/
public class ELSupport {
//20120418, henrichen: moved from org.zkoss.zel.impl.parser.AstValue (ZK-1062)
private static final boolean IS_SECURITY_ENABLED =
(System.getSecurityManager() != null);
private static final Logger log = LoggerFactory.getLogger(ELSupport.class);
//20120418, henrichen: ZK-1062
/**EL 2.2 by spec. should coerce "null" value to 0 number, 0 character, "" String, false boolean.
*This spec. makes it impossible to store null state into java lang object when doing setValue()
*and can set unexpected coerced value into java lang object. So we provide a system property
*"org.zkoss.zel.impl.parser.COERCE_NULL_TO_NULL" to switch this behavior when doing setValue().
*By default we will set org.zkoss.zel.impl.parser.COERCE_NULL_TO_NULL to true to allow set null value
*though this is not compliant to the EL 2.2 spec. Shall you need to make ZEL to follow EL 2.2
*spec. when doing setValue, please set "org.zkoss.zel.impl.parser.COERCE_NULL_TO_NULL" system property
*to false.
*Ref. https://services.brics.dk/java/courseadmin/SWP2011/documents/getDocument/expression_language-2_2-mrel-spec.pdf?d=41976
*/
protected static final boolean COERCE_NULL_TO_NULL;
static {
if (IS_SECURITY_ENABLED) {
COERCE_NULL_TO_NULL = AccessController.doPrivileged(
new PrivilegedAction<Boolean>(){
public Boolean run() {
return Boolean.valueOf(System.getProperty(
"org.zkoss.zel.impl.parser.COERCE_NULL_TO_NULL",
"true"));
}
}
).booleanValue();
} else {
COERCE_NULL_TO_NULL = Boolean.valueOf(System.getProperty(
"org.zkoss.zel.impl.parser.COERCE_NULL_TO_NULL",
"true")).booleanValue();
}
}
//^^
private static final Long ZERO = Long.valueOf(0L);
protected static final boolean COERCE_TO_ZERO;
static {
if (IS_SECURITY_ENABLED) {
COERCE_TO_ZERO = AccessController.doPrivileged(
new PrivilegedAction<Boolean>(){
public Boolean run() {
return Boolean.valueOf(System.getProperty(
"org.zkoss.zel.impl.parser.COERCE_TO_ZERO",
"false"));
}
}
).booleanValue();
} else {
COERCE_TO_ZERO = Boolean.valueOf(System.getProperty(
"org.zkoss.zel.impl.parser.COERCE_TO_ZERO",
"false")).booleanValue();
}
}
/**
* Compare two objects, after coercing to the same type if appropriate.
*
* If the objects are identical, or they are equal according to
* {@link #equals(Object, Object)} then return 0.
*
* If either object is a BigDecimal, then coerce both to BigDecimal first.
* Similarly for Double(Float), BigInteger, and Long(Integer, Char, Short, Byte).
*
* Otherwise, check that the first object is an instance of Comparable, and compare
* against the second object. If that is null, return 1, otherwise
* return the result of comparing against the second object.
*
* Similarly, if the second object is Comparable, if the first is null, return -1,
* else return the result of comparing against the first object.
*
* A null object is considered as:
* <ul>
* <li>ZERO when compared with Numbers</li>
* <li>the empty string for String compares</li>
* <li>Otherwise null is considered to be lower than anything else.</li>
* </ul>
*
* @param obj0 first object
* @param obj1 second object
* @return -1, 0, or 1 if this object is less than, equal to, or greater than val.
* @throws ELException if neither object is Comparable
* @throws ClassCastException if the objects are not mutually comparable
*/
public static final int compare(final Object obj0, final Object obj1)
throws ELException {
if (obj0 == obj1 || equals(obj0, obj1)) {
return 0;
}
if (isBigDecimalOp(obj0, obj1)) {
BigDecimal bd0 = (BigDecimal) coerceToNumber(obj0, BigDecimal.class);
BigDecimal bd1 = (BigDecimal) coerceToNumber(obj1, BigDecimal.class);
if (bd0 == null) {
return (bd1 == null) ? 0 : -1;
} else if (bd1 == null) {
return 1;
}
return bd0.compareTo(bd1);
}
if (isDoubleOp(obj0, obj1)) {
Double d0 = (Double) coerceToNumber(obj0, Double.class);
Double d1 = (Double) coerceToNumber(obj1, Double.class);
if (d0 == null) {
return (d1 == null) ? 0 : -1;
} else if (d1 == null) {
return 1;
}
return d0.compareTo(d1);
}
if (isBigIntegerOp(obj0, obj1)) {
BigInteger bi0 = (BigInteger) coerceToNumber(obj0, BigInteger.class);
BigInteger bi1 = (BigInteger) coerceToNumber(obj1, BigInteger.class);
if (bi0 == null) {
return (bi1 == null) ? 0 : -1;
} else if (bi1 == null) {
return 1;
}
return bi0.compareTo(bi1);
}
if (isLongOp(obj0, obj1)) {
Long l0 = (Long) coerceToNumber(obj0, Long.class);
Long l1 = (Long) coerceToNumber(obj1, Long.class);
if (l0 == null) {
return (l1 == null) ? 0 : -1;
} else if (l1 == null) {
return 1;
}
return l0.compareTo(l1);
}
if (obj0 instanceof String || obj1 instanceof String) {
return coerceToString(obj0).compareTo(coerceToString(obj1));
}
if (obj0 instanceof Comparable<?>) {
@SuppressWarnings("unchecked") // checked above
final Comparable<Object> comparable = (Comparable<Object>) obj0;
return (obj1 != null) ? comparable.compareTo(obj1) : 1;
}
if (obj1 instanceof Comparable<?>) {
@SuppressWarnings("unchecked") // checked above
final Comparable<Object> comparable = (Comparable<Object>) obj1;
if (obj0 != null) {
int comparisonResult = comparable.compareTo(obj0);
if (comparisonResult == Integer.MIN_VALUE) {
comparisonResult = Integer.MAX_VALUE;
} else {
comparisonResult = -comparisonResult; // Negate the result
}
return comparisonResult;
} else {
return -1;
}
}
throw new ELException(MessageFactory.get("error.compare", obj0, obj1));
}
/**
* Compare two objects for equality, after coercing to the same type if appropriate.
*
* If the objects are identical (including both null) return true.
* If either object is null, return false.
* If either object is Boolean, coerce both to Boolean and check equality.
* Similarly for Enum, String, BigDecimal, Double(Float), Long(Integer, Short, Byte, Character)
* Otherwise default to using Object.equals().
*
* @param obj0 the first object
* @param obj1 the second object
* @return true if the objects are equal
* @throws ELException if one of the coercion fails
*/
public static final boolean equals(final Object obj0, final Object obj1)
throws ELException {
if (obj0 == obj1) {
return true;
} else if (obj0 == null || obj1 == null) {
return false;
} else if (isBigDecimalOp(obj0, obj1)) {
BigDecimal bd0 = (BigDecimal) coerceToNumber(obj0, BigDecimal.class);
BigDecimal bd1 = (BigDecimal) coerceToNumber(obj1, BigDecimal.class);
return Objects.equals(bd0, bd1);
} else if (isDoubleOp(obj0, obj1)) {
Double d0 = (Double) coerceToNumber(obj0, Double.class);
Double d1 = (Double) coerceToNumber(obj1, Double.class);
return Objects.equals(d0, d1);
} else if (isBigIntegerOp(obj0, obj1)) {
BigInteger bi0 = (BigInteger) coerceToNumber(obj0, BigInteger.class);
BigInteger bi1 = (BigInteger) coerceToNumber(obj1, BigInteger.class);
return Objects.equals(bi0, bi1);
} else if (isLongOp(obj0, obj1)) {
Long l0 = (Long) coerceToNumber(obj0, Long.class);
Long l1 = (Long) coerceToNumber(obj1, Long.class);
return Objects.equals(l0, l1);
} else if (obj0 instanceof Boolean || obj1 instanceof Boolean) {
return Objects.equals(coerceToBoolean(obj0, false), coerceToBoolean(obj1, false));
} else if (obj0.getClass().isEnum()) {
return obj0.equals(coerceToEnum(obj1, obj0.getClass()));
} else if (obj1.getClass().isEnum()) {
return obj1.equals(coerceToEnum(obj0, obj1.getClass()));
} else if (obj0 instanceof String || obj1 instanceof String) {
int lexCompare = coerceToString(obj0).compareTo(coerceToString(obj1));
return lexCompare == 0;
} else {
return obj0.equals(obj1);
}
}
// Going to have to have some casts /raw types somewhere so doing it here
// keeps them all in one place. There might be a neater / better solution
// but I couldn't find it
@SuppressWarnings("unchecked")
public static final Enum<?> coerceToEnum(final Object obj,
@SuppressWarnings("rawtypes") Class type) {
if (obj == null || "".equals(obj)) {
return null;
}
if (type.isAssignableFrom(obj.getClass())) {
return (Enum<?>) obj;
}
if (!(obj instanceof String)) {
throw new ELException(MessageFactory.get("error.convert",
obj, obj.getClass(), type));
}
Enum<?> result;
try {
result = Enum.valueOf(type, (String) obj);
} catch (IllegalArgumentException iae) {
throw new ELException(MessageFactory.get("error.convert",
obj, obj.getClass(), type));
}
return result;
}
/**
* Convert an object to Boolean.
* Null and empty string are false.
* @param obj the object to convert
* @param primitive is the target a primitive in which case coercion to null
* is not permitted
* @return the Boolean value of the object
* @throws ELException if object is not Boolean or String
*/
public static final Boolean coerceToBoolean(final Object obj,
boolean primitive) throws ELException {
if (!COERCE_TO_ZERO && !primitive) {
if (obj == null) {
return null;
}
}
if (obj == null || "".equals(obj)) {
return Boolean.FALSE;
}
if (obj instanceof Boolean) {
return (Boolean) obj;
}
if (obj instanceof String) {
return Boolean.valueOf((String) obj);
}
throw new ELException(MessageFactory.get("error.convert",
obj, obj.getClass(), Boolean.class));
}
private static final Character coerceToCharacter(final Object obj)
throws ELException {
if (obj == null || "".equals(obj)) {
return Character.valueOf((char) 0);
}
if (obj instanceof String) {
return Character.valueOf(((String) obj).charAt(0));
}
if (ELArithmetic.isNumber(obj)) {
return Character.valueOf((char) ((Number) obj).shortValue());
}
Class<?> objType = obj.getClass();
if (obj instanceof Character) {
return (Character) obj;
}
throw new ELException(MessageFactory.get("error.convert",
obj, objType, Character.class));
}
protected static final Number coerceToNumber(final Number number,
final Class<?> type) throws ELException {
if (Long.TYPE == type || Long.class.equals(type)) {
return Long.valueOf(number.longValue());
}
if (Double.TYPE == type || Double.class.equals(type)) {
return new Double(number.doubleValue());
}
if (Integer.TYPE == type || Integer.class.equals(type)) {
return Integer.valueOf(number.intValue());
}
if (BigInteger.class.equals(type)) {
if (number instanceof BigDecimal) {
return ((BigDecimal) number).toBigInteger();
}
if (number instanceof BigInteger) {
return number;
}
return BigInteger.valueOf(number.longValue());
}
if (BigDecimal.class.equals(type)) {
if (number instanceof BigDecimal) {
return number;
}
if (number instanceof BigInteger) {
return new BigDecimal((BigInteger) number);
}
return BigDecimal.valueOf(number.doubleValue());
}
if (Byte.TYPE == type || Byte.class.equals(type)) {
return number.byteValue();
}
if (Short.TYPE == type || Short.class.equals(type)) {
return number.shortValue();
}
if (Float.TYPE == type || Float.class.equals(type)) {
return number.floatValue();
}
if (Number.class.equals(type)) {
return number;
}
throw new ELException(MessageFactory.get("error.convert",
number, number.getClass(), type));
}
public static final Number coerceToNumber(final Object obj,
final Class<?> type) throws ELException {
if (!COERCE_TO_ZERO) {
if (obj == null && !type.isPrimitive()) {
return null;
}
}
if (obj == null || "".equals(obj)) {
return coerceToNumber(ZERO, type);
}
if (obj instanceof String) {
return coerceToNumber((String) obj, type);
}
if (ELArithmetic.isNumber(obj)) {
return coerceToNumber((Number) obj, type);
}
if (obj instanceof Character) {
return coerceToNumber(Short.valueOf((short) ((Character) obj)
.charValue()), type);
}
throw new ELException(MessageFactory.get("error.convert",
obj, obj.getClass(), type));
}
protected static final Number coerceToNumber(final String val,
final Class<?> type) throws ELException {
if (Long.TYPE == type || Long.class.equals(type)) {
try {
return Long.valueOf(val);
} catch (NumberFormatException nfe) {
throw new ELException(MessageFactory.get("error.convert",
val, String.class, type));
}
}
if (Integer.TYPE == type || Integer.class.equals(type)) {
try {
return Integer.valueOf(val);
} catch (NumberFormatException nfe) {
throw new ELException(MessageFactory.get("error.convert",
val, String.class, type));
}
}
if (Double.TYPE == type || Double.class.equals(type)) {
try {
return Double.valueOf(val);
} catch (NumberFormatException nfe) {
throw new ELException(MessageFactory.get("error.convert",
val, String.class, type));
}
}
if (BigInteger.class.equals(type)) {
try {
return new BigInteger(val);
} catch (NumberFormatException nfe) {
throw new ELException(MessageFactory.get("error.convert",
val, String.class, type));
}
}
if (BigDecimal.class.equals(type)) {
try {
return new BigDecimal(val);
} catch (NumberFormatException nfe) {
throw new ELException(MessageFactory.get("error.convert",
val, String.class, type));
}
}
if (Byte.TYPE == type || Byte.class.equals(type)) {
try {
return Byte.valueOf(val);
} catch (NumberFormatException nfe) {
throw new ELException(MessageFactory.get("error.convert",
val, String.class, type));
}
}
if (Short.TYPE == type || Short.class.equals(type)) {
try {
return Short.valueOf(val);
} catch (NumberFormatException nfe) {
throw new ELException(MessageFactory.get("error.convert",
val, String.class, type));
}
}
if (Float.TYPE == type || Float.class.equals(type)) {
try {
return Float.valueOf(val);
} catch (NumberFormatException nfe) {
throw new ELException(MessageFactory.get("error.convert",
val, String.class, type));
}
}
throw new ELException(MessageFactory.get("error.convert",
val, String.class, type));
}
/**
* Coerce an object to a string.
* @param obj the object to convert
* @return the String value of the object
*/
public static final String coerceToString(final Object obj) {
if (obj == null) {
return "";
} else if (obj instanceof String) {
return (String) obj;
} else if (obj instanceof Enum<?>) {
return ((Enum<?>) obj).name();
} else {
return obj.toString();
}
}
//ZK-1062: null intbox give a 0 value to Long bean property (expect null)
protected static final Object coerceToTypeForSetValue(final Object obj,
Class<?> type) throws ELException {
if (type == null || Object.class.equals(type) ||
(obj != null && type.isAssignableFrom(obj.getClass()))) {
return obj;
}
if (String.class.equals(type)) {
return obj == null && COERCE_NULL_TO_NULL ? null : coerceToString(obj);
}
// since 8.0.0 support proxy form binding
if (ProxyFactory.isProxyClass(type)) {
type = type.getSuperclass();
if (obj != null && type.isAssignableFrom(obj.getClass()))
return obj;
}
if (ELArithmetic.isNumberType(type)) {
return (obj == null || "".equals(obj)) && !type.isPrimitive() && COERCE_NULL_TO_NULL ? null : coerceToNumber(obj, type);
}
if (Character.class.equals(type) || Character.TYPE == type) {
return (obj == null || "".equals(obj)) && !type.isPrimitive() && COERCE_NULL_TO_NULL ? null : coerceToCharacter(obj);
}
if (Boolean.class.equals(type) || Boolean.TYPE == type) {
return (obj == null || "".equals(obj)) && !type.isPrimitive() && COERCE_NULL_TO_NULL ? null : coerceToBoolean(obj, true);
}
if (type.isEnum()) {
return coerceToEnum(obj, type);
}
//ZK-919: Cannot handle java.sql.Timestamp
if (isDateTimeType(type)) {
return coerceToDateTime(obj, type);
}
//ZK-1595: Cannot coerce Set to List
//Dennis, do the last effort to convert to List or Set
//sometimes, if user's bean doesn't allow the List/Set we converted, it will still get error
//but, if we don't do this, user will still get error from last line of this method.
if (isCollectionType(type)) {
return coerceToCollection(obj, type);
}
// new to spec
if (obj == null)
return null;
if (obj instanceof String) {
if ("".equals(obj))
return null;
PropertyEditor editor = PropertyEditorManager.findEditor(type);
if (editor != null) {
editor.setAsText((String) obj);
return editor.getValue();
}
}
throw new ELException(MessageFactory.get("error.convert",
obj, obj.getClass(), type));
}
public static final Object coerceToType(final Object obj,
Class<?> type) throws ELException {
if (type == null || Object.class.equals(type) ||
(obj != null && type.isAssignableFrom(obj.getClass()))) {
return obj;
}
if (!COERCE_TO_ZERO) {
if (obj == null && !type.isPrimitive() &&
!String.class.isAssignableFrom(type)) {
return null;
}
}
if (String.class.equals(type)) {
return coerceToString(obj);
}
// since 8.0.0 support proxy form binding
if (ProxyFactory.isProxyClass(type)) {
type = type.getSuperclass();
if (obj != null && type.isAssignableFrom(obj.getClass()))
return obj;
}
if (ELArithmetic.isNumberType(type)) {
return coerceToNumber(obj, type);
}
if (Character.class.equals(type) || Character.TYPE == type) {
return coerceToCharacter(obj);
}
if (Boolean.class.equals(type) || Boolean.TYPE == type) {
return coerceToBoolean(obj, Boolean.TYPE == type);
}
if (type.isEnum()) {
return coerceToEnum(obj, type);
}
//ZK-919: Cannot handle java.sql.Timestamp
if (isDateTimeType(type)) {
return coerceToDateTime(obj, type);
}
//ZK-1595: Cannot coerce Set to List
if (isCollectionType(type)) {
return coerceToCollection(obj, type);
}
// new to spec
if (obj == null)
return null;
if (obj instanceof String) {
PropertyEditor editor = PropertyEditorManager.findEditor(type);
if (editor == null) {
if ("".equals(obj)) {
return null;
}
throw new ELException(MessageFactory.get("error.convert", obj,
obj.getClass(), type));
} else {
try {
editor.setAsText((String) obj);
return editor.getValue();
} catch (RuntimeException e) {
if ("".equals(obj)) {
return null;
}
throw new ELException(MessageFactory.get("error.convert",
obj, obj.getClass(), type), e);
}
}
}
// Handle special case because the syntax for the empty set is the same
// for an empty map. The parser will always parse {} as an empty set.
if (obj instanceof Set && type == Map.class &&
((Set<?>) obj).isEmpty()) {
return Collections.EMPTY_MAP;
}
// Handle arrays
if (type.isArray() && obj.getClass().isArray()) {
return coerceToArray(obj, type);
}
throw new ELException(MessageFactory.get("error.convert",
obj, obj.getClass(), type));
}
private static Object coerceToArray(final Object obj,
final Class<?> type) {
// Note: Nested arrays will result in nested calls to this method.
// Note: Calling method has checked the obj is an array.
int size = Array.getLength(obj);
// Cast the input object to an array (calling method has checked it is
// an array)
// Get the target type for the array elements
Class<?> componentType = type.getComponentType();
// Create a new array of the correct type
Object result = Array.newInstance(componentType, size);
// Coerce each element in turn.
for (int i = 0; i < size; i++) {
Array.set(result, i, coerceToType(Array.get(obj, i), componentType));
}
return result;
}
private static boolean isCollectionType(Class<?> type) {
//support set and list only
return Collection.class.isAssignableFrom(type);
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private static Object coerceToCollection(Object obj, Class<?> type) {
if(obj==null) return null;
// since 8.0.0 support proxy form binding
if (ProxyFactory.isProxyClass(type)) {
type = type.getSuperclass();
if (obj != null && type.isAssignableFrom(obj.getClass()))
return obj;
}
if(obj instanceof Collection){
//try the construct first
try {
return ClassUtil.newInstance(type, new Object[]{obj});
} catch (Throwable e) {
//B70-ZK-2493 only throw those undefined exceptions
if (!(e instanceof NoSuchMethodException) && !(e instanceof InstantiationException) &&
!(e instanceof InvocationTargetException) && !(e instanceof IllegalAccessException)) {
log.warn("ClassUtil.newInstance has unexpected error: ", e);
throw new ELException("ClassUtil.newInstance has unexpected error: ", e);
}
}
//try the common java.lang collections
if(Set.class.isAssignableFrom(type)){
if(Set.class.equals(type) || LinkedHashSet.class.isAssignableFrom(type)){
return new LinkedHashSet((Collection)obj);
}else if(HashSet.class.isAssignableFrom(type)){
return new HashSet((Collection)obj);
}else if(TreeSet.class.isAssignableFrom(type)){
return new TreeSet((Collection)obj);
}
//give it a default one
return new LinkedHashSet((Collection)obj);
}else if(List.class.isAssignableFrom(type)){
if(List.class.equals(type) || LinkedList.class.isAssignableFrom(type)){
return new LinkedList((Collection)obj);
}else if(ArrayList.class.isAssignableFrom(type)){
return new ArrayList((Collection)obj);
}else if(Vector.class.isAssignableFrom(type)){
return new Vector((Collection)obj);
}
//give it a default
return new LinkedList((Collection)obj);
}
}
throw new ELException(MessageFactory.get("error.convert",
obj, obj.getClass(), type));
}
public static final boolean isBigDecimalOp(final Object obj0,
final Object obj1) {
return (obj0 instanceof BigDecimal || obj1 instanceof BigDecimal);
}
public static final boolean isBigIntegerOp(final Object obj0,
final Object obj1) {
return (obj0 instanceof BigInteger || obj1 instanceof BigInteger);
}
public static final boolean isDoubleOp(final Object obj0, final Object obj1) {
return (obj0 instanceof Double
|| obj1 instanceof Double
|| obj0 instanceof Float
|| obj1 instanceof Float);
}
public static final boolean isLongOp(final Object obj0, final Object obj1) {
return (obj0 instanceof Long
|| obj1 instanceof Long
|| obj0 instanceof Integer
|| obj1 instanceof Integer
|| obj0 instanceof Character
|| obj1 instanceof Character
|| obj0 instanceof Short
|| obj1 instanceof Short
|| obj0 instanceof Byte
|| obj1 instanceof Byte);
}
public static final boolean isStringFloat(final String str) {
int len = str.length();
if (len > 1) {
for (int i = 0; i < len; i++) {
switch (str.charAt(i)) {
case 'E':
return true;
case 'e':
return true;
case '.':
return true;
}
}
}
return false;
}
/**
*
*/
public ELSupport() {
super();
}
//20120314, henrichen: ZK-919
public static final boolean isDateTimeType(final Class<?> type) {
return java.util.Date.class.isAssignableFrom(type);
}
/**
* Coerce an object to an instance of java.util.Date
* @param obj the object to be converted; must be an instance of java.util.Date or
* a number which represents the millisecond since "the epoch" base time, namely January 1, 1970, 00:00:00 GMT.
* @param type the class type which shall be a kind of java.util.Date; e.g. java.sql.Date, java.sql.Time, java.sql.Timestamp
* @return the instance as requested by the specified type
*/
@SuppressWarnings("unchecked")
public static final java.util.Date coerceToDateTime(final Object obj,
@SuppressWarnings("rawtypes") Class type) throws ELException {
if (obj == null || "".equals(obj)) {
return null;
}
// since 8.0.0 support proxy form binding
if (ProxyFactory.isProxyClass(type)) {
type = type.getSuperclass();
}
if (type.isAssignableFrom(obj.getClass())) {
return (java.util.Date) obj;
}
//convert among java.util.Date, java.sql.Date, java.sql.Time, java.sql.Timestamp
try {
if (obj instanceof java.util.Date) {
return newDateInstance(((java.util.Date) obj).getTime(), type);
} else if (obj instanceof Number) {
return newDateInstance(((Number) obj).longValue(), type);
}
} catch (Exception e) {
throw new ELException(MessageFactory.get("error.convert",
obj, obj.getClass(), type), e);
}
throw new ELException(MessageFactory.get("error.convert",
obj, obj.getClass(), type));
}
@SuppressWarnings("rawtypes")
private static java.util.Date newDateInstance(long time, Class type) throws Exception {
@SuppressWarnings("unchecked")
final Constructor ctr = type.getConstructor(new Class[] {long.class});
return (java.util.Date) ctr.newInstance(time);
}
}