JdbcMsg.java
/*
* SPDX-FileCopyrightText: 2025 kaumei.io
* SPDX-License-Identifier: Apache-2.0
*/
package io.kaumei.jdbc.anno.msg;
import io.kaumei.jdbc.anno.model.JavaAnnoType;
import io.kaumei.jdbc.anno.model.JdbcTypeMirror;
import io.kaumei.jdbc.anno.model.OptionalFlag;
import io.kaumei.jdbc.anno.store.SearchKey;
import io.kaumei.jdbc.anno.store.SourceDV;
import io.kaumei.jdbc.anno.store.StoreID;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.TypeMirror;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
import static io.kaumei.jdbc.anno.ctx.JavaModelToString.searchKey;
public class JdbcMsg {
private JdbcMsg() { // JaCoCo:no
}
public static final Msg.Message INVALID_DEPENDENT_CONVERTER = Msg.of("invalid dependent converter");
public static final Msg.Message DUPLICATE_KEY = Msg.of("duplicate key");
public static Msg.Message duplicateConverter(SourceDV source) {
return Msg.of("Duplicate converter: " + source);
}
public static final Msg.Message DUPLICATE_METHOD = Msg.of("duplicate method");
public static final Msg.Message INCOMPATIBLE_TYPE = Msg.of("incompatible type");
public static final Msg.Message NOT_FOUND = Msg.of("not found");
public static final Msg.Message OPTIONAL_TYPE_NOT_SUPPORTED = Msg.of("""
Optional<T> type not supported.
See JavaDoc of Optional:
Optional is primarily intended for use as a method return ...
""");
public static final Msg.Message INVALID_RETURN_TYPE = Msg.of("Invalid return type");
public static final Msg.Message NULLABLE_NOT_SUPPORTED = Msg.of("@Nullable not supported");
public static final Msg.Message JDBC_CONVERTER_NAME_NOT_SUPPORTED = Msg.of("@JdbcConverterName not supported");
public static final Msg.Message JAVA_TO_JDBC_RESOLVER_LIST_REQUIRES_COMPONENT_TYPE = Msg.of("List parameters used with values placeholders must define a component type, e.g. List<String>");
public static final Msg.Message JAVA_TO_JDBC_RESOLVER_VALUES_UNSUPPORTED_TYPES = Msg.of("Only Array, List or Record are supported");
public static final Msg.Message JAVA_TO_JDBC_RESOLVER_NAMES_UNSUPPORTED_TYPES = Msg.of("Only records are supported");
public static final Msg.Message JDBC_TO_JAVA_METHOD_MUST_BE_STATIC = Msg.of("@JdbcToJava method must be static");
public static final Msg.Message JDBC_TO_JAVA_METHOD_MUST_BE_VISIBLE = Msg.of("@JdbcToJava method must be visible (public/package)");
public static final Msg.Message JDBC_TO_JAVA_METHOD_THROWS_INCOMPATIBLE_EXCEPTIONS = Msg.of("@JdbcToJava method throws incompatible exceptions");
public static final Msg.Message JDBC_TO_JAVA_MUST_NOT_RETURN_VOID = Msg.of("@JdbcToJava must not return void");
public static final Msg.Message JDBC_TO_JAVA_METHOD_REQUIRES_PARAMETER = Msg.of("@JdbcToJava method must have at least one parameter");
public static final Msg.Message JDBC_TO_JAVA_RECORD_CONSTRUCTOR_DOES_NOT_SUPPORT_RESULT_SET = Msg.of("@JdbcToJava record constructor does not support ResultSet");
public static final Msg.Message JDBC_TO_JAVA_CLASS_CONSTRUCTOR_DOES_NOT_SUPPORT_RESULT_SET_INT = Msg.of("@JdbcToJava class constructor does not support ResultSet,int");
public static final Msg.Message JDBC_TO_JAVA_FIRST_PARAMETER_MUST_BE_RESULT_SET = Msg.of("@JdbcToJava first parameter must be ResultSet");
public static final Msg.Message JDBC_TO_JAVA_RESULT_SET_PARAMETER_COUNT_INVALID = Msg.of("@JdbcToJava with first ResultSet param has to many parameters");
public static final Msg.Message JDBC_TO_JAVA_NAME_MAPPING_UNSUPPORTED_FOR_ONE_PARAM = Msg.of("@JdbcToJava name mapping not supported for one param");
public static final Msg.Message JDBC_TO_JAVA_NAME_MAPPING_UNSUPPORTED_FOR_RESULT_SET = Msg.of("@JdbcToJava name mapping not supported for ResultSet");
public static final Msg.Message JDBC_TO_JAVA_NULLABLE_PARAMETER_UNSUPPORTED = Msg.of("@JdbcToJava nullable param not supported");
public static final Msg.Message JDBC_TO_JAVA_RETURN_TYPE_MUST_BE_NON_NULL_OR_UNSPECIFIED = Msg.of("@JdbcToJava method return type must be @NonNull or unspecified");
public static final Msg.Message JDBC_TO_JAVA_FIRST_PARAMETER_MUST_BE_NON_NULL_OR_UNSPECIFIED = Msg.of("@JdbcToJava method first parameter must be @NonNull or unspecified");
public static final Msg.Message JDBC_TO_JAVA_SECOND_PARAMETER_MUST_BE_INT = Msg.of("@JdbcToJava method second parameter must be int");
public static final Msg.Message JDBC_TO_JAVA_RETURN_TYPE_MUST_BE_NULLABLE_OR_UNSPECIFIED = Msg.of("@JdbcToJava method return type must be a @Nullable or unspecified");
public static final Msg.Message JDBC_TO_JAVA_ANNOTATION_NAME_UNSUPPORTED = Msg.of("Annotation must not have a name");
public static final Msg.Message JDBC_TO_JAVA_TOO_MANY_ANNOTATIONS = Msg.of("To many annotations");
public static final Msg.Message JDBC_TO_JAVA_NO_CONSTRUCTOR_FOUND = Msg.of("no constructor found");
public static final Msg.Message JDBC_TO_JAVA_TOO_MANY_CONSTRUCTORS = Msg.of("too many constructors were found");
public static final Msg.Message JDBC_TO_JAVA_COLUMN_OBJECT_REQUIRES_COLUMN_CONVERTER = Msg.of("@JdbcToJava column object requires a column converter");
public static final Msg.Message JDBC_TO_JAVA_ROW_COMPONENT_REQUIRES_COLUMN_CONVERTER = Msg.of("@JdbcToJava row component requires a column converter");
public static final Msg.Message JAVA_TO_JDBC_METHOD_REQUIRES_ONE_OR_THREE_PARAMETERS = Msg.of("@JavaToJdbc method must have one or three parameters");
public static final Msg.Message JAVA_TO_JDBC_METHOD_MUST_BE_VISIBLE = Msg.of("@JavaToJdbc Must be visible (public/package)");
public static final Msg.Message JAVA_TO_JDBC_RETURN_TYPE_MUST_BE_NON_NULL_OR_UNSPECIFIED = Msg.of("@JavaToJdbc return type must be @NonNull or unspecified");
public static final Msg.Message JAVA_TO_JDBC_METHOD_MUST_HAVE_NO_PARAMETERS = Msg.of("@JavaToJdbc method must have no parameters");
public static final Msg.Message JAVA_TO_JDBC_HAS_INCOMPATIBLE_EXCEPTIONS = Msg.of("has incompatible exceptions");
public static final Msg.Message JAVA_TO_JDBC_RECORD_MUST_HAVE_ONE_COMPONENT = Msg.of("Record must have exact one component");
public static final Msg.Message JAVA_TO_JDBC_RECORD_COMPONENT_MUST_BE_NON_NULL_OR_UNSPECIFIED = Msg.of("Record component must be 'non-null' or 'unspecified'");
public static final Msg.Message JAVA_TO_JDBC_NO_TYPE = Msg.of("no type");
public static final Msg.Message JAVA_TO_JDBC_FIRST_PARAMETER_MUST_BE_PREPARED_STATEMENT = Msg.of("first parameter must be a PreparedStatement");
public static final Msg.Message JAVA_TO_JDBC_FIRST_PARAMETER_MUST_BE_NON_NULL_OR_UNSPECIFIED = Msg.of("first parameter must be @NonNull or unspecified");
public static final Msg.Message JAVA_TO_JDBC_SECOND_PARAMETER_MUST_BE_INT = Msg.of("second parameter must be int");
public static final Msg.Message JAVA_TO_JDBC_THIRD_PARAMETER_MUST_BE_NULLABLE_OR_UNSPECIFIED = Msg.of("third parameter must be @Nullable or unspecified");
public static final Msg.Message JAVA_TO_JDBC_ANNOTATION_NAME_UNSUPPORTED = Msg.of("Annotation must not have a name");
public static final Msg.Message JAVA_TO_JDBC_TOO_MANY_ANNOTATIONS = Msg.of("To many annotations");
public static Msg.Message jdbcToJavaParameterOptionalInvalid(CharSequence parameter) {
return Msg.of("@JdbcToJava parameter " + parameter + " invalid Optional");
}
public static Msg.Message jdbcToJavaElementTypeNotFound(TypeMirror type) {
return Msg.of("@JavaToJdbc element type not found for " + type);
}
public static Msg.Message jdbcToJavaAnnotationHasWrongType(TypeMirror type) {
return Msg.of("Annotation has wrong type: " + type);
}
public static Msg.Message javaToJdbcElementTypeNotFound(TypeMirror type) {
return Msg.of("@JavaToJdbc element type not found for " + type);
}
public static Msg.Message javaToJdbcAnnotationHasWrongType(TypeMirror type) {
return Msg.of("Annotation has wrong type: " + type);
}
public static Msg.Message annotationValueMustNotBeBlank(CharSequence annotation) {
return Msg.of(annotation + " value must not be blank");
}
public static Msg.Message invalidJdbcName(CharSequence name) {
return Msg.of("@JdbcName value '" + name + "' is not a valid SQL name");
}
public static Msg.Message converter(String text) {
return Msg.of(text);
}
public static Msg.Message cycle(List<?> cycle) {
return Msg.of("generate cycle: " + cycle);
}
// ------------------------------------------------------------------------
public static Msg.Message ambiguous(Set<StoreID> ambiguous) {
return Msg.of("Ambiguous converter: " + toSortedList(ambiguous, StoreID::toString));
}
// ------------------------------------------------------------------------
protected static Msg.Message invalidParam(CharSequence name, String type, Msg.Messages messages) {
var sb = new StringBuilder();
sb.append("Java method parameter '").append(name);
sb.append("': type '").append(type);
sb.append("' must be supported as SQL placeholder, but ");
messages.join(sb, ", ");
return Msg.of(sb.toString());
}
public static Msg.Message invalidParam(VariableElement param, Msg.Messages messages) {
return invalidParam(param.getSimpleName(), searchKey(param.asType()), messages);
}
public static Msg.Message jdbcMethodRequiresEntryPointAnnotation() {
return Msg.of("Java method: must have one of @JdbcNative, @JdbcSelect, @JdbcUpdate or @JdbcBatchUpdate");
}
public static Msg.Message jdbcMethodRequiresSingleEntryPointAnnotation() {
return Msg.of("Java method: must not have multiple entry-point annotations");
}
public static Msg.Message jdbcSelectRequiresSqlString() {
return Msg.of("Java method: @JdbcSelect requires a SQL string");
}
public static Msg.Message jdbcUpdateRequiresSqlString() {
return Msg.of("Java method: @JdbcUpdate requires a SQL string");
}
public static Msg.Message javaMethodHasUnusedAnnotations(Set<JavaAnnoType<?>> annotations) {
return Msg.of("Java method: has unused annotations: " + toSortedList(annotations, JavaAnnoType::toString));
}
public static Msg.Message javaMethodHasUnusedAnnotations(Class<?>... annotations) {
return Msg.of("Java method: has unused annotations: " + Arrays.stream(annotations).map(Class::getCanonicalName).toList());
}
public static Msg.Message unusedAnnotations(TypeElement element, Set<JavaAnnoType<?>> annoSet) {
return Msg.of("Type " + element.getQualifiedName() + " has unused annotations: " + toSortedList(annoSet, JavaAnnoType::toString));
}
public static Msg.Message javaMethodParameterHasUnusedAnnotations(CharSequence parameter, Set<JavaAnnoType<?>> annotations) {
return Msg.of("Java method parameter '" + parameter + "': has unused annotations: " + toSortedList(annotations, JavaAnnoType::toString));
}
private static <T> String toSortedList(Set<T> annotations, Function<T, String> func) {
return annotations.stream().map(func).sorted().toList().toString();
}
public static Msg.Message javaMethodParameterJdbcConverterNameUnsupportedForSqlPlaceholder(CharSequence parameter,
CharSequence placeholder,
CharSequence shape) {
return Msg.of("Java method parameter '" + parameter + "': @JdbcConverterName is not supported for SQL "
+ shape + " placeholder '" + placeholder + "'");
}
public static Msg.Message sqlPlaceholderUnnamedUnsupported() {
return Msg.of("SQL placeholder: unnamed placeholders are not supported");
}
public static Msg.Message sqlPlaceholderPathUnsupported(CharSequence placeholder) {
return Msg.of("SQL placeholder '" + placeholder + "': paths are not supported");
}
public static Msg.Message sqlPlaceholderNotFound(CharSequence placeholder) {
return Msg.of("SQL placeholder '" + placeholder + "': not found in method parameter list");
}
public static Msg.Message sqlPlaceholderUsedWithDifferentShapes(CharSequence placeholder,
CharSequence first,
CharSequence second) {
return Msg.of("SQL placeholder '" + placeholder + "': must not be used as '" + first
+ "' and '" + second + "' placeholder");
}
public static Msg.Message sqlPlaceholderCountExceedsLimit(int count, int limit) {
return Msg.of("SQL placeholder count exceeds @JdbcConfig.maxTotalPlaceholders: " + count + " > " + limit);
}
// ------------------------------------------------------------------------
public static Msg.Message invalidSqlParameterConverter(String sql, SourceDV component, Msg.Messages messages) {
return new InvalidParamConverter(sql, component, messages);
}
public static Msg.Message invalidJdbcToJavaResultConverter(SourceDV method,
SearchKey searchKey,
Msg.Messages messages) {
return new InvalidReturnConverter(method, searchKey, messages);
}
private static String renderInvalidSqlParameterConverter(String sql, SourceDV component, Msg.Messages messages) {
var sb = new StringBuilder();
sb.append("sql......: ':").append(sql);
sb.append("'\nparameter: ").append(component);
sb.append('\n');
appendMessages(sb, messages);
return sb.toString();
}
private static String renderInvalidJdbcToJavaResultConverter(SearchKey searchKey,
Msg.Messages messages) {
var sb = new StringBuilder();
sb.append("return...: ").append(searchKey.toStoreId()).append('\n');
appendMessages(sb, messages);
return sb.toString();
}
private static void appendMessages(StringBuilder sb, Msg.Messages messages) {
var first = true;
for (var message : messages) {
if (first) {
first = false;
} else {
sb.append('\n');
}
sb.append(message.text());
}
}
// @JdbcNative ------------------------------------------------------------
public static Msg.Message jdbcNativeRequiresMatchingStaticMethod(TypeElement targetClass,
CharSequence targetMethod) {
return jdbcNativeRequiresMatchingStaticMethod(targetClass.toString(), targetMethod);
}
public static Msg.Message jdbcNativeRequiresMatchingStaticMethod(CharSequence targetClass,
CharSequence targetMethod) {
return Msg.of("Java method: @JdbcNative requires a matching static method '" + targetMethod
+ "' in '" + targetClass + "' with java.sql.Connection as first parameter");
}
public static Msg.Message jdbcNativeRequiresCallableStaticMethod(TypeElement targetClass,
CharSequence targetMethod) {
return jdbcNativeRequiresCallableStaticMethod(targetClass.toString(), targetMethod);
}
public static Msg.Message jdbcNativeRequiresCallableStaticMethod(CharSequence targetClass,
CharSequence targetMethod) {
return Msg.of("Java method: @JdbcNative requires static method '" + targetMethod + "' in '"
+ targetClass + "' to match return type, parameters, nullness and checked exceptions");
}
public static Msg.Message returnTypeNotAssignable(CharSequence targetReturnType,
CharSequence sourceReturnType) {
return Msg.of("Return type '" + targetReturnType + "' is not assignable to '" + sourceReturnType + "'");
}
public static Msg.Message returnTypeNullnessMismatch(OptionalFlag targetFlag,
OptionalFlag sourceFlag) {
return Msg.of("Return type mismatch. target: '" + targetFlag + "' is not compatible with source: '" + sourceFlag + "'");
}
public static Msg.Message javaMethodsHaveDifferentParameter() {
return Msg.of("have different parameter");
}
public static Msg.Message firstParameterMustBeConnection() {
return Msg.of("first param must have type 'java.sql.Connection'");
}
public static Msg.Message sourceParamOptionalTypeNotSupported(int pos) {
return Msg.of("Source param at pos " + pos + ": " + OPTIONAL_TYPE_NOT_SUPPORTED);
}
public static Msg.Message targetParamOptionalTypeNotSupported(int pos) {
return Msg.of("Target param at pos " + pos + ": " + OPTIONAL_TYPE_NOT_SUPPORTED);
}
public static Msg.Message paramNullnessMismatch(int pos,
OptionalFlag sourceFlag,
OptionalFlag targetFlag) {
return Msg.of("Param mismatch optional at pos " + pos + ": '" + sourceFlag + "' is not compatible with '" + targetFlag + "'");
}
public static Msg.Message paramTypeMismatch(int pos,
CharSequence sourceType,
CharSequence targetType) {
return Msg.of("Param mismatch type at pos " + pos + ": '" + sourceType + "' is not same type '" + targetType + "'");
}
public static Msg.Message exceptionNotCompatible(CharSequence exception) {
return Msg.of("Exception not compatible: " + exception);
}
// @JdbcSelect ------------------------------------------------------------
public static Msg.Message jdbcSelectRequiresSupportedReturnType(JdbcTypeMirror<?> returnType) {
return jdbcSelectRequiresSupportedReturnType(returnType.asType().toString());
}
public static Msg.Message jdbcSelectRequiresSupportedReturnType(CharSequence returnType) {
return Msg.of("Java method return value: @JdbcSelect requires a supported value, row, list, stream, "
+ "JdbcIterable, JdbcResultSet, or JdbcRow return type, but found '"
+ returnType + "'");
}
public static Msg.Message jdbcSelectJdbcRowRequiresNullableOrOptional(OptionalFlag optional) {
return Msg.of("Java method return value: @JdbcSelect with JdbcRow<T> requires a nullable or "
+ "Optional<JdbcRow<T>> return type, but found '" + optional + "'");
}
public static Msg.Message jdbcSelectNoRowsReturnNullRequiresNullableOrOptional(OptionalFlag optional) {
return Msg.of("Java method return value: @JdbcNoRows(RETURN_NULL) requires a nullable or Optional<T> "
+ "return type, but found '" + optional + "'");
}
public static Msg.Message jdbcSelectNoRowsReturnNullRequiresNullable(OptionalFlag optional) {
return Msg.of("Java method return value: @JdbcNoRows(RETURN_NULL) requires a nullable return type, "
+ "but found '" + optional + "'");
}
public static Msg.Message jdbcSelectRequiresNonNullOrUnspecifiedReturnType(OptionalFlag optional) {
return Msg.of("Java method return value: @JdbcSelect requires a non-null or unspecified return type "
+ "for list, stream, JdbcIterable, or JdbcResultSet return types, but found '" + optional + "'");
}
public static Msg.Message jdbcSelectRequiresNonNullOrUnspecifiedRowReturnComponent(OptionalFlag optional) {
return Msg.of("Java method return value: row return components must be unspecified or non-null, "
+ "but found '" + optional + "'");
}
public static final Msg.Message JDBC_SELECT_REQUIRES_RESULT_SET_TYPE_AND_CONCURRENCY = Msg.of(
"Java method: @JdbcResultSetType and @JdbcResultSetConcurrency must be configured together");
// @JdbcUpdate ------------------------------------------------------------
public static Msg.Message jdbcUpdateRequiresSupportedReturnType(JdbcTypeMirror<?> returnType) {
return jdbcUpdateRequiresSupportedReturnType(returnType.asType().toString());
}
public static Msg.Message jdbcUpdateRequiresSupportedReturnType(CharSequence returnType) {
return Msg.of("Java method return value: @JdbcUpdate requires void, int, boolean, or generated values "
+ "with a supported return type, but found '" + returnType + "'");
}
public static Msg.Message jdbcUpdateGeneratedValuesRequiresSupportedReturnType(JdbcTypeMirror<?> returnType) {
return jdbcUpdateGeneratedValuesRequiresSupportedReturnType(returnType.asType().toString());
}
public static Msg.Message jdbcUpdateGeneratedValuesRequiresSupportedReturnType(CharSequence returnType) {
return Msg.of("Java method return value: @JdbcUpdate generated values require a supported value or row "
+ "return type, but found '" + returnType + "'");
}
public static Msg.Message jdbcUpdateGeneratedValuesRequiresNonNullOrUnspecifiedReturnType(OptionalFlag optional) {
return Msg.of("Java method return value: @JdbcUpdate generated values require a non-null or unspecified "
+ "return type, but found '" + optional + "'");
}
// @JdbcBatch ------------------------------------------------------------
public static Msg.Message jdbcBatchRequiresNonNullOrUnspecifiedReturnType(OptionalFlag optional) {
return Msg.of("Java method return value: @JdbcBatchUpdate requires a non-null or unspecified batch "
+ "interface return type, but found '" + optional + "'");
}
public static Msg.Message jdbcBatchRequiresBatchInterfaceReturnType(JdbcTypeMirror<?> returnType) {
return jdbcBatchRequiresBatchInterfaceReturnType(returnType.asType().toString());
}
public static Msg.Message jdbcBatchRequiresBatchInterfaceReturnType(CharSequence returnType) {
return Msg.of("Java method return value: @JdbcBatchUpdate requires a nested batch interface that "
+ "extends JdbcBatch, but found '" + returnType + "'");
}
public static Msg.Message jdbcBatchTypeMustBeInterfaceMessage(CharSequence batchType) {
return Msg.of("Java method return value: @JdbcBatchUpdate requires a batch interface, but found '"
+ batchType + "'");
}
public static Msg.Message jdbcBatchTypeMustBeNestedInFactoryInterface(CharSequence batchType) {
return Msg.of("Java method return value: @JdbcBatchUpdate requires the batch interface to be declared "
+ "inside the factory interface, but found '" + batchType + "'");
}
public static Msg.Message jdbcBatchTypeMustDeclareExactlyOneUpdateMethod(CharSequence batchType) {
return Msg.of("Java method return value: @JdbcBatchUpdate batch interface '" + batchType
+ "' must declare exactly one non-static, non-default @JdbcUpdate method");
}
public static Msg.Message jdbcBatchUpdateMethodMustReturnVoidMessage(CharSequence batchType,
CharSequence updateMethod) {
return Msg.of("Java method: @JdbcBatchUpdate update method '" + batchType + "." + updateMethod
+ "' must return void");
}
public static Msg.Message jdbcBatchUpdateMethodMustNotUseDynamicCollectionExpansion(VariableElement param) {
var sb = new StringBuilder();
sb.append("Java method parameter '").append(param.getSimpleName());
sb.append("': type '").append(searchKey(param.asType()));
sb.append("' dynamic parameter extension in batch mode not allowed");
return Msg.of(sb.toString());
}
// ------------------------------------------------------------------------
public static final class InvalidParamConverter extends MessageImpl {
private final String sql;
private final SourceDV parameter;
private final Msg.Messages messages;
InvalidParamConverter(String sql, SourceDV parameter, Msg.Messages messages) {
super(renderInvalidSqlParameterConverter(sql, parameter, messages));
this.sql = sql;
this.parameter = parameter;
this.messages = messages;
}
public String sql() {
return sql;
}
public SourceDV parameter() {
return parameter;
}
public Msg.Messages messages() {
return messages;
}
}
public static final class InvalidReturnConverter extends MessageImpl {
private final SourceDV method;
private final SearchKey searchKey;
private final Msg.Messages messages;
InvalidReturnConverter(SourceDV method, SearchKey searchKey, Msg.Messages messages) {
super(renderInvalidJdbcToJavaResultConverter(searchKey, messages));
this.method = method;
this.searchKey = searchKey;
this.messages = messages;
}
public SourceDV method() {
return method;
}
public SearchKey searchKey() {
return searchKey;
}
public Msg.Messages messages() {
return messages;
}
}
}