JavaAnnoType.java
/*
* SPDX-FileCopyrightText: 2025 kaumei.io
* SPDX-License-Identifier: Apache-2.0
*/
package io.kaumei.jdbc.anno.model;
import io.kaumei.jdbc.anno.ctx.AnnoElementValueMap;
import io.kaumei.jdbc.anno.ctx.Context;
import io.kaumei.jdbc.anno.utils.SqlDV;
import io.kaumei.jdbc.annotation.JdbcUpdate;
import io.kaumei.jdbc.annotation.config.JdbcConfig;
import io.kaumei.jdbc.annotation.config.JdbcLogLevel;
import org.jspecify.annotations.Nullable;
import javax.lang.model.AnnotatedConstruct;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import java.lang.annotation.Annotation;
import java.util.function.Function;
import static java.util.Objects.requireNonNull;
public final class JavaAnnoType<T> {
private final Context ctx;
private final TypeElement typeElement;
private final Function<AnnoElementValueMap, T> func;
private final Class<T> annoValueCls;
public JavaAnnoType(Context ctx,
Class<? extends Annotation> annoCls,
Class<T> annoValueCls,
Function<AnnoElementValueMap, T> func
) {
this.ctx = ctx;
this.typeElement = ctx.elements.getTypeElement(annoCls.getCanonicalName());
this.annoValueCls = annoValueCls;
this.func = func;
}
// ------------------------------------------------------------------------
@Override
public String toString() {
return typeElement.getQualifiedName().toString();
}
public TypeElement typeElement() {
return this.typeElement;
}
// ------------------------------------------------------------------------
public boolean hasAnno(AnnotatedConstruct element) {
for (AnnotationMirror mirror : element.getAnnotationMirrors()) {
if (isSameType(mirror)) {
return true;
}
}
return false;
}
public T cast(Object value) {
return this.annoValueCls.cast(value);
}
// ------------------------------------------------------------------------
public T getAnno(AnnotationMirror mirror) {
return requireNonNull(this.func.apply(new AnnoElementValueMap(mirror)));
}
public T getAnno(AnnotatedConstruct element) {
var mirror = requireNonNull(findOne(element));
return requireNonNull(this.func.apply(new AnnoElementValueMap(mirror)));
}
public T getAnno(AnnotatedConstruct element, T defaultValue) {
var mirror = findOne(element);
return mirror == null ? defaultValue : requireNonNull(this.func.apply(new AnnoElementValueMap(mirror)));
}
/**
* @return null if element could not be found else the value
*/
public @Nullable T getAnnoOpt(AnnotatedConstruct element) {
var mirror = findOne(element);
return mirror == null ? null : getAnno(mirror);
}
// ------------------------------------------------------------------------
private boolean isSameType(AnnotationMirror mirror) {
return ctx.isSameType(mirror.getAnnotationType(), typeElement.asType());
}
private @Nullable AnnotationMirror findOne(AnnotatedConstruct element) {
AnnotationMirror found = null;
for (AnnotationMirror mirror : element.getAnnotationMirrors()) {
if (isSameType(mirror)) {
if (found == null) {
found = mirror;
} else {
throw new IllegalStateException("Found too many annotations");
}
}
}
return found;
}
// ------------------------------------------------------------------------
public record JdbcGenerationRecord(TypeMirror[] classAnnotations,
TypeMirror[] constructorAnnotations) {
public static JdbcGenerationRecord of(AnnoElementValueMap map) {
return new JdbcGenerationRecord(
map.getTypeMirrorArray("classAnnotations"),
map.getTypeMirrorArray("constructorAnnotations"));
}
}
public record JdbcDebugRecord() {
public static JdbcDebugRecord of(AnnoElementValueMap map) {
return new JdbcDebugRecord();
}
}
public record JdbcNativeRecord(@Nullable TypeMirror cls, @Nullable String method) {
public static JdbcNativeRecord of(AnnoElementValueMap map) {
var cls = map.getTypeMirror("cls");
var method = map.getString("method", "");
return new JdbcNativeRecord(
cls == null || cls.getKind() == TypeKind.VOID ? null : cls,
method.isBlank() ? null : method);
}
}
public record JdbcSelectRecord(SqlDV sql) {
public static JdbcSelectRecord of(AnnoElementValueMap map) {
return new JdbcSelectRecord(SqlDV.of(map.getString("value", "")));
}
}
public record JdbcUpdateRecord(SqlDV sql, JdbcUpdate.GeneratedValues returnGeneratedValues) {
public static JdbcUpdateRecord of(AnnoElementValueMap map) {
return new JdbcUpdateRecord(
SqlDV.of(map.getString("value", "")),
map.getEnum("returnGeneratedValues", JdbcUpdate.GeneratedValues.class, JdbcUpdate.GeneratedValues.NONE));
}
}
public record JdbcBatchUpdateRecord() {
public static JdbcBatchUpdateRecord of(AnnoElementValueMap map) {
return new JdbcBatchUpdateRecord();
}
}
// ------------------------------------------------------------------------
public record JdbcConfigRecord(@Nullable TypeMirror parent, TypeMirror[] converter,
String generatedClassSuffix,
JdbcConfig.GeneratedValues returnGeneratedValues,
int maxCollectionPlaceholders,
int maxTotalPlaceholders) {
public static JdbcConfigRecord of(AnnoElementValueMap map) {
var parent = map.getTypeMirror("parent");
var converter = map.getTypeMirrorArray("converter");
var generatedClassSuffix = map.getString("generatedClassSuffix", "Jdbc");
var returnGeneratedValues = map.getEnum("returnGeneratedValues", JdbcConfig.GeneratedValues.class, JdbcConfig.GeneratedValues.DEFAULT);
var maxCollectionPlaceholders = map.getInteger("maxCollectionPlaceholders", 1_000);
var maxTotalPlaceholders = map.getInteger("maxTotalPlaceholders", 2_000);
return new JdbcConfigRecord(
parent != null && parent.getKind() == TypeKind.VOID ? null : parent,
converter,
generatedClassSuffix,
returnGeneratedValues,
maxCollectionPlaceholders,
maxTotalPlaceholders);
}
}
public record JdbcLogLevelRecord(JdbcLogLevel.LogLevel value) {
public static JdbcLogLevelRecord of(AnnoElementValueMap map) {
return new JdbcLogLevelRecord(map.getEnum("value", JdbcLogLevel.LogLevel.class, JdbcLogLevel.LogLevel.ERROR));
}
}
}