SourceMethod.java
/*
* SPDX-FileCopyrightText: 2025 kaumei.io
* SPDX-License-Identifier: Apache-2.0
*/
package io.kaumei.jdbc.anno.model;
import io.kaumei.jdbc.anno.ctx.ConfigService;
import io.kaumei.jdbc.anno.ctx.Context;
import io.kaumei.jdbc.anno.msg.JdbcMsg;
import io.kaumei.jdbc.anno.msg.Msg;
import io.kaumei.jdbc.annotation.JdbcUpdate;
import io.kaumei.jdbc.annotation.config.*;
import org.jspecify.annotations.Nullable;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import static java.util.Objects.requireNonNull;
public class SourceMethod {
public static SourceMethod of(Context ctx, ExecutableElement method) {
var messages = Msg.builder();
var annoMap = AnnoMap.of(ctx, method);
SourceMethodParameter parameter = null;
// ----- check entry points
var entryPoint = EntryPoint.of(ctx, annoMap);
var entryPoints = entryPoint.countEntryPoints();
if (entryPoints == 0) {
messages.add(JdbcMsg.jdbcMethodRequiresEntryPointAnnotation());
} else if (entryPoints > 1) {
messages.add(JdbcMsg.jdbcMethodRequiresSingleEntryPointAnnotation());
} else if (entryPoint.selectOpt != null) {
if (entryPoint.selectOpt.sql().value().isBlank()) {
messages.add(JdbcMsg.jdbcSelectRequiresSqlString());
} else {
parameter = SourceMethodParameter.of(ctx, messages, method, entryPoint.selectOpt.sql());
}
} else if (entryPoint.updateOpt != null) {
if (entryPoint.updateOpt.sql().value().isBlank()) {
messages.add(JdbcMsg.jdbcUpdateRequiresSqlString());
} else {
parameter = SourceMethodParameter.of(ctx, messages, method, entryPoint.updateOpt.sql());
}
}
if (parameter == null) {
parameter = SourceMethodParameter.of(ctx, messages, method);
}
var jdbcName = ctx.JDBC_NAME.getAnnoOpt(method);
messages.add(jdbcName);
var jdbcConverterName = ctx.JDBC_CONVERTER_NAME.getAnnoOpt(method);
if (jdbcConverterName != null && jdbcConverterName.isBlankName()) {
messages.add(JdbcMsg.annotationValueMustNotBeBlank("@JdbcConverterName"));
}
return new SourceMethod(ctx, method, entryPoint, annoMap, parameter, messages.build());
}
public record EntryPoint(JavaAnnoType.@Nullable JdbcSelectRecord selectOpt,
JavaAnnoType.@Nullable JdbcUpdateRecord updateOpt,
JavaAnnoType.@Nullable JdbcNativeRecord nativeJdbcOpt,
JavaAnnoType.@Nullable JdbcBatchUpdateRecord batchUpdateOpt) {
static EntryPoint of(Context ctx, AnnoMap map) {
return new EntryPoint(map.getOpt(ctx.JDBC_SELECT), map.getOpt(ctx.JDBC_UPDATE), map.getOpt(ctx.JDBC_NATIVE), map.getOpt(ctx.JDBC_BATCH_UPDATE));
}
public JavaAnnoType.JdbcUpdateRecord update() {
return requireNonNull(updateOpt);
}
public JavaAnnoType.JdbcNativeRecord nativeJdbc() {
return requireNonNull(nativeJdbcOpt);
}
int countEntryPoints() {
int count = 0;
if (selectOpt != null) {
count++;
}
if (updateOpt != null) {
count++;
}
if (nativeJdbcOpt != null) {
count++;
}
if (batchUpdateOpt != null) {
count++;
}
return count;
}
}
// ------------------------------------------------------------------------
private final Context ctx;
private final ExecutableElement method;
private final EntryPoint entryPoint;
private final AnnoMap annoMap;
private final SourceMethodParameter parameter;
private final Msg.Messages messages;
SourceMethod(Context ctx, ExecutableElement method, EntryPoint entryPoint, AnnoMap annoMap, SourceMethodParameter parameter, Msg.Messages messages) {
this.ctx = ctx;
this.method = method;
this.entryPoint = entryPoint;
this.annoMap = annoMap;
this.parameter = parameter;
this.messages = messages;
}
@Override
public String toString() {
return method.getEnclosingElement() + "." + method.getSimpleName() + ":" + method.getReturnType();
}
// ------------------------------------------------------------------------
public ExecutableElement method() {
return method;
}
public EntryPoint entryPoint() {
return entryPoint;
}
public AnnoMap annoMap() {
return annoMap;
}
public TypeElement parent() {
return (TypeElement) this.method.getEnclosingElement();
}
public SourceMethodParameter parameter() {
return parameter;
}
public boolean hasMessages() {
return messages.hasMessages();
}
public Msg.Messages messages() {
return messages;
}
public SQLNameDV jdbcName() {
return annoMap.get(ctx.JDBC_NAME, SQLNameDV.noName());
}
public ConverterNameDV jdbcConverterName() {
return annoMap.get(ctx.JDBC_CONVERTER_NAME, ConverterNameDV.unnamed());
}
public JdbcUpdate.GeneratedValues jdbcReturnGeneratedValues() {
var value = entryPoint.update().returnGeneratedValues();
if (value == JdbcUpdate.GeneratedValues.DEFAULT) {
value = this.ctx.kaumeiConfig.jdbcReturnGeneratedValues();
}
return value;
}
public JdbcNoMoreRows.Kind jdbcNoMoreRows() {
return this.ctx.kaumeiConfig.searchOnMethod(this.ctx.kaumeiConfig.NO_MORE_ROWS, this);
}
public JdbcNoRows.Kind jdbcNoRows() {
return this.ctx.kaumeiConfig.searchOnMethod(this.ctx.kaumeiConfig.NO_ROWS, this);
}
public ConfigService.@Nullable OptionValue<Integer> jdbcFetchSize() {
return this.ctx.kaumeiConfig.search(this.ctx.kaumeiConfig.FETCH_SIZE, this);
}
public ConfigService.@Nullable OptionValue<Integer> jdbcMaxRows() {
return this.ctx.kaumeiConfig.search(this.ctx.kaumeiConfig.MAX_ROWS, this);
}
public ConfigService.@Nullable OptionValue<JdbcResultSetConcurrency.Kind> jdbcResultSetConcurrency() {
return this.ctx.kaumeiConfig.search(this.ctx.kaumeiConfig.RESULT_SET_CONCURRENCY, this);
}
public ConfigService.@Nullable OptionValue<JdbcResultSetType.Kind> jdbcResultSetType() {
return this.ctx.kaumeiConfig.search(this.ctx.kaumeiConfig.RESULT_SET_TYPE, this);
}
public ConfigService.@Nullable OptionValue<JdbcFetchDirection.Kind> jdbcFetchDirection() {
return this.ctx.kaumeiConfig.search(this.ctx.kaumeiConfig.FETCH_DIRECTION, this);
}
public ConfigService.@Nullable OptionValue<Integer> jdbcQueryTimeout() {
return this.ctx.kaumeiConfig.search(this.ctx.kaumeiConfig.QUERY_TIMEOUT, this);
}
public ConfigService.@Nullable OptionValue<Integer> jdbcBatchSize() {
return this.ctx.kaumeiConfig.search(this.ctx.kaumeiConfig.BATCH_SIZE, this);
}
// ------------------------------------------------------------------------
public Msg.Messages unusedAnno() {
var messages = Msg.builder();
if (annoMap.hasUnused()) {
messages.add(JdbcMsg.javaMethodHasUnusedAnnotations(annoMap.unused()));
}
parameter.addUnusedAnno(messages);
return messages.build();
}
// ------------------------------------------------------------------------
public JdbcTypeMirror<ExecutableElement> returnType() {
return ctx.jdbcTypeMirror(this.method);
}
}