View Javadoc
1   /*
2    *    Copyright 2006-2025 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.api;
17  
18  import static org.mybatis.generator.internal.util.StringUtility.isTrue;
19  import static org.mybatis.generator.internal.util.StringUtility.stringHasValue;
20  
21  import java.util.ArrayList;
22  import java.util.EnumMap;
23  import java.util.HashMap;
24  import java.util.Iterator;
25  import java.util.List;
26  import java.util.Map;
27  import java.util.Optional;
28  import java.util.Properties;
29  import java.util.function.Function;
30  import java.util.stream.Stream;
31  
32  import org.mybatis.generator.config.Context;
33  import org.mybatis.generator.config.GeneratedKey;
34  import org.mybatis.generator.config.JavaClientGeneratorConfiguration;
35  import org.mybatis.generator.config.JavaModelGeneratorConfiguration;
36  import org.mybatis.generator.config.ModelType;
37  import org.mybatis.generator.config.PropertyHolder;
38  import org.mybatis.generator.config.PropertyRegistry;
39  import org.mybatis.generator.config.SqlMapGeneratorConfiguration;
40  import org.mybatis.generator.config.TableConfiguration;
41  import org.mybatis.generator.internal.rules.ConditionalModelRules;
42  import org.mybatis.generator.internal.rules.FlatModelRules;
43  import org.mybatis.generator.internal.rules.HierarchicalModelRules;
44  import org.mybatis.generator.internal.rules.Rules;
45  
46  /**
47   * Base class for all code generator implementations. This class provides many
48   * of the housekeeping methods needed to implement a code generator, with only
49   * the actual code generation methods left unimplemented.
50   *
51   * @author Jeff Butler
52   */
53  public abstract class IntrospectedTable {
54  
55      public enum TargetRuntime {
56          MYBATIS3,
57          MYBATIS3_DSQL
58      }
59  
60      protected enum InternalAttribute {
61          ATTR_PRIMARY_KEY_TYPE,
62          ATTR_BASE_RECORD_TYPE,
63          ATTR_RECORD_WITH_BLOBS_TYPE,
64          ATTR_EXAMPLE_TYPE,
65          ATTR_MYBATIS3_XML_MAPPER_PACKAGE,
66          ATTR_MYBATIS3_XML_MAPPER_FILE_NAME,
67          /** also used as XML Mapper namespace if a Java mapper is generated. */
68          ATTR_MYBATIS3_JAVA_MAPPER_TYPE,
69          /** used as XML Mapper namespace if no client is generated. */
70          ATTR_MYBATIS3_FALLBACK_SQL_MAP_NAMESPACE,
71          ATTR_FULLY_QUALIFIED_TABLE_NAME_AT_RUNTIME,
72          ATTR_ALIASED_FULLY_QUALIFIED_TABLE_NAME_AT_RUNTIME,
73          ATTR_COUNT_BY_EXAMPLE_STATEMENT_ID,
74          ATTR_DELETE_BY_EXAMPLE_STATEMENT_ID,
75          ATTR_DELETE_BY_PRIMARY_KEY_STATEMENT_ID,
76          ATTR_INSERT_STATEMENT_ID,
77          ATTR_INSERT_SELECTIVE_STATEMENT_ID,
78          ATTR_SELECT_ALL_STATEMENT_ID,
79          ATTR_SELECT_BY_EXAMPLE_STATEMENT_ID,
80          ATTR_SELECT_BY_EXAMPLE_WITH_BLOBS_STATEMENT_ID,
81          ATTR_SELECT_BY_PRIMARY_KEY_STATEMENT_ID,
82          ATTR_UPDATE_BY_EXAMPLE_STATEMENT_ID,
83          ATTR_UPDATE_BY_EXAMPLE_SELECTIVE_STATEMENT_ID,
84          ATTR_UPDATE_BY_EXAMPLE_WITH_BLOBS_STATEMENT_ID,
85          ATTR_UPDATE_BY_PRIMARY_KEY_STATEMENT_ID,
86          ATTR_UPDATE_BY_PRIMARY_KEY_SELECTIVE_STATEMENT_ID,
87          ATTR_UPDATE_BY_PRIMARY_KEY_WITH_BLOBS_STATEMENT_ID,
88          ATTR_BASE_RESULT_MAP_ID,
89          ATTR_RESULT_MAP_WITH_BLOBS_ID,
90          ATTR_EXAMPLE_WHERE_CLAUSE_ID,
91          ATTR_BASE_COLUMN_LIST_ID,
92          ATTR_BLOB_COLUMN_LIST_ID,
93          ATTR_MYBATIS3_UPDATE_BY_EXAMPLE_WHERE_CLAUSE_ID,
94          ATTR_MYBATIS3_SQL_PROVIDER_TYPE,
95          ATTR_MYBATIS_DYNAMIC_SQL_SUPPORT_TYPE,
96          ATTR_KOTLIN_RECORD_TYPE,
97          ATTR_MYBATIS_DYNAMIC_SQL_TABLE_OBJECT_NAME
98      }
99  
100     protected TableConfiguration tableConfiguration;
101 
102     protected FullyQualifiedTable fullyQualifiedTable;
103 
104     protected Context context;
105 
106     protected Rules rules;
107 
108     protected final List<IntrospectedColumn> primaryKeyColumns = new ArrayList<>();
109 
110     protected final List<IntrospectedColumn> baseColumns = new ArrayList<>();
111 
112     protected final List<IntrospectedColumn> blobColumns = new ArrayList<>();
113 
114     protected TargetRuntime targetRuntime;
115 
116     /**
117      * Attributes may be used by plugins to capture table related state between
118      * the different plugin calls.
119      */
120     protected final Map<String, Object> attributes = new HashMap<>();
121 
122     /** Internal attributes are used to store commonly accessed items by all code generators. */
123     protected final Map<IntrospectedTable.InternalAttribute, String> internalAttributes =
124             new EnumMap<>(InternalAttribute.class);
125 
126     /**
127      * Table remarks retrieved from database metadata.
128      */
129     protected String remarks;
130 
131     /**
132      * Table type retrieved from database metadata.
133      */
134     protected String tableType;
135 
136     protected IntrospectedTable(TargetRuntime targetRuntime) {
137         this.targetRuntime = targetRuntime;
138     }
139 
140     public FullyQualifiedTable getFullyQualifiedTable() {
141         return fullyQualifiedTable;
142     }
143 
144     public String getSelectByExampleQueryId() {
145         return tableConfiguration.getSelectByExampleQueryId();
146     }
147 
148     public String getSelectByPrimaryKeyQueryId() {
149         return tableConfiguration.getSelectByPrimaryKeyQueryId();
150     }
151 
152     public Optional<GeneratedKey> getGeneratedKey() {
153         return tableConfiguration.getGeneratedKey();
154     }
155 
156     public Optional<IntrospectedColumn> getColumn(String columnName) {
157         return Stream.of(primaryKeyColumns.stream(), baseColumns.stream(), blobColumns.stream())
158                 .flatMap(Function.identity())
159                 .filter(ic -> columnMatches(ic, columnName))
160                 .findFirst();
161     }
162 
163     private boolean columnMatches(IntrospectedColumn introspectedColumn, String columnName) {
164         if (introspectedColumn.isColumnNameDelimited()) {
165             return introspectedColumn.getActualColumnName().equals(columnName);
166         } else {
167             return introspectedColumn.getActualColumnName().equalsIgnoreCase(columnName);
168         }
169     }
170 
171     /**
172      * Returns true if any of the columns in the table are JDBC Dates (as
173      * opposed to timestamps).
174      *
175      * @return true if the table contains DATE columns
176      */
177     public boolean hasJDBCDateColumns() {
178         return Stream.of(primaryKeyColumns.stream(), baseColumns.stream())
179                 .flatMap(Function.identity())
180                 .anyMatch(IntrospectedColumn::isJDBCDateColumn);
181     }
182 
183     /**
184      * Returns true if any of the columns in the table are JDBC Times (as
185      * opposed to timestamps).
186      *
187      * @return true if the table contains TIME columns
188      */
189     public boolean hasJDBCTimeColumns() {
190         return Stream.of(primaryKeyColumns.stream(), baseColumns.stream())
191                 .flatMap(Function.identity())
192                 .anyMatch(IntrospectedColumn::isJDBCTimeColumn);
193     }
194 
195     /**
196      * Returns the columns in the primary key. If the generatePrimaryKeyClass()
197      * method returns false, then these columns will be iterated as the
198      * parameters of the selectByPrimaryKay and deleteByPrimaryKey methods
199      *
200      * @return a List of ColumnDefinition objects for columns in the primary key
201      */
202     public List<IntrospectedColumn> getPrimaryKeyColumns() {
203         return primaryKeyColumns;
204     }
205 
206     public boolean hasPrimaryKeyColumns() {
207         return !primaryKeyColumns.isEmpty();
208     }
209 
210     public List<IntrospectedColumn> getBaseColumns() {
211         return baseColumns;
212     }
213 
214     /**
215      * Returns all columns in the table (for use by the select by primary key and
216      * select by example with BLOBs methods).
217      *
218      * @return a List of ColumnDefinition objects for all columns in the table
219      */
220     public List<IntrospectedColumn> getAllColumns() {
221         return Stream.of(primaryKeyColumns.stream(), baseColumns.stream(), blobColumns.stream())
222                 .flatMap(Function.identity())
223                 .toList();
224     }
225 
226     /**
227      * Returns all columns except BLOBs (for use by the select by example without BLOBs method).
228      *
229      * @return a List of ColumnDefinition objects for columns in the table that are non BLOBs
230      */
231     public List<IntrospectedColumn> getNonBLOBColumns() {
232         return Stream.of(primaryKeyColumns.stream(), baseColumns.stream())
233                 .flatMap(Function.identity())
234                 .toList();
235     }
236 
237     public int getNonBLOBColumnCount() {
238         return primaryKeyColumns.size() + baseColumns.size();
239     }
240 
241     public List<IntrospectedColumn> getNonPrimaryKeyColumns() {
242         return Stream.of(baseColumns.stream(), blobColumns.stream())
243                 .flatMap(Function.identity())
244                 .toList();
245     }
246 
247     public List<IntrospectedColumn> getBLOBColumns() {
248         return blobColumns;
249     }
250 
251     public boolean hasBLOBColumns() {
252         return !blobColumns.isEmpty();
253     }
254 
255     public boolean hasBaseColumns() {
256         return !baseColumns.isEmpty();
257     }
258 
259     public Rules getRules() {
260         return rules;
261     }
262 
263     public String getTableConfigurationProperty(String property) {
264         return tableConfiguration.getProperty(property);
265     }
266 
267     public String getPrimaryKeyType() {
268         return internalAttributes.get(InternalAttribute.ATTR_PRIMARY_KEY_TYPE);
269     }
270 
271     /**
272      * Gets the base record type.
273      *
274      * @return the type for the record (the class that holds non-primary key and non-BLOB fields). Note that the value
275      *         will be calculated regardless of whether the table has these columns or not.
276      */
277     public String getBaseRecordType() {
278         return internalAttributes.get(InternalAttribute.ATTR_BASE_RECORD_TYPE);
279     }
280 
281     public String getKotlinRecordType() {
282         return internalAttributes.get(InternalAttribute.ATTR_KOTLIN_RECORD_TYPE);
283     }
284 
285     /**
286      * Gets the example type.
287      *
288      * @return the type for the example class.
289      */
290     public String getExampleType() {
291         return internalAttributes.get(InternalAttribute.ATTR_EXAMPLE_TYPE);
292     }
293 
294     /**
295      * Gets the record with blo bs type.
296      *
297      * @return the type for the record with BLOBs class. Note that the value will be calculated regardless of whether
298      *         the table has BLOB columns or not.
299      */
300     public String getRecordWithBLOBsType() {
301         return internalAttributes
302                 .get(InternalAttribute.ATTR_RECORD_WITH_BLOBS_TYPE);
303     }
304 
305     public String getMyBatis3SqlMapNamespace() {
306         String namespace = getMyBatis3JavaMapperType();
307         if (namespace == null) {
308             namespace = getMyBatis3FallbackSqlMapNamespace();
309         }
310 
311         return namespace;
312     }
313 
314     public String getMyBatis3FallbackSqlMapNamespace() {
315         return internalAttributes
316                 .get(InternalAttribute.ATTR_MYBATIS3_FALLBACK_SQL_MAP_NAMESPACE);
317     }
318 
319     public boolean hasAnyColumns() {
320         return hasPrimaryKeyColumns() || hasBaseColumns() || hasBLOBColumns();
321     }
322 
323     public void setTableConfiguration(TableConfiguration tableConfiguration) {
324         this.tableConfiguration = tableConfiguration;
325     }
326 
327     public void setFullyQualifiedTable(FullyQualifiedTable fullyQualifiedTable) {
328         this.fullyQualifiedTable = fullyQualifiedTable;
329     }
330 
331     public void setContext(Context context) {
332         this.context = context;
333     }
334 
335     public void addColumn(IntrospectedColumn introspectedColumn) {
336         if (introspectedColumn.isBLOBColumn()) {
337             blobColumns.add(introspectedColumn);
338         } else {
339             baseColumns.add(introspectedColumn);
340         }
341 
342         introspectedColumn.setIntrospectedTable(this);
343     }
344 
345     public void addPrimaryKeyColumn(String columnName) {
346         boolean found = false;
347         // first search base columns
348         Iterator<IntrospectedColumn> iter = baseColumns.iterator();
349         while (iter.hasNext()) {
350             IntrospectedColumn introspectedColumn = iter.next();
351             if (introspectedColumn.getActualColumnName().equals(columnName)) {
352                 primaryKeyColumns.add(introspectedColumn);
353                 iter.remove();
354                 found = true;
355                 break;
356             }
357         }
358 
359         // search blob columns in the weird event that a blob is the primary key
360         if (!found) {
361             iter = blobColumns.iterator();
362             while (iter.hasNext()) {
363                 IntrospectedColumn introspectedColumn = iter.next();
364                 if (introspectedColumn.getActualColumnName().equals(columnName)) {
365                     primaryKeyColumns.add(introspectedColumn);
366                     iter.remove();
367                     break;
368                 }
369             }
370         }
371     }
372 
373     public Object getAttribute(String name) {
374         return attributes.get(name);
375     }
376 
377     public void removeAttribute(String name) {
378         attributes.remove(name);
379     }
380 
381     public void setAttribute(String name, Object value) {
382         attributes.put(name, value);
383     }
384 
385     public void initialize() {
386         calculateJavaClientAttributes();
387         calculateModelAttributes();
388         calculateXmlAttributes();
389 
390         if (tableConfiguration.getModelType() == ModelType.HIERARCHICAL) {
391             rules = new HierarchicalModelRules(this);
392         } else if (tableConfiguration.getModelType() == ModelType.FLAT) {
393             rules = new FlatModelRules(this);
394         } else {
395             rules = new ConditionalModelRules(this);
396         }
397 
398         context.getPlugins().initialized(this);
399     }
400 
401     protected void calculateXmlAttributes() {
402         setMyBatis3XmlMapperFileName(calculateMyBatis3XmlMapperFileName());
403         setMyBatis3XmlMapperPackage(calculateSqlMapPackage());
404 
405         setMyBatis3FallbackSqlMapNamespace(calculateMyBatis3FallbackSqlMapNamespace());
406 
407         setSqlMapFullyQualifiedRuntimeTableName(calculateSqlMapFullyQualifiedRuntimeTableName());
408         setSqlMapAliasedFullyQualifiedRuntimeTableName(calculateSqlMapAliasedFullyQualifiedRuntimeTableName());
409 
410         setCountByExampleStatementId("countByExample"); //$NON-NLS-1$
411         setDeleteByExampleStatementId("deleteByExample"); //$NON-NLS-1$
412         setDeleteByPrimaryKeyStatementId("deleteByPrimaryKey"); //$NON-NLS-1$
413         setInsertStatementId("insert"); //$NON-NLS-1$
414         setInsertSelectiveStatementId("insertSelective"); //$NON-NLS-1$
415         setSelectAllStatementId("selectAll"); //$NON-NLS-1$
416         setSelectByExampleStatementId("selectByExample"); //$NON-NLS-1$
417         setSelectByExampleWithBLOBsStatementId("selectByExampleWithBLOBs"); //$NON-NLS-1$
418         setSelectByPrimaryKeyStatementId("selectByPrimaryKey"); //$NON-NLS-1$
419         setUpdateByExampleStatementId("updateByExample"); //$NON-NLS-1$
420         setUpdateByExampleSelectiveStatementId("updateByExampleSelective"); //$NON-NLS-1$
421         setUpdateByExampleWithBLOBsStatementId("updateByExampleWithBLOBs"); //$NON-NLS-1$
422         setUpdateByPrimaryKeyStatementId("updateByPrimaryKey"); //$NON-NLS-1$
423         setUpdateByPrimaryKeySelectiveStatementId("updateByPrimaryKeySelective"); //$NON-NLS-1$
424         setUpdateByPrimaryKeyWithBLOBsStatementId("updateByPrimaryKeyWithBLOBs"); //$NON-NLS-1$
425         setBaseResultMapId("BaseResultMap"); //$NON-NLS-1$
426         setResultMapWithBLOBsId("ResultMapWithBLOBs"); //$NON-NLS-1$
427         setExampleWhereClauseId("Example_Where_Clause"); //$NON-NLS-1$
428         setBaseColumnListId("Base_Column_List"); //$NON-NLS-1$
429         setBlobColumnListId("Blob_Column_List"); //$NON-NLS-1$
430         setMyBatis3UpdateByExampleWhereClauseId("Update_By_Example_Where_Clause"); //$NON-NLS-1$
431     }
432 
433     public void setBlobColumnListId(String s) {
434         internalAttributes.put(InternalAttribute.ATTR_BLOB_COLUMN_LIST_ID, s);
435     }
436 
437     public void setBaseColumnListId(String s) {
438         internalAttributes.put(InternalAttribute.ATTR_BASE_COLUMN_LIST_ID, s);
439     }
440 
441     public void setExampleWhereClauseId(String s) {
442         internalAttributes.put(InternalAttribute.ATTR_EXAMPLE_WHERE_CLAUSE_ID,
443                 s);
444     }
445 
446     public void setMyBatis3UpdateByExampleWhereClauseId(String s) {
447         internalAttributes.put(
448                 InternalAttribute.ATTR_MYBATIS3_UPDATE_BY_EXAMPLE_WHERE_CLAUSE_ID,
449                 s);
450     }
451 
452     public void setResultMapWithBLOBsId(String s) {
453         internalAttributes.put(InternalAttribute.ATTR_RESULT_MAP_WITH_BLOBS_ID,
454                 s);
455     }
456 
457     public void setBaseResultMapId(String s) {
458         internalAttributes.put(InternalAttribute.ATTR_BASE_RESULT_MAP_ID, s);
459     }
460 
461     public void setUpdateByPrimaryKeyWithBLOBsStatementId(String s) {
462         internalAttributes.put(
463                 InternalAttribute.ATTR_UPDATE_BY_PRIMARY_KEY_WITH_BLOBS_STATEMENT_ID,
464                 s);
465     }
466 
467     public void setUpdateByPrimaryKeySelectiveStatementId(String s) {
468         internalAttributes
469                 .put(
470                         InternalAttribute.ATTR_UPDATE_BY_PRIMARY_KEY_SELECTIVE_STATEMENT_ID,
471                         s);
472     }
473 
474     public void setUpdateByPrimaryKeyStatementId(String s) {
475         internalAttributes.put(
476                 InternalAttribute.ATTR_UPDATE_BY_PRIMARY_KEY_STATEMENT_ID, s);
477     }
478 
479     public void setUpdateByExampleWithBLOBsStatementId(String s) {
480         internalAttributes.put(
481                 InternalAttribute.ATTR_UPDATE_BY_EXAMPLE_WITH_BLOBS_STATEMENT_ID,
482                 s);
483     }
484 
485     public void setUpdateByExampleSelectiveStatementId(String s) {
486         internalAttributes
487                 .put(
488                         InternalAttribute.ATTR_UPDATE_BY_EXAMPLE_SELECTIVE_STATEMENT_ID,
489                         s);
490     }
491 
492     public void setUpdateByExampleStatementId(String s) {
493         internalAttributes.put(
494                 InternalAttribute.ATTR_UPDATE_BY_EXAMPLE_STATEMENT_ID, s);
495     }
496 
497     public void setSelectByPrimaryKeyStatementId(String s) {
498         internalAttributes.put(
499                 InternalAttribute.ATTR_SELECT_BY_PRIMARY_KEY_STATEMENT_ID, s);
500     }
501 
502     public void setSelectByExampleWithBLOBsStatementId(String s) {
503         internalAttributes
504                 .put(
505                         InternalAttribute.ATTR_SELECT_BY_EXAMPLE_WITH_BLOBS_STATEMENT_ID,
506                         s);
507     }
508 
509     public void setSelectAllStatementId(String s) {
510         internalAttributes.put(
511                 InternalAttribute.ATTR_SELECT_ALL_STATEMENT_ID, s);
512     }
513 
514     public void setSelectByExampleStatementId(String s) {
515         internalAttributes.put(
516                 InternalAttribute.ATTR_SELECT_BY_EXAMPLE_STATEMENT_ID, s);
517     }
518 
519     public void setInsertSelectiveStatementId(String s) {
520         internalAttributes.put(
521                 InternalAttribute.ATTR_INSERT_SELECTIVE_STATEMENT_ID, s);
522     }
523 
524     public void setInsertStatementId(String s) {
525         internalAttributes.put(InternalAttribute.ATTR_INSERT_STATEMENT_ID, s);
526     }
527 
528     public void setDeleteByPrimaryKeyStatementId(String s) {
529         internalAttributes.put(
530                 InternalAttribute.ATTR_DELETE_BY_PRIMARY_KEY_STATEMENT_ID, s);
531     }
532 
533     public void setDeleteByExampleStatementId(String s) {
534         internalAttributes.put(
535                 InternalAttribute.ATTR_DELETE_BY_EXAMPLE_STATEMENT_ID, s);
536     }
537 
538     public void setCountByExampleStatementId(String s) {
539         internalAttributes.put(
540                 InternalAttribute.ATTR_COUNT_BY_EXAMPLE_STATEMENT_ID, s);
541     }
542 
543     public String getBlobColumnListId() {
544         return internalAttributes
545                 .get(InternalAttribute.ATTR_BLOB_COLUMN_LIST_ID);
546     }
547 
548     public String getBaseColumnListId() {
549         return internalAttributes
550                 .get(InternalAttribute.ATTR_BASE_COLUMN_LIST_ID);
551     }
552 
553     public String getExampleWhereClauseId() {
554         return internalAttributes
555                 .get(InternalAttribute.ATTR_EXAMPLE_WHERE_CLAUSE_ID);
556     }
557 
558     public String getMyBatis3UpdateByExampleWhereClauseId() {
559         return internalAttributes
560                 .get(InternalAttribute.ATTR_MYBATIS3_UPDATE_BY_EXAMPLE_WHERE_CLAUSE_ID);
561     }
562 
563     public String getResultMapWithBLOBsId() {
564         return internalAttributes
565                 .get(InternalAttribute.ATTR_RESULT_MAP_WITH_BLOBS_ID);
566     }
567 
568     public String getBaseResultMapId() {
569         return internalAttributes
570                 .get(InternalAttribute.ATTR_BASE_RESULT_MAP_ID);
571     }
572 
573     public String getUpdateByPrimaryKeyWithBLOBsStatementId() {
574         return internalAttributes
575                 .get(InternalAttribute.ATTR_UPDATE_BY_PRIMARY_KEY_WITH_BLOBS_STATEMENT_ID);
576     }
577 
578     public String getUpdateByPrimaryKeySelectiveStatementId() {
579         return internalAttributes
580                 .get(InternalAttribute.ATTR_UPDATE_BY_PRIMARY_KEY_SELECTIVE_STATEMENT_ID);
581     }
582 
583     public String getUpdateByPrimaryKeyStatementId() {
584         return internalAttributes
585                 .get(InternalAttribute.ATTR_UPDATE_BY_PRIMARY_KEY_STATEMENT_ID);
586     }
587 
588     public String getUpdateByExampleWithBLOBsStatementId() {
589         return internalAttributes
590                 .get(InternalAttribute.ATTR_UPDATE_BY_EXAMPLE_WITH_BLOBS_STATEMENT_ID);
591     }
592 
593     public String getUpdateByExampleSelectiveStatementId() {
594         return internalAttributes
595                 .get(InternalAttribute.ATTR_UPDATE_BY_EXAMPLE_SELECTIVE_STATEMENT_ID);
596     }
597 
598     public String getUpdateByExampleStatementId() {
599         return internalAttributes
600                 .get(InternalAttribute.ATTR_UPDATE_BY_EXAMPLE_STATEMENT_ID);
601     }
602 
603     public String getSelectByPrimaryKeyStatementId() {
604         return internalAttributes
605                 .get(InternalAttribute.ATTR_SELECT_BY_PRIMARY_KEY_STATEMENT_ID);
606     }
607 
608     public String getSelectByExampleWithBLOBsStatementId() {
609         return internalAttributes
610                 .get(InternalAttribute.ATTR_SELECT_BY_EXAMPLE_WITH_BLOBS_STATEMENT_ID);
611     }
612 
613     public String getSelectAllStatementId() {
614         return internalAttributes
615                 .get(InternalAttribute.ATTR_SELECT_ALL_STATEMENT_ID);
616     }
617 
618     public String getSelectByExampleStatementId() {
619         return internalAttributes
620                 .get(InternalAttribute.ATTR_SELECT_BY_EXAMPLE_STATEMENT_ID);
621     }
622 
623     public String getInsertSelectiveStatementId() {
624         return internalAttributes
625                 .get(InternalAttribute.ATTR_INSERT_SELECTIVE_STATEMENT_ID);
626     }
627 
628     public String getInsertStatementId() {
629         return internalAttributes
630                 .get(InternalAttribute.ATTR_INSERT_STATEMENT_ID);
631     }
632 
633     public String getDeleteByPrimaryKeyStatementId() {
634         return internalAttributes
635                 .get(InternalAttribute.ATTR_DELETE_BY_PRIMARY_KEY_STATEMENT_ID);
636     }
637 
638     public String getDeleteByExampleStatementId() {
639         return internalAttributes
640                 .get(InternalAttribute.ATTR_DELETE_BY_EXAMPLE_STATEMENT_ID);
641     }
642 
643     public String getCountByExampleStatementId() {
644         return internalAttributes
645                 .get(InternalAttribute.ATTR_COUNT_BY_EXAMPLE_STATEMENT_ID);
646     }
647 
648     public String getMyBatisDynamicSQLTableObjectName() {
649         return internalAttributes.get(InternalAttribute.ATTR_MYBATIS_DYNAMIC_SQL_TABLE_OBJECT_NAME);
650     }
651 
652     public void setMyBatisDynamicSQLTableObjectName(String name) {
653         internalAttributes.put(InternalAttribute.ATTR_MYBATIS_DYNAMIC_SQL_TABLE_OBJECT_NAME, name);
654     }
655 
656     private boolean isSubPackagesEnabled(PropertyHolder propertyHolder) {
657         return isTrue(propertyHolder.getProperty(PropertyRegistry.ANY_ENABLE_SUB_PACKAGES));
658     }
659 
660     protected String calculateJavaClientInterfacePackage() {
661         JavaClientGeneratorConfiguration config = context
662                 .getJavaClientGeneratorConfiguration();
663         if (config == null) {
664             return null;
665         }
666 
667         return config.getTargetPackage()
668                 + fullyQualifiedTable.getSubPackageForClientOrSqlMap(isSubPackagesEnabled(config));
669     }
670 
671     protected String calculateDynamicSqlSupportPackage() {
672         JavaClientGeneratorConfiguration config = context
673                 .getJavaClientGeneratorConfiguration();
674         if (config == null) {
675             return null;
676         }
677 
678         String packkage = config.getProperty(PropertyRegistry.CLIENT_DYNAMIC_SQL_SUPPORT_PACKAGE);
679         if (stringHasValue(packkage)) {
680             return packkage + fullyQualifiedTable.getSubPackageForClientOrSqlMap(isSubPackagesEnabled(config));
681         } else {
682             return calculateJavaClientInterfacePackage();
683         }
684     }
685 
686     protected void calculateJavaClientAttributes() {
687         if (context.getJavaClientGeneratorConfiguration() == null) {
688             return;
689         }
690 
691         StringBuilder sb = new StringBuilder();
692         sb.append(calculateJavaClientInterfacePackage());
693         sb.append('.');
694         if (stringHasValue(tableConfiguration.getMapperName())) {
695             sb.append(tableConfiguration.getMapperName());
696         } else {
697             if (stringHasValue(fullyQualifiedTable.getDomainObjectSubPackage())) {
698                 sb.append(fullyQualifiedTable.getDomainObjectSubPackage());
699                 sb.append('.');
700             }
701             sb.append(fullyQualifiedTable.getDomainObjectName());
702             sb.append("Mapper"); //$NON-NLS-1$
703         }
704         setMyBatis3JavaMapperType(sb.toString());
705 
706         sb.setLength(0);
707         sb.append(calculateJavaClientInterfacePackage());
708         sb.append('.');
709         if (stringHasValue(tableConfiguration.getSqlProviderName())) {
710             sb.append(tableConfiguration.getSqlProviderName());
711         } else {
712             if (stringHasValue(fullyQualifiedTable.getDomainObjectSubPackage())) {
713                 sb.append(fullyQualifiedTable.getDomainObjectSubPackage());
714                 sb.append('.');
715             }
716             sb.append(fullyQualifiedTable.getDomainObjectName());
717             sb.append("SqlProvider"); //$NON-NLS-1$
718         }
719         setMyBatis3SqlProviderType(sb.toString());
720 
721         sb.setLength(0);
722         sb.append(calculateDynamicSqlSupportPackage());
723         sb.append('.');
724         if (stringHasValue(tableConfiguration.getDynamicSqlSupportClassName())) {
725             sb.append(tableConfiguration.getDynamicSqlSupportClassName());
726         } else {
727             if (stringHasValue(fullyQualifiedTable.getDomainObjectSubPackage())) {
728                 sb.append(fullyQualifiedTable.getDomainObjectSubPackage());
729                 sb.append('.');
730             }
731             sb.append(fullyQualifiedTable.getDomainObjectName());
732             sb.append("DynamicSqlSupport"); //$NON-NLS-1$
733         }
734         setMyBatisDynamicSqlSupportType(sb.toString());
735 
736         if (stringHasValue(tableConfiguration.getDynamicSqlTableObjectName())) {
737             setMyBatisDynamicSQLTableObjectName(tableConfiguration.getDynamicSqlTableObjectName());
738         } else {
739             setMyBatisDynamicSQLTableObjectName(fullyQualifiedTable.getDomainObjectName());
740         }
741     }
742 
743     protected String calculateJavaModelPackage() {
744         JavaModelGeneratorConfiguration config = context
745                 .getJavaModelGeneratorConfiguration();
746 
747         return config.getTargetPackage()
748                 + fullyQualifiedTable.getSubPackageForModel(isSubPackagesEnabled(config));
749     }
750 
751     protected void calculateModelAttributes() {
752         String pakkage = calculateJavaModelPackage();
753 
754         StringBuilder sb = new StringBuilder();
755         sb.append(pakkage);
756         sb.append('.');
757         sb.append(fullyQualifiedTable.getDomainObjectName());
758         sb.append("Key"); //$NON-NLS-1$
759         setPrimaryKeyType(sb.toString());
760 
761         sb.setLength(0);
762         sb.append(pakkage);
763         sb.append('.');
764         sb.append(fullyQualifiedTable.getDomainObjectName());
765         setBaseRecordType(sb.toString());
766 
767         sb.setLength(0);
768         sb.append(pakkage);
769         sb.append('.');
770         sb.append(fullyQualifiedTable.getDomainObjectName());
771         setKotlinRecordType(sb.toString());
772 
773         sb.setLength(0);
774         sb.append(pakkage);
775         sb.append('.');
776         sb.append(fullyQualifiedTable.getDomainObjectName());
777         sb.append("WithBLOBs"); //$NON-NLS-1$
778         setRecordWithBLOBsType(sb.toString());
779 
780         String exampleTargetPackage = calculateJavaModelExamplePackage();
781         sb.setLength(0);
782         sb.append(exampleTargetPackage);
783         sb.append('.');
784         sb.append(fullyQualifiedTable.getDomainObjectName());
785         sb.append("Example"); //$NON-NLS-1$
786         setExampleType(sb.toString());
787     }
788 
789     /**
790      * If property exampleTargetPackage specified for example use the specified value, else
791      * use default value (targetPackage).
792      *
793      * @return the calculated package
794      */
795     protected String calculateJavaModelExamplePackage() {
796         JavaModelGeneratorConfiguration config = context.getJavaModelGeneratorConfiguration();
797         String exampleTargetPackage = config.getProperty(PropertyRegistry.MODEL_GENERATOR_EXAMPLE_PACKAGE);
798         if (!stringHasValue(exampleTargetPackage)) {
799             return calculateJavaModelPackage();
800         }
801 
802         return exampleTargetPackage
803                 + fullyQualifiedTable.getSubPackageForModel(isSubPackagesEnabled(config));
804     }
805 
806     protected String calculateSqlMapPackage() {
807         StringBuilder sb = new StringBuilder();
808         SqlMapGeneratorConfiguration config = context
809                 .getSqlMapGeneratorConfiguration();
810 
811         // config can be null if the Java client does not require XML
812         if (config != null) {
813             sb.append(config.getTargetPackage());
814             sb.append(fullyQualifiedTable.getSubPackageForClientOrSqlMap(isSubPackagesEnabled(config)));
815             if (stringHasValue(tableConfiguration.getMapperName())) {
816                 String mapperName = tableConfiguration.getMapperName();
817                 int ind = mapperName.lastIndexOf('.');
818                 if (ind != -1) {
819                     sb.append('.').append(mapperName, 0, ind);
820                 }
821             } else if (stringHasValue(fullyQualifiedTable.getDomainObjectSubPackage())) {
822                 sb.append('.').append(fullyQualifiedTable.getDomainObjectSubPackage());
823             }
824         }
825 
826         return sb.toString();
827     }
828 
829     protected String calculateMyBatis3XmlMapperFileName() {
830         StringBuilder sb = new StringBuilder();
831         if (stringHasValue(tableConfiguration.getMapperName())) {
832             String mapperName = tableConfiguration.getMapperName();
833             int ind = mapperName.lastIndexOf('.');
834             if (ind == -1) {
835                 sb.append(mapperName);
836             } else {
837                 sb.append(mapperName.substring(ind + 1));
838             }
839             sb.append(".xml"); //$NON-NLS-1$
840         } else {
841             sb.append(fullyQualifiedTable.getDomainObjectName());
842             sb.append("Mapper.xml"); //$NON-NLS-1$
843         }
844         return sb.toString();
845     }
846 
847     protected String calculateMyBatis3FallbackSqlMapNamespace() {
848         StringBuilder sb = new StringBuilder();
849         sb.append(calculateSqlMapPackage());
850         sb.append('.');
851         if (stringHasValue(tableConfiguration.getMapperName())) {
852             sb.append(tableConfiguration.getMapperName());
853         } else {
854             sb.append(fullyQualifiedTable.getDomainObjectName());
855             sb.append("Mapper"); //$NON-NLS-1$
856         }
857         return sb.toString();
858     }
859 
860     protected String calculateSqlMapFullyQualifiedRuntimeTableName() {
861         return fullyQualifiedTable.getFullyQualifiedTableNameAtRuntime();
862     }
863 
864     protected String calculateSqlMapAliasedFullyQualifiedRuntimeTableName() {
865         return fullyQualifiedTable.getAliasedFullyQualifiedTableNameAtRuntime();
866     }
867 
868     public String getFullyQualifiedTableNameAtRuntime() {
869         return internalAttributes
870                 .get(InternalAttribute.ATTR_FULLY_QUALIFIED_TABLE_NAME_AT_RUNTIME);
871     }
872 
873     public String getAliasedFullyQualifiedTableNameAtRuntime() {
874         return internalAttributes
875                 .get(InternalAttribute.ATTR_ALIASED_FULLY_QUALIFIED_TABLE_NAME_AT_RUNTIME);
876     }
877 
878     /**
879      * This method can be used to initialize the generators before they will be called.
880      *
881      * <p>This method is called after all the setX methods, but before getNumberOfSubtasks(), getGeneratedJavaFiles, and
882      * getGeneratedXmlFiles.
883      *
884      * @param warnings
885      *            the warnings
886      * @param progressCallback
887      *            the progress callback
888      */
889     public abstract void calculateGenerators(List<String> warnings,
890             ProgressCallback progressCallback);
891 
892     /**
893      * This method should return a list of generated Java files related to this
894      * table. This list could include various types of model classes, as well as
895      * DAO classes.
896      *
897      * @return the list of generated Java files for this table
898      */
899     public abstract List<GeneratedJavaFile> getGeneratedJavaFiles();
900 
901     /**
902      * This method should return a list of generated XML files related to this
903      * table. Most implementations will only return one file - the generated
904      * SqlMap file.
905      *
906      * @return the list of generated XML files for this table
907      */
908     public abstract List<GeneratedXmlFile> getGeneratedXmlFiles();
909 
910     /**
911      * This method should return a list of generated Kotlin files related to this
912      * table. This list could include a data classes, a mapper interface, extension methods, etc.
913      *
914      * @return the list of generated Kotlin files for this table
915      */
916     public abstract List<GeneratedKotlinFile> getGeneratedKotlinFiles();
917 
918     /**
919      * This method should return the number of progress messages that will be
920      * sent during the generation phase.
921      *
922      * @return the number of progress messages
923      */
924     public abstract int getGenerationSteps();
925 
926     /**
927      * This method exists to give plugins the opportunity to replace the calculated rules if necessary.
928      *
929      * @param rules
930      *            the new rules
931      */
932     public void setRules(Rules rules) {
933         this.rules = rules;
934     }
935 
936     public TableConfiguration getTableConfiguration() {
937         return tableConfiguration;
938     }
939 
940     public void setPrimaryKeyType(String primaryKeyType) {
941         internalAttributes.put(InternalAttribute.ATTR_PRIMARY_KEY_TYPE,
942                 primaryKeyType);
943     }
944 
945     public void setBaseRecordType(String baseRecordType) {
946         internalAttributes.put(InternalAttribute.ATTR_BASE_RECORD_TYPE,
947                 baseRecordType);
948     }
949 
950     public void setKotlinRecordType(String kotlinRecordType) {
951         internalAttributes.put(InternalAttribute.ATTR_KOTLIN_RECORD_TYPE,
952                 kotlinRecordType);
953     }
954 
955     public void setRecordWithBLOBsType(String recordWithBLOBsType) {
956         internalAttributes.put(InternalAttribute.ATTR_RECORD_WITH_BLOBS_TYPE,
957                 recordWithBLOBsType);
958     }
959 
960     public void setExampleType(String exampleType) {
961         internalAttributes
962                 .put(InternalAttribute.ATTR_EXAMPLE_TYPE, exampleType);
963     }
964 
965     public void setMyBatis3FallbackSqlMapNamespace(String sqlMapNamespace) {
966         internalAttributes.put(
967                 InternalAttribute.ATTR_MYBATIS3_FALLBACK_SQL_MAP_NAMESPACE,
968                 sqlMapNamespace);
969     }
970 
971     public void setSqlMapFullyQualifiedRuntimeTableName(
972             String fullyQualifiedRuntimeTableName) {
973         internalAttributes.put(
974                 InternalAttribute.ATTR_FULLY_QUALIFIED_TABLE_NAME_AT_RUNTIME,
975                 fullyQualifiedRuntimeTableName);
976     }
977 
978     public void setSqlMapAliasedFullyQualifiedRuntimeTableName(
979             String aliasedFullyQualifiedRuntimeTableName) {
980         internalAttributes
981                 .put(
982                         InternalAttribute.ATTR_ALIASED_FULLY_QUALIFIED_TABLE_NAME_AT_RUNTIME,
983                         aliasedFullyQualifiedRuntimeTableName);
984     }
985 
986     public String getMyBatis3XmlMapperPackage() {
987         return internalAttributes
988                 .get(InternalAttribute.ATTR_MYBATIS3_XML_MAPPER_PACKAGE);
989     }
990 
991     public void setMyBatis3XmlMapperPackage(String mybatis3XmlMapperPackage) {
992         internalAttributes.put(
993                 InternalAttribute.ATTR_MYBATIS3_XML_MAPPER_PACKAGE,
994                 mybatis3XmlMapperPackage);
995     }
996 
997     public String getMyBatis3XmlMapperFileName() {
998         return internalAttributes
999                 .get(InternalAttribute.ATTR_MYBATIS3_XML_MAPPER_FILE_NAME);
1000     }
1001 
1002     public void setMyBatis3XmlMapperFileName(String mybatis3XmlMapperFileName) {
1003         internalAttributes.put(
1004                 InternalAttribute.ATTR_MYBATIS3_XML_MAPPER_FILE_NAME,
1005                 mybatis3XmlMapperFileName);
1006     }
1007 
1008     public String getMyBatis3JavaMapperType() {
1009         return internalAttributes
1010                 .get(InternalAttribute.ATTR_MYBATIS3_JAVA_MAPPER_TYPE);
1011     }
1012 
1013     public void setMyBatis3JavaMapperType(String mybatis3JavaMapperType) {
1014         internalAttributes.put(
1015                 InternalAttribute.ATTR_MYBATIS3_JAVA_MAPPER_TYPE,
1016                 mybatis3JavaMapperType);
1017     }
1018 
1019     public String getMyBatis3SqlProviderType() {
1020         return internalAttributes
1021                 .get(InternalAttribute.ATTR_MYBATIS3_SQL_PROVIDER_TYPE);
1022     }
1023 
1024     public void setMyBatis3SqlProviderType(String mybatis3SqlProviderType) {
1025         internalAttributes.put(
1026                 InternalAttribute.ATTR_MYBATIS3_SQL_PROVIDER_TYPE,
1027                 mybatis3SqlProviderType);
1028     }
1029 
1030     public String getMyBatisDynamicSqlSupportType() {
1031         return internalAttributes.get(InternalAttribute.ATTR_MYBATIS_DYNAMIC_SQL_SUPPORT_TYPE);
1032     }
1033 
1034     public void setMyBatisDynamicSqlSupportType(String s) {
1035         internalAttributes.put(InternalAttribute.ATTR_MYBATIS_DYNAMIC_SQL_SUPPORT_TYPE, s);
1036     }
1037 
1038     public TargetRuntime getTargetRuntime() {
1039         return targetRuntime;
1040     }
1041 
1042     public boolean isImmutable() {
1043         Properties properties;
1044 
1045         if (tableConfiguration.getProperties().containsKey(PropertyRegistry.ANY_IMMUTABLE)) {
1046             properties = tableConfiguration.getProperties();
1047         } else {
1048             properties = context.getJavaModelGeneratorConfiguration().getProperties();
1049         }
1050 
1051         return isTrue(properties.getProperty(PropertyRegistry.ANY_IMMUTABLE));
1052     }
1053 
1054     public boolean isConstructorBased() {
1055         if (isImmutable()) {
1056             return true;
1057         }
1058 
1059         Properties properties;
1060 
1061         if (tableConfiguration.getProperties().containsKey(PropertyRegistry.ANY_CONSTRUCTOR_BASED)) {
1062             properties = tableConfiguration.getProperties();
1063         } else {
1064             properties = context.getJavaModelGeneratorConfiguration().getProperties();
1065         }
1066 
1067         return isTrue(properties.getProperty(PropertyRegistry.ANY_CONSTRUCTOR_BASED));
1068     }
1069 
1070     /**
1071      * Should return true if an XML generator is required for this table. This method will be called during validation
1072      * of the configuration, so it should not rely on database introspection. This method simply tells the validator if
1073      * an XML configuration is normally required for this implementation.
1074      *
1075      * @return true, if successful
1076      */
1077     public abstract boolean requiresXMLGenerator();
1078 
1079     public Context getContext() {
1080         return context;
1081     }
1082 
1083     public String getRemarks() {
1084         return remarks;
1085     }
1086 
1087     public void setRemarks(String remarks) {
1088         this.remarks = remarks;
1089     }
1090 
1091     public String getTableType() {
1092         return tableType;
1093     }
1094 
1095     public void setTableType(String tableType) {
1096         this.tableType = tableType;
1097     }
1098 }