• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mybatis / generator / 1941

12 Jan 2026 04:39PM UTC coverage: 88.75% (+0.4%) from 88.365%
1941

Pull #1412

github

web-flow
Merge b9b8be318 into 98a4124a3
Pull Request #1412: Adopt JSpecify

2331 of 3162 branches covered (73.72%)

1800 of 1949 new or added lines in 202 files covered. (92.36%)

18 existing lines in 10 files now uncovered.

11384 of 12827 relevant lines covered (88.75%)

0.89 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

80.19
/core/mybatis-generator-core/src/main/java/org/mybatis/generator/internal/ObjectFactory.java
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.internal;
17

18
import static org.mybatis.generator.internal.util.StringUtility.stringHasValue;
19
import static org.mybatis.generator.internal.util.messages.Messages.getString;
20

21
import java.net.URL;
22
import java.util.ArrayList;
23
import java.util.List;
24
import java.util.Optional;
25

26
import org.mybatis.generator.api.CommentGenerator;
27
import org.mybatis.generator.api.ConnectionFactory;
28
import org.mybatis.generator.api.FullyQualifiedTable;
29
import org.mybatis.generator.api.IntrospectedColumn;
30
import org.mybatis.generator.api.IntrospectedTable;
31
import org.mybatis.generator.api.JavaFormatter;
32
import org.mybatis.generator.api.JavaTypeResolver;
33
import org.mybatis.generator.api.KotlinFormatter;
34
import org.mybatis.generator.api.Plugin;
35
import org.mybatis.generator.api.XmlFormatter;
36
import org.mybatis.generator.codegen.mybatis3.IntrospectedTableMyBatis3Impl;
37
import org.mybatis.generator.codegen.mybatis3.IntrospectedTableMyBatis3SimpleImpl;
38
import org.mybatis.generator.config.CommentGeneratorConfiguration;
39
import org.mybatis.generator.config.ConnectionFactoryConfiguration;
40
import org.mybatis.generator.config.Context;
41
import org.mybatis.generator.config.Defaults;
42
import org.mybatis.generator.config.JavaTypeResolverConfiguration;
43
import org.mybatis.generator.config.PluginConfiguration;
44
import org.mybatis.generator.config.PropertyRegistry;
45
import org.mybatis.generator.config.TableConfiguration;
46
import org.mybatis.generator.runtime.dynamic.sql.IntrospectedTableMyBatis3DynamicSqlImpl;
47
import org.mybatis.generator.runtime.kotlin.IntrospectedTableKotlinImpl;
48

49
/**
50
 * This class creates the different objects needed by the generator.
51
 *
52
 * @author Jeff Butler
53
 */
