KotlinDynamicSqlSupportClassGenerator.java
/*
* Copyright 2006-2026 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mybatis.generator.runtime.dynamicsql.kotlin;
import static org.mybatis.generator.internal.util.StringUtility.escapeStringForKotlin;
import static org.mybatis.generator.runtime.mybatis3.MyBatis3FormattingUtilities.getEscapedColumnName;
import java.util.List;
import org.mybatis.generator.api.IntrospectedColumn;
import org.mybatis.generator.api.dom.java.FullyQualifiedJavaType;
import org.mybatis.generator.api.dom.kotlin.FullyQualifiedKotlinType;
import org.mybatis.generator.api.dom.kotlin.JavaToKotlinTypeConverter;
import org.mybatis.generator.api.dom.kotlin.KotlinFile;
import org.mybatis.generator.api.dom.kotlin.KotlinProperty;
import org.mybatis.generator.api.dom.kotlin.KotlinType;
import org.mybatis.generator.codegen.AbstractGenerator;
import org.mybatis.generator.config.PropertyRegistry;
import org.mybatis.generator.internal.util.JavaBeansUtil;
import org.mybatis.generator.internal.util.StringUtility;
import org.mybatis.generator.internal.util.messages.Messages;
public class KotlinDynamicSqlSupportClassGenerator extends AbstractGenerator {
private final KotlinFile kotlinFile;
private final KotlinType innerClass;
private final KotlinType outerObject;
private final KotlinProperty tableProperty;
private final boolean useSnakeCase;
public KotlinDynamicSqlSupportClassGenerator(Builder builder) {
super(builder);
useSnakeCase = introspectedTable
.findTableOrClientGeneratorPropertyAsBoolean(PropertyRegistry.ANY_USE_SNAKE_CASE_IDENTIFIERS);
FullyQualifiedJavaType type = new FullyQualifiedJavaType(introspectedTable.getMyBatisDynamicSqlSupportType());
kotlinFile = buildBasicFile(type);
outerObject = buildOuterObject(kotlinFile, type);
tableProperty = calculateTableProperty();
outerObject.addNamedItem(tableProperty);
innerClass = buildInnerClass();
List<IntrospectedColumn> columns = introspectedTable.getAllColumns();
for (IntrospectedColumn column : columns) {
handleColumn(kotlinFile, outerObject, innerClass, getTablePropertyName(), column);
}
outerObject.addNamedItem(innerClass);
}
public KotlinFile getKotlinFile() {
return kotlinFile;
}
public String getTablePropertyName() {
return tableProperty.getName();
}
public KotlinType getInnerClass() {
return innerClass;
}
public KotlinType getOuterObject() {
return outerObject;
}
public String getTablePropertyImport() {
return getSupportObjectImport()
+ "." //$NON-NLS-1$
+ tableProperty.getName();
}
public String getSupportObjectImport() {
return kotlinFile.getPackage().map(s -> s + ".").orElse("") //$NON-NLS-1$ //$NON-NLS-2$
+ outerObject.getName();
}
private KotlinFile buildBasicFile(FullyQualifiedJavaType type) {
KotlinFile kf = new KotlinFile(type.getShortNameWithoutTypeArguments());
kf.setPackage(type.getPackageName());
commentGenerator.addFileComment(kf);
return kf;
}
private KotlinType buildOuterObject(KotlinFile kotlinFile, FullyQualifiedJavaType type) {
KotlinType answer = KotlinType.newObject(type.getShortNameWithoutTypeArguments()).build();
kotlinFile.addImport("org.mybatis.dynamic.sql.AliasableSqlTable"); //$NON-NLS-1$
kotlinFile.addImport("org.mybatis.dynamic.sql.util.kotlin.elements.column"); //$NON-NLS-1$
kotlinFile.addImport("java.sql.JDBCType"); //$NON-NLS-1$
kotlinFile.addNamedItem(answer);
return answer;
}
private KotlinType buildInnerClass() {
String domainObjectName = introspectedTable.getMyBatisDynamicSQLTableObjectName();
return KotlinType.newClass(domainObjectName)
.withSuperType("AliasableSqlTable<" + domainObjectName + ">(\"" //$NON-NLS-1$ //$NON-NLS-2$
+ escapeStringForKotlin(introspectedTable.getFullyQualifiedTableNameAtRuntime())
+ "\", ::" + domainObjectName //$NON-NLS-1$
+ ")") //$NON-NLS-1$
.build();
}
private KotlinProperty calculateTableProperty() {
String tableType = introspectedTable.getMyBatisDynamicSQLTableObjectName();
String fieldName = JavaBeansUtil.getValidPropertyName(introspectedTable.getMyBatisDynamicSQLTableObjectName());
if (useSnakeCase) {
fieldName = StringUtility.convertCamelCaseToSnakeCase(fieldName);
}
return KotlinProperty.newVal(fieldName)
.withInitializationString(tableType + "()") //$NON-NLS-1$
.build();
}
private void handleColumn(KotlinFile kotlinFile, KotlinType outerObject, KotlinType innerClass,
String tableFieldName, IntrospectedColumn column) {
FullyQualifiedKotlinType kt = JavaToKotlinTypeConverter.convert(column.getFullyQualifiedJavaType());
kotlinFile.addImports(kt.getImportList());
String fieldName = column.getJavaProperty();
if (useSnakeCase) {
fieldName = StringUtility.convertCamelCaseToSnakeCase(fieldName);
}
// outer object
if (fieldName.equals(tableFieldName)) {
// name collision, skip the shortcut field
warnings.add(
Messages.getString("Warning.29", //$NON-NLS-1$
fieldName, getSupportObjectImport()));
} else {
KotlinProperty prop = KotlinProperty.newVal(fieldName)
.withInitializationString(tableFieldName + "." + fieldName)
.build();
outerObject.addNamedItem(prop);
}
// inner class
KotlinProperty property = KotlinProperty.newVal(fieldName)
.withInitializationString(calculateInnerInitializationString(column, kt))
.build();
innerClass.addNamedItem(property);
}
private String calculateInnerInitializationString(IntrospectedColumn column, FullyQualifiedKotlinType kt) {
StringBuilder initializationString = new StringBuilder();
initializationString.append(String.format("column<%s>(name = \"%s\", jdbcType = JDBCType.%s", //$NON-NLS-1$
kt.getShortNameWithTypeArguments(),
escapeStringForKotlin(getEscapedColumnName(column)),
column.getJdbcTypeName()));
column.getTypeHandler().ifPresent(
th -> initializationString.append(String.format(", typeHandler = \"%s\"", th))); //$NON-NLS-1$
if (StringUtility.isTrue(
column.getProperties().getProperty(PropertyRegistry.COLUMN_OVERRIDE_FORCE_JAVA_TYPE))) {
initializationString.append(
String.format(", javaType = %s::class", kt.getShortNameWithoutTypeArguments())); //$NON-NLS-1$
}
initializationString.append(
String.format(", javaProperty = \"%s\"", column.getJavaProperty())); //$NON-NLS-1$
initializationString.append(')'); //$NON-NLS-1$
return initializationString.toString();
}
public static class Builder extends AbstractGeneratorBuilder<Builder> {
@Override
protected Builder getThis() {
return this;
}
public KotlinDynamicSqlSupportClassGenerator build() {
return new KotlinDynamicSqlSupportClassGenerator(this);
}
}
}