View Javadoc
1   /*
2    *    Copyright 2006-2023 the original author or authors.
3    *
4    *    Licensed under the Apache License, Version 2.0 (the "License");
5    *    you may not use this file except in compliance with the License.
6    *    You may obtain a copy of the License at
7    *
8    *       https://www.apache.org/licenses/LICENSE-2.0
9    *
10   *    Unless required by applicable law or agreed to in writing, software
11   *    distributed under the License is distributed on an "AS IS" BASIS,
12   *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *    See the License for the specific language governing permissions and
14   *    limitations under the License.
15   */
16  package org.mybatis.generator.runtime.kotlin;
17  
18  import static org.mybatis.generator.codegen.mybatis3.MyBatis3FormattingUtilities.getEscapedColumnName;
19  import static org.mybatis.generator.internal.util.StringUtility.escapeStringForKotlin;
20  
21  import java.util.List;
22  import java.util.Objects;
23  
24  import org.mybatis.generator.api.IntrospectedColumn;
25  import org.mybatis.generator.api.IntrospectedTable;
26  import org.mybatis.generator.api.dom.java.FullyQualifiedJavaType;
27  import org.mybatis.generator.api.dom.kotlin.FullyQualifiedKotlinType;
28  import org.mybatis.generator.api.dom.kotlin.JavaToKotlinTypeConverter;
29  import org.mybatis.generator.api.dom.kotlin.KotlinFile;
30  import org.mybatis.generator.api.dom.kotlin.KotlinProperty;
31  import org.mybatis.generator.api.dom.kotlin.KotlinType;
32  import org.mybatis.generator.config.Context;
33  import org.mybatis.generator.config.PropertyRegistry;
34  import org.mybatis.generator.internal.util.JavaBeansUtil;
35  import org.mybatis.generator.internal.util.StringUtility;
36  import org.mybatis.generator.internal.util.messages.Messages;
37  
38  public class KotlinDynamicSqlSupportClassGenerator {
39      private final IntrospectedTable introspectedTable;
40      private final Context context;
41      private final List<String> warnings;
42      private KotlinFile kotlinFile;
43      private KotlinType innerClass;
44      private KotlinType outerObject;
45      private KotlinProperty tableProperty;
46  
47      public KotlinDynamicSqlSupportClassGenerator(Context context, IntrospectedTable introspectedTable,
48                                                   List<String> warnings) {
49          this.introspectedTable = Objects.requireNonNull(introspectedTable);
50          this.context = Objects.requireNonNull(context);
51          this.warnings = Objects.requireNonNull(warnings);
52          generate();
53      }
54  
55      private void generate() {
56          FullyQualifiedJavaType type =
57                  new FullyQualifiedJavaType(introspectedTable.getMyBatisDynamicSqlSupportType());
58  
59          kotlinFile = buildBasicFile(type);
60  
61          outerObject = buildOuterObject(kotlinFile, type);
62  
63          tableProperty = calculateTableProperty();
64          outerObject.addNamedItem(tableProperty);
65  
66          innerClass = buildInnerClass();
67  
68          List<IntrospectedColumn> columns = introspectedTable.getAllColumns();
69          for (IntrospectedColumn column : columns) {
70              handleColumn(kotlinFile, outerObject, innerClass, getTablePropertyName(), column);
71          }
72  
73          outerObject.addNamedItem(innerClass);
74      }
75  
76      public KotlinFile getKotlinFile() {
77          return kotlinFile;
78      }
79  
80      public String getTablePropertyName() {
81          return tableProperty.getName();
82      }
83  
84      public KotlinType getInnerClass() {
85          return innerClass;
86      }
87  
88      public KotlinType getOuterObject() {
89          return outerObject;
90      }
91  
92      public String getTablePropertyImport() {
93          return getSupportObjectImport()
94                  + "." //$NON-NLS-1$
95                  + tableProperty.getName();
96      }
97  
98      public String getSupportObjectImport() {
99          return kotlinFile.getPackage().map(s -> s + ".").orElse("") //$NON-NLS-1$ //$NON-NLS-2$
100                 + outerObject.getName();
101     }
102 
103     private KotlinFile buildBasicFile(FullyQualifiedJavaType type) {
104         KotlinFile kf = new KotlinFile(type.getShortNameWithoutTypeArguments());
105         kf.setPackage(type.getPackageName());
106         context.getCommentGenerator().addFileComment(kf);
107 
108         return kf;
109     }
110 
111     private KotlinType buildOuterObject(KotlinFile kotlinFile, FullyQualifiedJavaType type) {
112         KotlinType outerObject = KotlinType.newObject(type.getShortNameWithoutTypeArguments())
113                 .build();
114 
115         kotlinFile.addImport("org.mybatis.dynamic.sql.AliasableSqlTable"); //$NON-NLS-1$
116         kotlinFile.addImport("org.mybatis.dynamic.sql.util.kotlin.elements.column"); //$NON-NLS-1$
117         kotlinFile.addImport("java.sql.JDBCType"); //$NON-NLS-1$
118         kotlinFile.addNamedItem(outerObject);
119         return outerObject;
120     }
121 
122 
123     private KotlinType buildInnerClass() {
124         String domainObjectName = introspectedTable.getMyBatisDynamicSQLTableObjectName();
125 
126         return KotlinType.newClass(domainObjectName)
127                 .withSuperType("AliasableSqlTable<" + domainObjectName + ">(\"" //$NON-NLS-1$ //$NON-NLS-2$
128                         + escapeStringForKotlin(introspectedTable.getFullyQualifiedTableNameAtRuntime())
129                         + "\", ::" + domainObjectName //$NON-NLS-1$
130                         + ")") //$NON-NLS-1$
131                 .build();
132     }
133 
134     private KotlinProperty calculateTableProperty() {
135         String tableType = introspectedTable.getMyBatisDynamicSQLTableObjectName();
136         String fieldName =
137                 JavaBeansUtil.getValidPropertyName(introspectedTable.getMyBatisDynamicSQLTableObjectName());
138 
139         return KotlinProperty.newVal(fieldName)
140                 .withInitializationString(tableType + "()") //$NON-NLS-1$
141                 .build();
142     }
143 
144     private void handleColumn(KotlinFile kotlinFile, KotlinType outerObject, KotlinType innerClass,
145             String tableFieldName, IntrospectedColumn column) {
146 
147         FullyQualifiedKotlinType kt = JavaToKotlinTypeConverter.convert(column.getFullyQualifiedJavaType());
148 
149         kotlinFile.addImports(kt.getImportList());
150 
151         String fieldName = column.getJavaProperty();
152 
153         // outer object
154         if (fieldName.equals(tableFieldName)) {
155             // name collision, skip the shortcut field
156             warnings.add(
157                     Messages.getString("Warning.29", //$NON-NLS-1$
158                             fieldName, getSupportObjectImport()));
159         } else {
160             KotlinProperty prop = KotlinProperty.newVal(fieldName)
161                     .withInitializationString(tableFieldName + "." + fieldName)
162                     .build();
163             outerObject.addNamedItem(prop);
164         }
165 
166 
167         // inner class
168         KotlinProperty property = KotlinProperty.newVal(fieldName)
169                 .withInitializationString(calculateInnerInitializationString(column, kt))
170                 .build();
171 
172         innerClass.addNamedItem(property);
173     }
174 
175     private String calculateInnerInitializationString(IntrospectedColumn column, FullyQualifiedKotlinType kt) {
176         StringBuilder initializationString = new StringBuilder();
177 
178         initializationString.append(String.format("column<%s>(name = \"%s\", jdbcType = JDBCType.%s", //$NON-NLS-1$
179                 kt.getShortNameWithTypeArguments(),
180                 escapeStringForKotlin(getEscapedColumnName(column)),
181                 column.getJdbcTypeName()));
182 
183         if (StringUtility.stringHasValue(column.getTypeHandler())) {
184             initializationString.append(
185                     String.format(", typeHandler = \"%s\"", column.getTypeHandler())); //$NON-NLS-1$
186         }
187 
188         if (StringUtility.isTrue(
189                 column.getProperties().getProperty(PropertyRegistry.COLUMN_OVERRIDE_FORCE_JAVA_TYPE))) {
190             initializationString.append(
191                     String.format(", javaType = %s::class", kt.getShortNameWithoutTypeArguments())); //$NON-NLS-1$
192         }
193 
194         initializationString.append(')'); //$NON-NLS-1$
195 
196         return initializationString.toString();
197     }
198 }