View Javadoc
1   /*
2    *    Copyright 2006-2026 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.config;
17  
18  import static org.mybatis.generator.internal.util.StringUtility.isTrue;
19  import static org.mybatis.generator.internal.util.StringUtility.stringHasValue;
20  import static org.mybatis.generator.internal.util.messages.Messages.getString;
21  
22  import java.util.ArrayList;
23  import java.util.Collections;
24  import java.util.List;
25  import java.util.Objects;
26  import java.util.Optional;
27  import java.util.stream.Stream;
28  
29  import org.jspecify.annotations.Nullable;
30  import org.mybatis.generator.api.KnownRuntime;
31  
32  public class Context extends PropertyHolder {
33      private final String id;
34      private final @Nullable JDBCConnectionConfiguration jdbcConnectionConfiguration;
35      private final @Nullable ConnectionFactoryConfiguration connectionFactoryConfiguration;
36      private final @Nullable SqlMapGeneratorConfiguration sqlMapGeneratorConfiguration;
37      private final @Nullable JavaTypeResolverConfiguration javaTypeResolverConfiguration;
38      private final ModelGeneratorConfiguration modelGeneratorConfiguration;
39      private final @Nullable ClientGeneratorConfiguration clientGeneratorConfiguration;
40      private final List<TableConfiguration> tableConfigurations;
41      private final ModelType defaultModelType;
42      private final String beginningDelimiter;
43      private final String endingDelimiter;
44      private final @Nullable Boolean autoDelimitKeywords;
45      private final @Nullable CommentGeneratorConfiguration commentGeneratorConfiguration;
46      private final List<PluginConfiguration> pluginConfigurations;
47      private final @Nullable String targetRuntime;
48      private final @Nullable String introspectedColumnImpl;
49  
50      protected Context(Builder builder) {
51          super(builder);
52          id = Objects.requireNonNull(builder.id, getString("ValidationError.16")); //$NON-NLS-1$
53          defaultModelType = Objects.requireNonNullElseGet(builder.defaultModelType,
54                  () -> calculateDefaultModelType(builder.targetRuntime));
55          tableConfigurations = Collections.unmodifiableList(builder.tableConfigurations);
56          pluginConfigurations = Collections.unmodifiableList(builder.pluginConfigurations);
57          commentGeneratorConfiguration = builder.commentGeneratorConfiguration;
58          jdbcConnectionConfiguration = builder.jdbcConnectionConfiguration;
59          connectionFactoryConfiguration = builder.connectionFactoryConfiguration;
60          sqlMapGeneratorConfiguration = builder.sqlMapGeneratorConfiguration;
61          javaTypeResolverConfiguration = builder.javaTypeResolverConfiguration;
62          introspectedColumnImpl = builder.introspectedColumnImpl;
63          modelGeneratorConfiguration = Objects.requireNonNull(builder.modelGeneratorConfiguration,
64                  getString("ValidationError.8", id)); //$NON-NLS-1$
65          clientGeneratorConfiguration = builder.clientGeneratorConfiguration;
66          targetRuntime = builder.targetRuntime;
67  
68          String property = getProperty(PropertyRegistry.CONTEXT_BEGINNING_DELIMITER);
69          beginningDelimiter = property == null ? Defaults.DEFAULT_BEGINNING_DELIMITER : property;
70  
71          property = getProperty(PropertyRegistry.CONTEXT_ENDING_DELIMITER);
72          endingDelimiter = property == null ? Defaults.DEFAULT_ENDING_DELIMITER : property;
73  
74          property = getProperty(PropertyRegistry.CONTEXT_AUTO_DELIMIT_KEYWORDS);
75          autoDelimitKeywords = isTrue(property);
76      }
77  
78      private ModelType calculateDefaultModelType(@Nullable String targetRuntime) {
79          if (targetRuntime == null) {
80              return ModelType.FLAT; // MyBatis Dynamic SQL is the default runtime
81          } else {
82              KnownRuntime knownRuntime = KnownRuntime.getByAlias(targetRuntime);
83              if (knownRuntime.isDynamicSqlBased() || knownRuntime == KnownRuntime.MYBATIS3_SIMPLE) {
84                  return ModelType.FLAT;
85              } else {
86                  return ModelType.CONDITIONAL;
87              }
88          }
89      }
90  
91      public Optional<ClientGeneratorConfiguration> getClientGeneratorConfiguration() {
92          return Optional.ofNullable(clientGeneratorConfiguration);
93      }
94  
95      public ModelGeneratorConfiguration getModelGeneratorConfiguration() {
96          return Objects.requireNonNull(modelGeneratorConfiguration);
97      }
98  
99      public Optional<JavaTypeResolverConfiguration> getJavaTypeResolverConfiguration() {
100         return Optional.ofNullable(javaTypeResolverConfiguration);
101     }
102 
103     public Optional<SqlMapGeneratorConfiguration> getSqlMapGeneratorConfiguration() {
104         return Optional.ofNullable(sqlMapGeneratorConfiguration);
105     }
106 
107     /**
108      * This method does a simple validate, it makes sure that all required fields have been filled in. It does not do
109      * any more complex operations such as validating that database tables exist or validating that named columns exist
110      *
111      * @param errors
112      *            the errors
113      */
114     public void validate(List<String> errors) {
115         if (!stringHasValue(id)) {
116             errors.add(getString("ValidationError.16")); //$NON-NLS-1$
117         }
118 
119         if (jdbcConnectionConfiguration == null && connectionFactoryConfiguration == null) {
120             // must specify one
121             errors.add(getString("ValidationError.10", id)); //$NON-NLS-1$
122         } else if (jdbcConnectionConfiguration != null && connectionFactoryConfiguration != null) {
123             // must not specify both
124             errors.add(getString("ValidationError.10", id)); //$NON-NLS-1$
125         } else if (jdbcConnectionConfiguration != null) {
126             jdbcConnectionConfiguration.validate(errors);
127         } else {
128             connectionFactoryConfiguration.validate(errors);
129         }
130 
131         modelGeneratorConfiguration.validate(errors, id);
132 
133         KnownRuntime knownRuntime = KnownRuntime.getByAlias(targetRuntime);
134         if (clientGeneratorConfiguration != null) {
135             clientGeneratorConfiguration.validate(errors, id, knownRuntime);
136         }
137 
138         if (knownRuntime.isLegacyMyBatis3Based()
139                 && clientGeneratorConfiguration != null
140                 && clientGeneratorConfiguration.requiresXmlMapper()
141                 && sqlMapGeneratorConfiguration == null) {
142             errors.add(getString("ValidationError.9", id)); //$NON-NLS-1$
143         }
144 
145         if (knownRuntime == KnownRuntime.MYBATIS3_DYNAMIC_SQL || knownRuntime == KnownRuntime.MYBATIS3_SIMPLE) {
146             if (defaultModelType != ModelType.FLAT && defaultModelType != ModelType.RECORD) {
147                 errors.add(getString("ValidationError.29", getId())); //$NON-NLS-1$
148             }
149         }
150 
151         if (sqlMapGeneratorConfiguration != null) {
152             sqlMapGeneratorConfiguration.validate(errors, id);
153         }
154 
155         if (tableConfigurations.isEmpty()) {
156             errors.add(getString("ValidationError.3", id)); //$NON-NLS-1$
157         } else {
158             for (int i = 0; i < tableConfigurations.size(); i++) {
159                 TableConfiguration tc = tableConfigurations.get(i);
160 
161                 tc.validate(errors, i, this, knownRuntime);
162             }
163         }
164 
165         for (PluginConfiguration pluginConfiguration : pluginConfigurations) {
166             pluginConfiguration.validate(errors, id);
167         }
168     }
169 
170     public String getId() {
171         return id;
172     }
173 
174     public ModelType getDefaultModelType() {
175         return defaultModelType;
176     }
177 
178     public String getBeginningDelimiter() {
179         return beginningDelimiter;
180     }
181 
182     public String getEndingDelimiter() {
183         return endingDelimiter;
184     }
185 
186     public Optional<CommentGeneratorConfiguration> getCommentGeneratorConfiguration() {
187         return Optional.ofNullable(commentGeneratorConfiguration);
188     }
189 
190     public Optional<String> getTargetRuntime() {
191         return Optional.ofNullable(targetRuntime);
192     }
193 
194     public Optional<String> getIntrospectedColumnImpl() {
195         return Optional.ofNullable(introspectedColumnImpl);
196     }
197 
198 
199     public int getIntrospectionSteps() {
200         int steps = 0;
201 
202         steps++; // connect to database
203 
204         // for each table:
205         //
206         // 1. Create introspected table implementation
207 
208         steps += tableConfigurations.size();
209 
210         return steps;
211     }
212 
213     public Stream<PluginConfiguration> pluginConfigurations() {
214         return pluginConfigurations.stream();
215     }
216 
217     public List<TableConfiguration> tableConfigurations() {
218         return tableConfigurations;
219     }
220 
221     public boolean autoDelimitKeywords() {
222         return autoDelimitKeywords != null && autoDelimitKeywords;
223     }
224 
225     public @Nullable ConnectionFactoryConfiguration getConnectionFactoryConfiguration() {
226         return connectionFactoryConfiguration;
227     }
228 
229     public @Nullable JDBCConnectionConfiguration getJDBCConnectionConfiguration() {
230         return jdbcConnectionConfiguration;
231     }
232 
233     public static class Builder extends AbstractBuilder<Builder> {
234         private @Nullable String id;
235         private @Nullable ModelType defaultModelType;
236         private @Nullable String targetRuntime;
237         private @Nullable String introspectedColumnImpl;
238         private final List<PluginConfiguration> pluginConfigurations = new ArrayList<>();
239         private final ArrayList<TableConfiguration> tableConfigurations = new ArrayList<>();
240         private @Nullable CommentGeneratorConfiguration commentGeneratorConfiguration;
241         private @Nullable JDBCConnectionConfiguration jdbcConnectionConfiguration;
242         private @Nullable ConnectionFactoryConfiguration connectionFactoryConfiguration;
243         private @Nullable SqlMapGeneratorConfiguration sqlMapGeneratorConfiguration;
244         private @Nullable JavaTypeResolverConfiguration javaTypeResolverConfiguration;
245         private @Nullable ModelGeneratorConfiguration modelGeneratorConfiguration;
246         private @Nullable ClientGeneratorConfiguration clientGeneratorConfiguration;
247 
248         @Override
249         protected Builder getThis() {
250             return this;
251         }
252 
253         public Context build() {
254             return new Context(this);
255         }
256 
257         public Builder withId(String id) {
258             this.id = id;
259             return this;
260         }
261 
262         public Builder withDefaultModelType(@Nullable ModelType defaultModelType) {
263             this.defaultModelType = defaultModelType;
264             return this;
265         }
266 
267         public Builder withTargetRuntime(@Nullable String targetRuntime) {
268             this.targetRuntime = targetRuntime;
269             return this;
270         }
271 
272         public Builder withIntrospectedColumnImpl(@Nullable String introspectedColumnImpl) {
273             this.introspectedColumnImpl = introspectedColumnImpl;
274             return this;
275         }
276 
277         @SuppressWarnings("UnusedReturnValue")
278         public Builder withPluginConfiguration(PluginConfiguration pluginConfiguration) {
279             pluginConfigurations.add(pluginConfiguration);
280             return this;
281         }
282 
283         @SuppressWarnings("UnusedReturnValue")
284         public Builder withTableConfiguration(TableConfiguration tableConfiguration) {
285             tableConfigurations.add(tableConfiguration);
286             return this;
287         }
288 
289         @SuppressWarnings("UnusedReturnValue")
290         public Builder withCommentGeneratorConfiguration(CommentGeneratorConfiguration commentGeneratorConfiguration) {
291             this.commentGeneratorConfiguration = commentGeneratorConfiguration;
292             return this;
293         }
294 
295         @SuppressWarnings("UnusedReturnValue")
296         public Builder withJdbcConnectionConfiguration(JDBCConnectionConfiguration jdbcConnectionConfiguration) {
297             this.jdbcConnectionConfiguration = jdbcConnectionConfiguration;
298             return this;
299         }
300 
301         @SuppressWarnings("UnusedReturnValue")
302         public Builder withSqlMapGeneratorConfiguration(SqlMapGeneratorConfiguration sqlMapGeneratorConfiguration) {
303             this.sqlMapGeneratorConfiguration = sqlMapGeneratorConfiguration;
304             return this;
305         }
306 
307         public Builder withConnectionFactoryConfiguration(
308                 ConnectionFactoryConfiguration connectionFactoryConfiguration) {
309             this.connectionFactoryConfiguration = connectionFactoryConfiguration;
310             return this;
311         }
312 
313         @SuppressWarnings("UnusedReturnValue")
314         public Builder withJavaTypeResolverConfiguration(JavaTypeResolverConfiguration javaTypeResolverConfiguration) {
315             this.javaTypeResolverConfiguration = javaTypeResolverConfiguration;
316             return this;
317         }
318 
319         public Builder withModelGeneratorConfiguration(
320                 ModelGeneratorConfiguration modelGeneratorConfiguration) {
321             this.modelGeneratorConfiguration = modelGeneratorConfiguration;
322             return this;
323         }
324 
325         @SuppressWarnings("UnusedReturnValue")
326         public Builder withClientGeneratorConfiguration(ClientGeneratorConfiguration clientGeneratorConfiguration) {
327             this.clientGeneratorConfiguration = clientGeneratorConfiguration;
328             return this;
329         }
330     }
331 }