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