EvaluatedExpressionCompilationUtils.java
/*
* Copyright 2017-2022 original authors
*
* Licensed 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
*
* https://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 io.micronaut.expressions.parser.ast.util;
import io.micronaut.core.annotation.Internal;
import io.micronaut.core.annotation.NonNull;
import io.micronaut.expressions.parser.exception.ExpressionCompilationException;
import io.micronaut.inject.ast.ClassElement;
import io.micronaut.inject.ast.PrimitiveElement;
import io.micronaut.inject.visitor.VisitorContext;
import io.micronaut.sourcegen.model.ClassTypeDef;
import io.micronaut.sourcegen.model.TypeDef;
import static io.micronaut.expressions.parser.ast.util.TypeDescriptors.toBoxedIfNecessary;
import static io.micronaut.expressions.parser.ast.util.TypeDescriptors.toUnboxedIfNecessary;
/**
* Utility methods for used when compiling evaluated expressions.
*
* @author Sergey Gavrilov
* @since 4.0.0
*/
@Internal
public final class EvaluatedExpressionCompilationUtils {
/**
* Checks whether the argument class element is assignable to the parameter
* class element. This method also accepts primitive and wrapper elements and
* determines whether argument can be assigned to parameter after boxing or unboxing.
* In case when parameter or argument is an array, array dimensions are also checked.
*
* @param parameter checked parameter
* @param argument checked argument
* @return whether argument is assignable to parameter
*/
public static boolean isAssignable(@NonNull ClassElement parameter,
@NonNull ClassElement argument) {
if (!argument.isAssignable(parameter)) {
TypeDef parameterType = TypeDef.erasure(parameter);
TypeDef argumentType = TypeDef.erasure(argument);
return toUnboxedIfNecessary(parameterType).equals(toUnboxedIfNecessary(argumentType))
|| toBoxedIfNecessary(parameterType).equals(toBoxedIfNecessary(argumentType));
}
if (parameter.getArrayDimensions() > 0 || argument.getArrayDimensions() > 0) {
return parameter.getArrayDimensions() == argument.getArrayDimensions();
}
return true;
}
/**
* Provides {@link ClassElement} for passed type or throws exception
* if class element can not be provided.
*
* @param type Type element for which {@link ClassElement} needs to be obtained.
* This type can also represent a primitive type. In this case it will be
* boxed
* @param visitorContext visitor context
* @return resolved class element
* @throws ExpressionCompilationException if class element can not be obtained
*/
@NonNull
public static ClassElement getRequiredClassElement(TypeDef type,
VisitorContext visitorContext) {
if (type instanceof TypeDef.Array array) {
TypeDef elementType = array.componentType();
ClassElement classElement = getRequiredClassElement(elementType, visitorContext);
for (int i = 0; i < array.dimensions(); i++) {
classElement = classElement.toArray();
}
return classElement;
}
if (type instanceof TypeDef.Primitive primitive) {
return PrimitiveElement.valueOf(primitive.name());
}
if (type instanceof ClassTypeDef classType) {
return getClassElementForName(visitorContext, classType.getName());
}
throw new IllegalStateException("Unknown type " + type);
}
private static ClassElement getClassElementForName(VisitorContext visitorContext, String className) {
return visitorContext.getClassElement(className)
.orElseThrow(() -> new ExpressionCompilationException(
"Can not resolve type information for [" + className + "]"));
}
}