EnumConstantValue.java
/*******************************************************************************
* Copyright (c) 2026 Carsten Hammer and others.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* https://www.eclipse.org/legal/epl-2.0/
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
package org.sandbox.jdt.internal.corext.fix.helper;
import java.util.Set;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.FieldAccess;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.IVariableBinding;
import org.eclipse.jdt.core.dom.Modifier;
import org.eclipse.jdt.core.dom.ParenthesizedExpression;
import org.eclipse.jdt.core.dom.QualifiedName;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.ThisExpression;
import org.eclipse.jdt.core.dom.Type;
/** Immutable value and declared type of a supported closed state constant. */
public record EnumConstantValue(String typeName, Object value) {
private static final Set<String> INTEGRAL_TYPES= Set.of("byte", "short", "char", "int", "long"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
private static final String STRING= "java.lang.String"; //$NON-NLS-1$
public EnumConstantValue {
if (!(INTEGRAL_TYPES.contains(typeName) && value instanceof Long
|| STRING.equals(typeName) && value instanceof String)) {
throw new IllegalArgumentException("Unsupported enum constant value"); //$NON-NLS-1$
}
}
/** Resolves a compile-time constant without narrowing a long value to int. */
public static EnumConstantValue from(IVariableBinding binding) {
if (binding == null || binding.isRecovered() || !binding.isField()
|| !Modifier.isStatic(binding.getModifiers()) || !Modifier.isFinal(binding.getModifiers())) {
return null;
}
ITypeBinding type= binding.getType();
if (!supports(type)) {
return null;
}
Object constant= binding.getConstantValue();
String name= type.getQualifiedName();
if (STRING.equals(name)) {
return constant instanceof String ? new EnumConstantValue(name, constant) : null;
}
if (constant instanceof Character character) {
return new EnumConstantValue(name, Long.valueOf(character.charValue()));
}
return constant instanceof Byte || constant instanceof Short || constant instanceof Integer || constant instanceof Long
? new EnumConstantValue(name, Long.valueOf(((Number) constant).longValue())) : null;
}
public static boolean supports(Type type) {
return type != null && supports(type.resolveBinding());
}
/** Replacing a constant must not discard an evaluated receiver or its exceptions. */
public static boolean isDirectReference(Expression expression) {
while (expression instanceof ParenthesizedExpression parenthesized) {
expression= parenthesized.getExpression();
}
return expression instanceof SimpleName
|| expression instanceof QualifiedName name && name.getQualifier().resolveBinding() instanceof ITypeBinding
|| expression instanceof FieldAccess access && access.getExpression() instanceof ThisExpression;
}
private static boolean supports(ITypeBinding type) {
return type != null && !type.isRecovered()
&& (type.isPrimitive() && INTEGRAL_TYPES.contains(type.getQualifiedName())
|| STRING.equals(type.getQualifiedName()));
}
/** A domain never mixes declared types or silently changes numeric promotion. */
public boolean matches(ITypeBinding type) {
return supports(type) && typeName.equals(type.getQualifiedName());
}
}