JdbcTypeMirror.java
/*
* SPDX-FileCopyrightText: 2025 kaumei.io
* SPDX-License-Identifier: Apache-2.0
*/
package io.kaumei.jdbc.anno.model;
import io.kaumei.jdbc.anno.ctx.Context;
import org.jspecify.annotations.Nullable;
import javax.lang.model.element.*;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeMirror;
import java.util.Objects;
import static io.kaumei.jdbc.anno.ctx.JavaModelUtils.component;
public final class JdbcTypeMirror<T extends Element> {
public static JdbcTypeMirror<ExecutableElement> of(Context ctx, ExecutableElement element) {
return of(ctx, element, element.getReturnType());
}
//public static JdbcTypeMirror<VariableElement> of(Context ctx, VariableElement element) {
// return of(ctx, element, element.asType());
//}
//public static JdbcTypeMirror<RecordComponentElement> of(Context ctx, RecordComponentElement element) {
// return of(ctx, element, element.asType());
//}
private static <T extends Element> JdbcTypeMirror<T> of(Context ctx, T element, TypeMirror type) {
var optional = ctx.optionalFlag(element, type);
if (optional.isOptionalType()) {
type = component(type);
}
switch (type.getKind()) {
case VOID:
return new JdbcTypeMirror<>(ctx, JdbcTypeKind.VOID, element, type, optional);
case BOOLEAN:
return new JdbcTypeMirror<>(ctx, JdbcTypeKind.BOOLEAN, element, type, optional);
case BYTE:
return new JdbcTypeMirror<>(ctx, JdbcTypeKind.BYTE, element, type, optional);
case SHORT:
return new JdbcTypeMirror<>(ctx, JdbcTypeKind.SHORT, element, type, optional);
case INT:
return new JdbcTypeMirror<>(ctx, JdbcTypeKind.INT, element, type, optional);
case LONG:
return new JdbcTypeMirror<>(ctx, JdbcTypeKind.LONG, element, type, optional);
case CHAR:
return new JdbcTypeMirror<>(ctx, JdbcTypeKind.CHAR, element, type, optional);
case FLOAT:
return new JdbcTypeMirror<>(ctx, JdbcTypeKind.FLOAT, element, type, optional);
case DOUBLE:
return new JdbcTypeMirror<>(ctx, JdbcTypeKind.DOUBLE, element, type, optional);
case ARRAY:
var cmpArray = ctx.resolveComponent(element, type);
return new JdbcTypeMirror<>(ctx, JdbcTypeKind.ARRAY, element, type, optional, cmpArray);
case DECLARED:
var declared = ((DeclaredType) type);
if (ctx.JAVA_List.isSameType(declared)) {
var cmp = ctx.resolveComponent(element, type);
return new JdbcTypeMirror<>(ctx, JdbcTypeKind.LIST, element, type, optional, cmp);
} else if (ctx.JAVA_Stream.isSameType(declared)) {
var cmp = ctx.resolveComponent(element, type);
return new JdbcTypeMirror<>(ctx, JdbcTypeKind.STREAM, element, type, optional, cmp);
} else if (ctx.KAUMEI_JDBC_JdbcIterable.isSameType(declared)) {
var cmp = ctx.resolveComponent(element, type);
return new JdbcTypeMirror<>(ctx, JdbcTypeKind.KAUMEI_JDBC_ITERABLE, element, type, optional, cmp);
} else if (ctx.KAUMEI_JDBC_JdbcRow.isSameType(declared)) {
var cmp = ctx.resolveComponent(element, type);
return new JdbcTypeMirror<>(ctx, JdbcTypeKind.KAUMEI_JDBC_ROW, element, type, optional, cmp);
} else if (ctx.KAUMEI_JDBC_JdbcResultSet.isSameType(declared)) {
var cmp = ctx.resolveComponent(element, type);
return new JdbcTypeMirror<>(ctx, JdbcTypeKind.KAUMEI_JDBC_RESULT_SET, element, type, optional, cmp);
} else if (declared.asElement() instanceof TypeElement te) {
if (ctx.JAVA_Boolean.isSameType(type)) {
return new JdbcTypeMirror<>(ctx, JdbcTypeKind.BOOLEAN, element, type, optional);
} else if (ctx.JAVA_Byte.isSameType(type)) {
return new JdbcTypeMirror<>(ctx, JdbcTypeKind.BYTE, element, type, optional);
} else if (ctx.JAVA_Character.isSameType(type)) {
return new JdbcTypeMirror<>(ctx, JdbcTypeKind.CHAR, element, type, optional);
} else if (ctx.JAVA_Double.isSameType(type)) {
return new JdbcTypeMirror<>(ctx, JdbcTypeKind.DOUBLE, element, type, optional);
} else if (ctx.JAVA_Float.isSameType(type)) {
return new JdbcTypeMirror<>(ctx, JdbcTypeKind.FLOAT, element, type, optional);
} else if (ctx.JAVA_Integer.isSameType(type)) {
return new JdbcTypeMirror<>(ctx, JdbcTypeKind.INT, element, type, optional);
} else if (ctx.JAVA_Long.isSameType(type)) {
return new JdbcTypeMirror<>(ctx, JdbcTypeKind.LONG, element, type, optional);
} else if (ctx.JAVA_Void.isSupertypeOf(type)) {
return new JdbcTypeMirror<>(ctx, JdbcTypeKind.VOID, element, type, optional);
} else if (ctx.KAUMEI_JDBC_JdbcBatch.isSupertypeOf(type)) {
return new JdbcTypeMirror<>(ctx, JdbcTypeKind.KAUMEI_JDBC_BATCH, element, type, optional);
} else if (ctx.JAVA_Short.isSameType(type)) {
return new JdbcTypeMirror<>(ctx, JdbcTypeKind.SHORT, element, type, optional);
} else if (te.getKind() == ElementKind.RECORD) {
return new JdbcTypeMirror<>(ctx, JdbcTypeKind.RECORD, element, type, optional);
} else if (te.getKind() == ElementKind.ENUM) {
return new JdbcTypeMirror<>(ctx, JdbcTypeKind.ENUM, element, type, optional);
}
}
break;
}
return new JdbcTypeMirror<>(ctx, JdbcTypeKind.UNKNOWN, element, type, optional);
}
// ---------------------------------------
private final Context ctx;
private final JdbcTypeKind kind;
private final T element;
private final TypeMirror type;
private final OptionalFlag optional;
private @Nullable TypeMirror cmpType;
private @Nullable OptionalFlag cmpOptional;
private JdbcTypeMirror(Context ctx, JdbcTypeKind kind, T element, TypeMirror type, OptionalFlag optional) {
this.ctx = ctx;
this.kind = kind;
this.element = element;
this.type = type;
this.optional = optional;
}
private JdbcTypeMirror(Context ctx, JdbcTypeKind kind, T element,
TypeMirror type, OptionalFlag optional,
TypeOptional cmp) {
this.ctx = ctx;
this.kind = kind;
this.element = element;
this.type = type;
this.optional = optional;
this.cmpType = cmp.type();
this.cmpOptional = cmp.optional();
}
@Override
public String toString() {
return "JdbcTypeMirror{" +
"kind=" + kind +
", element=" + element +
", type=" + type +
", optional=" + optional +
(cmpType != null ? ", cmpType=" + cmpType : "") +
(cmpOptional != null ? ", cmpOptional=" + cmpOptional : "") +
'}';
}
public JdbcTypeKind kind() {
return this.kind;
}
public T element() {
return this.element;
}
public TypeMirror asType() {
if (this.element instanceof ExecutableElement ee) {
return ee.getReturnType();
} else if (this.element instanceof VariableElement ve) {
return ve.asType();
} else if (this.element instanceof RecordComponentElement rce) {
return rce.asType();
}
throw new IllegalStateException("unknown: " + this.element);
}
public boolean isPrimitive() {
return this.type.getKind().isPrimitive();
}
public Name javaSimpleName() {
return element.getSimpleName();
}
// keep in mind: Optional<T> is treated different: T will be the type, you never ever see Optional<T> here
public TypeMirror type() {
return this.type;
}
public OptionalFlag optional() {
return this.optional;
}
// ------------------------------------------------------
public boolean hasComponent() {
return this.cmpType != null && this.cmpOptional != null;
}
public TypeMirror cmpType() {
return Objects.requireNonNull(this.cmpType);
}
public OptionalFlag cmpOptional() {
return Objects.requireNonNull(this.cmpOptional);
}
// ------------------------------------------------------
public TypeMirror cmpOrType() {
return this.cmpType != null ? this.cmpType : type;
}
public OptionalFlag cmpOrOptional() {
return this.cmpOptional != null ? this.cmpOptional : optional;
}
}