54
public class ObjectFactory {
55

56
    private static final List<ClassLoader> externalClassLoaders;
57

58
    static {
59
        externalClassLoaders = new ArrayList<>();
1✔
60
    }
1✔
61

62
    /**
63
     * Utility class. No instances allowed.
64
     */
65
    private ObjectFactory() {
66
        super();
67
    }
68

69
    /**
70
     * Clears the class loaders.  This method should be called at the beginning of
71
     * a generation run so that and change to the classloading configuration
72
     * will be reflected.  For example, if the eclipse launcher changes configuration
73
     * it might not be updated if eclipse hasn't been restarted.
74
     *
75
     */
76
    public static void reset() {
77
        externalClassLoaders.clear();
1✔
78
    }
1✔
79

80
    /**
81
     * Adds a custom classloader to the collection of classloaders searched for "external" classes. These are classes
82
     * that do not depend on any of the generator's classes or interfaces. Examples are JDBC drivers, root classes, root
83
     * interfaces, etc.
84
     *
85
     * @param classLoader
86
     *            the class loader
87
     */
88
    public static synchronized void addExternalClassLoader(ClassLoader classLoader) {
UNCOV
89
        ObjectFactory.externalClassLoaders.add(classLoader);
×
90
    }
×
91

92
    /**
93
     * Returns a class loaded from the context classloader, or the classloader supplied by a client. This is
94
     * appropriate for JDBC drivers, model root classes, etc. It is not appropriate for any class that extends one of
95
     * the supplied classes or interfaces.
96
     *
97
     * @param type
98
     *            the type
99
     * @return the Class loaded from the external classloader
100
     * @throws ClassNotFoundException
101
     *             the class not found exception
102
     */
103
    @SuppressWarnings("unchecked")
104
    public static <T> Class<T> externalClassForName(String type, Class<T> t) throws ClassNotFoundException {
105
        Class<T> clazz;
106

107
        for (ClassLoader classLoader : externalClassLoaders) {
1!
108
            try {
NEW
109
                clazz = (Class<T>) Class.forName(type, true, classLoader);
×
110
                return clazz;
×
111
            } catch (Exception e) {
×
112
                // ignore - fail safe below
113
            }
114
        }
×
115

116
        return internalClassForName(type, t);
1✔
117
    }
118

119
    public static <T> T createExternalObject(String type, Class<T> t) {
120
        T answer;
121

122
        try {
NEW
123
            Class<T> clazz = externalClassForName(type, t);
×
124
            answer = clazz.getConstructor().newInstance();
×
125
        } catch (Exception e) {
×
NEW
126
            throw new RuntimeException(getString("RuntimeError.6", type), e); //$NON-NLS-1$
×
UNCOV
127
        }
×
128

129
        return answer;
×
130
    }
131

132
    @SuppressWarnings("unchecked")
133
    public static <T> Class<T> internalClassForName(String type, Class<T> t) throws ClassNotFoundException {
134
        Class<T> clazz = null;
1✔
135

136
        try {
137
            ClassLoader cl = Thread.currentThread().getContextClassLoader();
1✔
138
            clazz = (Class<T>) Class.forName(type, true, cl);
1✔
139
        } catch (Exception e) {
1✔
140
            // ignore - failsafe below
141
        }
1✔
142

143
        if (clazz == null) {
1✔
NEW
144
            clazz = (Class<T>) Class.forName(type, true, ObjectFactory.class.getClassLoader());
×
145
        }
146

147
        return clazz;
1✔
148
    }
149

150
    public static Optional<URL> getResource(String resource) {
151
        URL url;
152

153
        for (ClassLoader classLoader : externalClassLoaders) {
1!
154
            url = classLoader.getResource(resource);
×
155
            if (url != null) {
×
NEW
156
                return Optional.of(url);
×
157
            }
158
        }
×
159

160
        ClassLoader cl = Thread.currentThread().getContextClassLoader();
1✔
161
        url = cl.getResource(resource);
1✔
162

163
        if (url == null) {
1!
164
            url = ObjectFactory.class.getClassLoader().getResource(resource);
×
165
        }
166

167
        return Optional.ofNullable(url);
1✔
168
    }
169

170
    public static <T> T createInternalObject(String type, Class<T> t) {
171
        T answer;
172

173
        try {
174
            Class<T> clazz = internalClassForName(type, t);
1✔
175
            answer = clazz.getConstructor().newInstance();
1✔
176
        } catch (Exception e) {
×
NEW
177
            throw new RuntimeException(getString("RuntimeError.6", type), e); //$NON-NLS-1$
×
178

179
        }
1✔
180

181
        return answer;
1✔
182
    }
183

184
    public static JavaTypeResolver createJavaTypeResolver(Context context, List<String> warnings) {
185
        String type = context.getJavaTypeResolverConfiguration()
1✔
186
                .map(JavaTypeResolverConfiguration::getImplementationType)
1✔
187
                .orElse(Defaults.DEFAULT_JAVA_TYPE_RESOLVER);
1✔
188

189
        JavaTypeResolver answer = createInternalObject(type, JavaTypeResolver.class);
1✔
190
        answer.setWarnings(warnings);
1✔
191

192
        context.getJavaTypeResolverConfiguration()
1✔
193
                .ifPresent(c -> answer.addConfigurationProperties(c.getProperties()));
1✔
194

195
        answer.setContext(context);
1✔
196

197
        return answer;
1✔
198
    }
199

200
    public static Plugin createPlugin(Context context, PluginConfiguration pluginConfiguration) {
201
        Plugin plugin = createInternalObject(pluginConfiguration.getConfigurationType().orElseThrow(), Plugin.class);
1✔
202
        plugin.setContext(context);
1✔
203
        plugin.setProperties(pluginConfiguration.getProperties());
1✔
204
        return plugin;
1✔
205
    }
206

207
    public static CommentGenerator createCommentGenerator(Context context) {
208
        CommentGenerator answer;
209

210
        String type = context.getCommentGeneratorConfiguration()
1✔
211
                .map(CommentGeneratorConfiguration::getImplementationType)
1✔
212
                .orElse(Defaults.DEFAULT_COMMENT_GENERATOR);
1✔
213

214
        answer = createInternalObject(type, CommentGenerator.class);
1✔
215

216
        context.getCommentGeneratorConfiguration()
1✔
217
                .ifPresent(c -> answer.addConfigurationProperties(c.getProperties()));
1✔
218

219
        return answer;
1✔
220
    }
221

222
    public static ConnectionFactory createConnectionFactory(ConnectionFactoryConfiguration config) {
223
        ConnectionFactory answer;
224

225
        String type = config.getImplementationType();
1✔
226

227
        answer = createInternalObject(type, ConnectionFactory.class);
1✔
228
        answer.addConfigurationProperties(config.getProperties());
1✔
229

230
        return answer;
1✔
231
    }
232

233
    public static JavaFormatter createJavaFormatter(Context context) {
234
        String type = context.getProperty(PropertyRegistry.CONTEXT_JAVA_FORMATTER);
1✔
235
        if (!stringHasValue(type)) {
1!
236
            type = Defaults.DEFAULT_JAVA_FORMATTER;
1✔
237
        }
238

239
        JavaFormatter answer = createInternalObject(type, JavaFormatter.class);
1✔
240

241
        answer.setContext(context);
1✔
242

243
        return answer;
1✔
244
    }
245

246
    public static KotlinFormatter createKotlinFormatter(Context context) {
247
        String type = context.getProperty(PropertyRegistry.CONTEXT_KOTLIN_FORMATTER);
1✔
248
        if (!stringHasValue(type)) {
1!
249
            type = Defaults.DEFAULT_KOTLIN_FORMATTER;
1✔
250
        }
251

252
        KotlinFormatter answer = createInternalObject(type, KotlinFormatter.class);
1✔
253

254
        answer.setContext(context);
1✔
255

256
        return answer;
1✔
257
    }
258

259
    public static XmlFormatter createXmlFormatter(Context context) {
260
        String type = context.getProperty(PropertyRegistry.CONTEXT_XML_FORMATTER);
1✔
261
        if (!stringHasValue(type)) {
1!
262
            type = Defaults.DEFAULT_XML_FORMATTER;
1✔
263
        }
264

265
        XmlFormatter answer = createInternalObject(type, XmlFormatter.class);
1✔
266

267
        answer.setContext(context);
1✔
268

269
        return answer;
1✔
270
    }
271

272
    public static IntrospectedTable createIntrospectedTable(TableConfiguration tableConfiguration,
273
                                                            FullyQualifiedTable table,
274
                                                            Context context) {
275
        IntrospectedTable answer = createIntrospectedTableForValidation(context);
1✔
276
        answer.setFullyQualifiedTable(table);
1✔
277
        answer.setTableConfiguration(tableConfiguration);
1✔
278

279
        return answer;
1✔
280
    }
281

282
    /**
283
     * Creates an introspected table implementation that is only usable for validation .
284
     *
285
     *
286
     * @param context
287
     *            the context
288
     * @return the introspected table
289
     */
290
    public static IntrospectedTable createIntrospectedTableForValidation(Context context) {
291
        String type = context.getTargetRuntime().map(t -> {
1✔
292
            if ("MyBatis3".equalsIgnoreCase(t)) { //$NON-NLS-1$
1✔
293
                return IntrospectedTableMyBatis3Impl.class.getName();
1✔
294
            } else if ("MyBatis3Simple".equalsIgnoreCase(t)) { //$NON-NLS-1$
1✔
295
                return IntrospectedTableMyBatis3SimpleImpl.class.getName();
1✔
296
            } else if ("MyBatis3DynamicSql".equalsIgnoreCase(t)) { //$NON-NLS-1$
1✔
297
                return IntrospectedTableMyBatis3DynamicSqlImpl.class.getName();
1✔
298
            } else if ("MyBatis3Kotlin".equalsIgnoreCase(t)) { //$NON-NLS-1$
1!
299
                return IntrospectedTableKotlinImpl.class.getName();
1✔
300
            } else {
NEW
301
                return t;
×
302
            }
303
        }).orElse(IntrospectedTableMyBatis3DynamicSqlImpl.class.getName());
1✔
304

305
        IntrospectedTable answer = createInternalObject(type, IntrospectedTable.class);
1✔
306
        answer.setContext(context);
1✔
307

308
        return answer;
1✔
309
    }
310

311
    public static IntrospectedColumn createIntrospectedColumn(Context context) {
312
        String type = context.getIntrospectedColumnImpl().orElse(IntrospectedColumn.class.getName());
1✔
313
        IntrospectedColumn answer = createInternalObject(type, IntrospectedColumn.class);
1✔
314
        answer.setContext(context);
1✔
315

316
        return answer;
1✔
317
    }
318
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc