SQLNameDV.java
/*
* SPDX-FileCopyrightText: 2026 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.msg.JdbcMsg;
import io.kaumei.jdbc.anno.msg.Msg;
import io.kaumei.jdbc.anno.utils.AnnoUtils;
import org.jspecify.annotations.Nullable;
public class SQLNameDV implements Msg.HasMessages {
private static final SQLNameDV NO_NAME = new SQLNameDV(null, Msg.empty());
public static SQLNameDV noName() {
return NO_NAME;
}
public static SQLNameDV of(AnnoElementValueMap map) {
return ofStaticValue(map.getString("value", ""));
}
public static SQLNameDV ofStaticValue(String value) {
return new SQLNameDV(value, validate(value));
}
public static SQLNameDV ofJavaIdentifier(CharSequence javaName) {
if (javaName.isEmpty()) {
throw new IllegalArgumentException("name can not be empty");
}
var length = javaName.length();
var sb = new StringBuilder(length);
for (int i = 0; i < length; i++) {
var current = javaName.charAt(i);
if (i > 0 && Character.isUpperCase(current)
&& (Character.isLowerCase(javaName.charAt(i - 1))
|| Character.isDigit(javaName.charAt(i - 1))
|| i + 1 < length && Character.isUpperCase(javaName.charAt(i - 1))
&& Character.isLowerCase(javaName.charAt(i + 1)))) {
sb.append('_');
}
sb.append(Character.toLowerCase(current));
}
var value = sb.toString();
return new SQLNameDV(value, validate(value));
}
// ------------------------------------------------------------------------
private final @Nullable String value;
private final Msg.Messages messages;
private SQLNameDV(@Nullable String value, Msg.Messages messages) {
this.messages = messages;
this.value = messages.hasMessages() ? null : value;
}
public boolean hasName() {
return this.value != null;
}
public String value() {
return AnnoUtils.requireState(this.value, "value");
}
@Override
public boolean hasMessages() {
return this.messages.hasMessages();
}
@Override
public Msg.Messages messages() {
return this.messages;
}
// -----------------------------------------------------------------
private static Msg.Messages validate(String value) {
if (value.isBlank()) {
return JdbcMsg.annotationValueMustNotBeBlank("@JdbcName");
} else if (value.charAt(0) == '.' || value.charAt(value.length() - 1) == '.' || value.contains("..")) {
return JdbcMsg.invalidJdbcName(value);
}
for (int i = 0; i < value.length(); i++) {
if (isDenied(value.charAt(i))) {
return JdbcMsg.invalidJdbcName(value);
}
}
return Msg.empty();
}
private static boolean isDenied(char ch) {
return Character.isWhitespace(ch)
|| Character.isISOControl(ch)
|| ch == '\''
|| ch == '"'
|| ch == '`'
|| ch == '['
|| ch == ']'
|| ch == ';'
|| ch == ','
|| ch == '('
|| ch == ')'
|| ch == '{'
|| ch == '}'
|| ch == '+'
|| ch == '-'
|| ch == '*'
|| ch == '/'
|| ch == '%'
|| ch == '='
|| ch == '<'
|| ch == '>'
|| ch == '!'
|| ch == '&'
|| ch == '|'
|| ch == '^'
|| ch == '~'
|| ch == '?'
|| ch == ':'
|| ch == '\\';
}
}