NumericConditionalExprHandler.java
/*
* Copyright (C) 2013-2024 The JavaParser Team.
*
* This file is part of JavaParser.
*
* JavaParser can be used either under the terms of
* a) the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* b) the terms of the Apache License
*
* You should have received a copy of both licenses in LICENCE.LGPL and
* LICENCE.APACHE. Please refer to those files for details.
*
* JavaParser is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*/
package com.github.javaparser.symbolsolver.resolution.promotion;
import com.github.javaparser.resolution.promotion.ConditionalExprHandler;
import com.github.javaparser.resolution.types.ResolvedPrimitiveType;
import com.github.javaparser.resolution.types.ResolvedType;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/*
* Numeric conditional expressions are standalone expressions (��15.2).
* The type of a numeric conditional expression is determined as follows:
* If the second and third operands have the same type, then that is the type of the conditional expression.
* If one of the second and third operands is of primitive type T, and the type of the other is the result of applying boxing conversion (��5.1.7) to T, then the type of the conditional expression is T.
* If one of the operands is of type byte or Byte and the other is of type short or Short, then the type of the conditional expression is short.
* If one of the operands is of type T where T is byte, short, or char, and the other operand is a constant expression (��15.28) of type int whose value is representable in type T, then the type of the conditional expression is T.
* If one of the operands is of type T, where T is Byte, Short, or Character, and the other operand is a constant expression of type int whose value is representable in the type U which is the result of applying unboxing conversion to T, then the type of the conditional expression is U.
* Otherwise, binary numeric promotion (��5.6.2) is applied to the operand types, and the type of the conditional expression is the promoted type of the second and third operands.
*/
public class NumericConditionalExprHandler implements ConditionalExprHandler {
private static Map<ResolvedType, List<ResolvedType>> NumericConverter = new HashMap();
static {
// first type is the type of the resolution
// the list indicates the types that can be resolved in the type specified as
// key
NumericConverter.put(
ResolvedPrimitiveType.SHORT, Arrays.asList(ResolvedPrimitiveType.SHORT, ResolvedPrimitiveType.BYTE));
}
private static ResolvedPrimitiveType[] resolvedPrimitiveTypeSubList = new ResolvedPrimitiveType[] {
ResolvedPrimitiveType.BYTE, ResolvedPrimitiveType.SHORT, ResolvedPrimitiveType.CHAR
};
ResolvedType thenExpr;
ResolvedType elseExpr;
public NumericConditionalExprHandler(ResolvedType thenExpr, ResolvedType elseExpr) {
this.thenExpr = thenExpr;
this.elseExpr = elseExpr;
}
@Override
public ResolvedType resolveType() {
String qnameTypeThenExpr = thenExpr.isPrimitive()
? thenExpr.asPrimitive().describe()
: thenExpr.asReferenceType().describe();
String qnameTypeElseExpr = elseExpr.isPrimitive()
? elseExpr.asPrimitive().describe()
: elseExpr.asReferenceType().describe();
if (qnameTypeThenExpr.equals(qnameTypeElseExpr)) {
return thenExpr;
}
/*
* If one of the second and third operands is of primitive type T, and the type of the other is the result of
* applying boxing conversion (��5.1.7) to T, then the type of the conditional expression is T.
*/
if (thenExpr.isPrimitive()
&& elseExpr.isReferenceType()
&& elseExpr.asReferenceType()
.isUnboxableTo((ResolvedPrimitiveType) ResolvedPrimitiveType.byName(qnameTypeThenExpr))) {
return thenExpr;
}
if (elseExpr.isPrimitive()
&& thenExpr.isReferenceType()
&& thenExpr.asReferenceType()
.isUnboxableTo((ResolvedPrimitiveType) ResolvedPrimitiveType.byName(qnameTypeElseExpr))) {
return elseExpr;
}
/*
* If one of the operands is of type byte or Byte and the other is of type short or Short, then the type of the
* conditional expression is short.
*/
if ((isResolvableTo(ResolvedPrimitiveType.SHORT, thenExpr) && relaxMatch(elseExpr, ResolvedPrimitiveType.BYTE))
|| (isResolvableTo(ResolvedPrimitiveType.SHORT, elseExpr)
&& relaxMatch(thenExpr, ResolvedPrimitiveType.BYTE))) {
return ResolvedPrimitiveType.SHORT;
}
// if ((in(thenExpr, ResolvedPrimitiveType.SHORT) && in(elseExpr, ResolvedPrimitiveType.BYTE))
// || (in(elseExpr, ResolvedPrimitiveType.SHORT) && in(thenExpr, ResolvedPrimitiveType.BYTE))) {
// return ResolvedPrimitiveType.SHORT;
// }
// If one of the operands is of type T where T is byte, short, or char, and the
// other operand is a constant expression (��15.28) of type int whose value is
// representable in type T, then the type of the conditional expression is T
// How can we know if the constant expression of type int is representable in
// type T ?
if (thenExpr.isPrimitive() && elseExpr.isPrimitive()) {
if (((ResolvedPrimitiveType) thenExpr).in(resolvedPrimitiveTypeSubList)
&& ((ResolvedPrimitiveType) elseExpr).equals(ResolvedPrimitiveType.INT)) {
return thenExpr;
}
if (((ResolvedPrimitiveType) elseExpr).in(resolvedPrimitiveTypeSubList)
&& ((ResolvedPrimitiveType) thenExpr).equals(ResolvedPrimitiveType.INT)) {
return elseExpr;
}
}
// If one of the operands is of type T, where T is Byte, Short, or Character,
// and the other operand is a constant expression of type int whose value is
// representable in the type U which is the result of applying unboxing
// conversion to T, then the type of the conditional expression is U.
// How can we know it?
if (thenExpr.isReference()
&& elseExpr.isPrimitive()
&& thenExpr.asReferenceType().isUnboxable()
&& thenExpr.asReferenceType().toUnboxedType().get().in(resolvedPrimitiveTypeSubList)
&& ((ResolvedPrimitiveType) elseExpr).equals(ResolvedPrimitiveType.INT)) {
return thenExpr.asReferenceType().toUnboxedType().get();
}
if (elseExpr.isReference()
&& thenExpr.isPrimitive()
&& elseExpr.asReferenceType().isUnboxable()
&& elseExpr.asReferenceType().toUnboxedType().get().in(resolvedPrimitiveTypeSubList)
&& ((ResolvedPrimitiveType) thenExpr).equals(ResolvedPrimitiveType.INT)) {
return elseExpr.asReferenceType().toUnboxedType().get();
}
// Otherwise, binary numeric promotion (��5.6.2) is applied to the operand types,
// and the type of the conditional expression is the promoted type of the second
// and third operands.
ResolvedPrimitiveType PrimitiveThenExpr = thenExpr.isPrimitive()
? thenExpr.asPrimitive()
: thenExpr.asReferenceType().toUnboxedType().get();
ResolvedPrimitiveType PrimitiveElseExpr = elseExpr.isPrimitive()
? elseExpr.asPrimitive()
: elseExpr.asReferenceType().toUnboxedType().get();
return PrimitiveThenExpr.bnp(PrimitiveElseExpr);
}
/*
* Verify if the {code ResolvedType} is equals to one of the specified primitive types
*/
protected boolean exactMatch(ResolvedType type, ResolvedPrimitiveType... types) {
return type.isPrimitive() && type.asPrimitive().in(types);
}
protected boolean relaxMatch(ResolvedType type, ResolvedPrimitiveType... types) {
return exactMatch(type, types)
|| (type.isReferenceType()
&& Arrays.stream(types).anyMatch(t -> type.asReferenceType()
.getQualifiedName()
.equals(t.getBoxTypeQName())));
}
/*
* Verify if the {code ResolvedType} can be resolve to the specified primitive type
*/
protected boolean isResolvableTo(ResolvedPrimitiveType toType, ResolvedType resolvedType) {
// force to verify boxed type
return isResolvableTo(toType, resolvedType, true);
}
protected boolean isResolvableTo(ResolvedPrimitiveType toType, ResolvedType resolvedType, boolean verifyBoxedType) {
return NumericConverter.entrySet().stream()
.filter(e -> e.getKey() == toType)
.flatMap(entry -> entry.getValue().stream())
.anyMatch(rt -> rt == resolvedType
|| (verifyBoxedType
&& resolvedType.isReferenceType()
&& resolvedType
.asReferenceType()
.toUnboxedType()
.get()
== toType));
}
}