Impressum ELSupport.java
Interaktion und PortierbarkeitJAVA
/* * 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.apache.el.lang;
/** * Compare two objects, after coercing to the same type if appropriate. * * If the objects are identical, or they are equal according to * {@link #equals(ELContext, 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 ctx the context in which this comparison is taking place * @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
*/ publicstaticfinalint compare(final ELContext ctx, final Object obj0, final Object obj1) throws ELException { if (obj0 == obj1 || equals(ctx, obj0, obj1)) { return 0;
} if (isBigDecimalOp(obj0, obj1)) {
BigDecimal bd0 = (BigDecimal) coerceToNumber(ctx, obj0, BigDecimal.class);
BigDecimal bd1 = (BigDecimal) coerceToNumber(ctx, obj1, BigDecimal.class); return bd0.compareTo(bd1);
} if (isDoubleOp(obj0, obj1)) { Double d0 = (Double) coerceToNumber(ctx, obj0, Double.class); Double d1 = (Double) coerceToNumber(ctx, obj1, Double.class); return d0.compareTo(d1);
} if (isBigIntegerOp(obj0, obj1)) {
BigInteger bi0 = (BigInteger) coerceToNumber(ctx, obj0, BigInteger.class);
BigInteger bi1 = (BigInteger) coerceToNumber(ctx, obj1, BigInteger.class); return bi0.compareTo(bi1);
} if (isLongOp(obj0, obj1)) { Long l0 = (Long) coerceToNumber(ctx, obj0, Long.class); Long l1 = (Long) coerceToNumber(ctx, obj1, Long.class); return l0.compareTo(l1);
} if (obj0 instanceof String || obj1 instanceof String) { return coerceToString(ctx, obj0).compareTo(coerceToString(ctx, 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; return (obj0 != null) ? -comparable.compareTo(obj0) : -1;
} thrownew 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 ctx the context in which this equality test is taking place * @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
*/ publicstaticfinalboolean equals(final ELContext ctx, final Object obj0, final Object obj1) throws ELException { if (obj0 == obj1) { returntrue;
} elseif (obj0 == null || obj1 == null) { returnfalse;
} elseif (isBigDecimalOp(obj0, obj1)) {
BigDecimal bd0 = (BigDecimal) coerceToNumber(ctx, obj0, BigDecimal.class);
BigDecimal bd1 = (BigDecimal) coerceToNumber(ctx, obj1, BigDecimal.class); return bd0.equals(bd1);
} elseif (isDoubleOp(obj0, obj1)) { Double d0 = (Double) coerceToNumber(ctx, obj0, Double.class); Double d1 = (Double) coerceToNumber(ctx, obj1, Double.class); return d0.equals(d1);
} elseif (isBigIntegerOp(obj0, obj1)) {
BigInteger bi0 = (BigInteger) coerceToNumber(ctx, obj0, BigInteger.class);
BigInteger bi1 = (BigInteger) coerceToNumber(ctx, obj1, BigInteger.class); return bi0.equals(bi1);
} elseif (isLongOp(obj0, obj1)) { Long l0 = (Long) coerceToNumber(ctx, obj0, Long.class); Long l1 = (Long) coerceToNumber(ctx, obj1, Long.class); return l0.equals(l1);
} elseif (obj0 instanceofBoolean || obj1 instanceofBoolean) { return coerceToBoolean(ctx, obj0, false).equals(coerceToBoolean(ctx, obj1, false));
} elseif (obj0.getClass().isEnum()) { return obj0.equals(coerceToEnum(ctx, obj1, obj0.getClass()));
} elseif (obj1.getClass().isEnum()) { return obj1.equals(coerceToEnum(ctx, obj0, obj1.getClass()));
} elseif (obj0 instanceof String || obj1 instanceof String) { int lexCompare = coerceToString(ctx, obj0).compareTo(coerceToString(ctx, obj1)); return (lexCompare == 0) ? true : false;
} 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") publicstaticfinalEnum<?> coerceToEnum(final ELContext ctx, final Object obj,
@SuppressWarnings("rawtypes") Class type) {
if (ctx != null) { boolean originalIsPropertyResolved = ctx.isPropertyResolved(); try {
Object result = ctx.getELResolver().convertToType(ctx, obj, type); if (ctx.isPropertyResolved()) { return (Enum<?>) result;
}
} finally {
ctx.setPropertyResolved(originalIsPropertyResolved);
}
}
if (obj == null || "".equals(obj)) { returnnull;
} if (type.isAssignableFrom(obj.getClass())) { return (Enum<?>) obj;
}
/** * Convert an object to Boolean. * Null and empty string are false. * @param ctx the context in which this conversion is taking place * @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
*/ publicstaticfinalBoolean coerceToBoolean(final ELContext ctx, final Object obj, boolean primitive) throws ELException {
if (ctx != null) { boolean originalIsPropertyResolved = ctx.isPropertyResolved(); try {
Object result = ctx.getELResolver().convertToType(ctx, obj, Boolean.class); if (ctx.isPropertyResolved()) { return (Boolean) result;
}
} finally {
ctx.setPropertyResolved(originalIsPropertyResolved);
}
}
if (!COERCE_TO_ZERO && !primitive) { if (obj == null) { returnnull;
}
}
if (obj == null || "".equals(obj)) { returnBoolean.FALSE;
} if (obj instanceofBoolean) { return (Boolean) obj;
} if (obj instanceof String) { returnBoolean.valueOf((String) obj);
}
/** * Coerce an object to a string. * @param ctx the context in which this conversion is taking place * @param obj the object to convert * @return the String value of the object
*/ publicstaticfinal String coerceToString(final ELContext ctx, final Object obj) {
if (ctx != null) { boolean originalIsPropertyResolved = ctx.isPropertyResolved(); try {
Object result = ctx.getELResolver().convertToType(ctx, obj, String.class); if (ctx.isPropertyResolved()) { return (String) result;
}
} finally {
ctx.setPropertyResolved(originalIsPropertyResolved);
}
}
publicstaticfinal <T> T coerceToType(final ELContext ctx, final Object obj, finalClass<T> type) throws ELException {
if (ctx != null) { boolean originalIsPropertyResolved = ctx.isPropertyResolved(); try {
T result = ctx.getELResolver().convertToType(ctx, obj, type); if (ctx.isPropertyResolved()) { return result;
}
} finally {
ctx.setPropertyResolved(originalIsPropertyResolved);
}
}
if (type == null || Object.class.equals(type) ||
(obj != null && type.isAssignableFrom(obj.getClass()))) {
@SuppressWarnings("unchecked")
T result = (T) obj; return result;
}
if (!COERCE_TO_ZERO) { if (obj == null && !type.isPrimitive() &&
!String.class.isAssignableFrom(type)) { returnnull;
}
}
if (String.class.equals(type)) {
@SuppressWarnings("unchecked")
T result = (T) coerceToString(ctx, obj); return result;
} if (ELArithmetic.isNumberType(type)) {
@SuppressWarnings("unchecked")
T result = (T) coerceToNumber(ctx, obj, type); return result;
} if (Character.class.equals(type) || Character.TYPE == type) {
@SuppressWarnings("unchecked")
T result = (T) coerceToCharacter(ctx, obj); return result;
} if (Boolean.class.equals(type) || Boolean.TYPE == type) {
@SuppressWarnings("unchecked")
T result = (T) coerceToBoolean(ctx, obj, Boolean.TYPE == type); return result;
} if (type.isEnum()) {
@SuppressWarnings("unchecked")
T result = (T) coerceToEnum(ctx, obj, type); return result;
}
// new to spec if (obj == null) { returnnull;
} if (obj instanceof String) {
String str = (String) obj;
PropertyEditor editor = PropertyEditorManager.findEditor(type); if (editor == null) { if (str.isEmpty()) { returnnull;
} thrownew ELException(MessageFactory.get("error.convert", obj,
obj.getClass(), type));
} else { try {
editor.setAsText(str);
@SuppressWarnings("unchecked")
T result = (T) editor.getValue(); return result;
} catch (RuntimeException e) { if (str.isEmpty()) { returnnull;
} thrownew 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()) {
@SuppressWarnings("unchecked")
T result = (T) Collections.EMPTY_MAP; return result;
}
// Handle arrays if (type.isArray() && obj.getClass().isArray()) {
@SuppressWarnings("unchecked")
T result = (T) coerceToArray(ctx, obj, type); return result;
}
if (obj instanceof LambdaExpression && isFunctionalInterface(type)) {
T result = coerceToFunctionalInterface(ctx, (LambdaExpression) obj, type); return result;
}
privatestatic Object coerceToArray(final ELContext ctx, final Object obj, finalClass<?> 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(ctx, Array.get(obj, i), componentType));
}
return result;
}
privatestatic <T> T coerceToFunctionalInterface(final ELContext ctx, final LambdaExpression lambdaExpression, finalClass<T> type) {
Supplier<T> proxy = () -> { // Create a dynamic proxy for the functional interface
@SuppressWarnings("unchecked")
T result = (T) Proxy.newProxyInstance(type.getClassLoader(), newClass[]{type},
(Object obj, Method method, Object[] args) -> { // Functional interfaces have a single, abstract method if (!Modifier.isAbstract(method.getModifiers())) { thrownew ELException(MessageFactory.get("elSupport.coerce.nonAbstract", type, method));
} if (ctx == null) { return lambdaExpression.invoke(args);
} else { return lambdaExpression.invoke(ctx, args);
}
}); return result;
}; if (System.getSecurityManager() != null) { return AccessController.doPrivileged((PrivilegedAction<T>) proxy::get);
} else { return proxy.get();
}
}
publicstaticfinalboolean isStringFloat(final String str) { int len = str.length(); if (len > 1) { for (int i = 0; i < len; i++) { switch (str.charAt(i)) { case'E': returntrue; case'e': returntrue; case'.': returntrue;
}
}
} returnfalse;
}
/* * Copied to jakarta.el.ELContext - keep in sync
*/ staticboolean isFunctionalInterface(Class<?> type) {
if (!type.isInterface()) { returnfalse;
}
boolean foundAbstractMethod = false;
Method[] methods = type.getMethods(); for (Method method : methods) { if (Modifier.isAbstract(method.getModifiers())) { // Abstract methods that override one of the public methods // of Object don't count if (overridesObjectMethod(method)) { continue;
} if (foundAbstractMethod) { // Found more than one returnfalse;
} else {
foundAbstractMethod = true;
}
}
} return foundAbstractMethod;
}
/* * Copied to jakarta.el.ELContext - keep in sync
*/ privatestaticboolean overridesObjectMethod(Method method) { // There are three methods that can be overridden if ("equals".equals(method.getName())) { if (method.getReturnType().equals(boolean.class)) { if (method.getParameterCount() == 1) { if (method.getParameterTypes()[0].equals(Object.class)) { returntrue;
}
}
}
} elseif ("hashCode".equals(method.getName())) { if (method.getReturnType().equals(int.class)) { if (method.getParameterCount() == 0) { returntrue;
}
}
} elseif ("toString".equals(method.getName())) { if (method.getReturnType().equals(String.class)) { if (method.getParameterCount() == 0) { returntrue;
}
}
}
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.46Bemerkung:
Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.