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.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  
25  import org.mybatis.generator.api.CommentGenerator;
26  import org.mybatis.generator.api.ConnectionFactory;
27  import org.mybatis.generator.api.FullyQualifiedTable;
28  import org.mybatis.generator.api.IntrospectedColumn;
29  import org.mybatis.generator.api.IntrospectedTable;
30  import org.mybatis.generator.api.JavaFormatter;
31  import org.mybatis.generator.api.JavaTypeResolver;
32  import org.mybatis.generator.api.KotlinFormatter;
33  import org.mybatis.generator.api.Plugin;
34  import org.mybatis.generator.api.XmlFormatter;
35  import org.mybatis.generator.api.dom.DefaultJavaFormatter;
36  import org.mybatis.generator.api.dom.DefaultKotlinFormatter;
37  import org.mybatis.generator.api.dom.DefaultXmlFormatter;
38  import org.mybatis.generator.codegen.mybatis3.IntrospectedTableMyBatis3Impl;
39  import org.mybatis.generator.codegen.mybatis3.IntrospectedTableMyBatis3SimpleImpl;
40  import org.mybatis.generator.config.CommentGeneratorConfiguration;
41  import org.mybatis.generator.config.ConnectionFactoryConfiguration;
42  import org.mybatis.generator.config.Context;
43  import org.mybatis.generator.config.JavaTypeResolverConfiguration;
44  import org.mybatis.generator.config.PluginConfiguration;
45  import org.mybatis.generator.config.PropertyRegistry;
46  import org.mybatis.generator.config.TableConfiguration;
47  import org.mybatis.generator.internal.types.JavaTypeResolverDefaultImpl;
48  import org.mybatis.generator.runtime.dynamic.sql.IntrospectedTableMyBatis3DynamicSqlImpl;
49  import org.mybatis.generator.runtime.kotlin.IntrospectedTableKotlinImpl;
50  
51  /**
52   * This class creates the different objects needed by the generator.
53   *
54   * @author Jeff Butler
55   */
56  public class ObjectFactory {
57  
58      private static final List<ClassLoader> externalClassLoaders;
59  
60      static {
61          externalClassLoaders = new ArrayList<>();
62      }
63  
64      /**
65       * Utility class. No instances allowed.
66       */
67      private ObjectFactory() {
68          super();
69      }
70  
71      /**
72       * Clears the class loaders.  This method should be called at the beginning of
73       * a generation run so that and change to the classloading configuration
74       * will be reflected.  For example, if the eclipse launcher changes configuration
75       * it might not be updated if eclipse hasn't been restarted.
76       *
77       */
78      public static void reset() {
79          externalClassLoaders.clear();
80      }
81  
82      /**
83       * Adds a custom classloader to the collection of classloaders searched for "external" classes. These are classes
84       * that do not depend on any of the generator's classes or interfaces. Examples are JDBC drivers, root classes, root
85       * interfaces, etc.
86       *
87       * @param classLoader
88       *            the class loader
89       */
90      public static synchronized void addExternalClassLoader(
91              ClassLoader classLoader) {
92          ObjectFactory.externalClassLoaders.add(classLoader);
93      }
94  
95      /**
96       * Returns a class loaded from the context classloader, or the classloader supplied by a client. This is
97       * appropriate for JDBC drivers, model root classes, etc. It is not appropriate for any class that extends one of
98       * the supplied classes or interfaces.
99       *
100      * @param type
101      *            the type
102      * @return the Class loaded from the external classloader
103      * @throws ClassNotFoundException
104      *             the class not found exception
105      */
106     public static Class<?> externalClassForName(String type)
107             throws ClassNotFoundException {
108 
109         Class<?> clazz;
110 
111         for (ClassLoader classLoader : externalClassLoaders) {
112             try {
113                 clazz = Class.forName(type, true, classLoader);
114                 return clazz;
115             } catch (Exception e) {
116                 // ignore - fail safe below
117             }
118         }
119 
120         return internalClassForName(type);
121     }
122 
123     public static Object createExternalObject(String type) {
124         Object answer;
125 
126         try {
127             Class<?> clazz = externalClassForName(type);
128             answer = clazz.getConstructor().newInstance();
129         } catch (Exception e) {
130             throw new RuntimeException(getString(
131                     "RuntimeError.6", type), e); //$NON-NLS-1$
132         }
133 
134         return answer;
135     }
136 
137     public static Class<?> internalClassForName(String type)
138             throws ClassNotFoundException {
139         Class<?> clazz = null;
140 
141         try {
142             ClassLoader cl = Thread.currentThread().getContextClassLoader();
143             clazz = Class.forName(type, true, cl);
144         } catch (Exception e) {
145             // ignore - failsafe below
146         }
147 
148         if (clazz == null) {
149             clazz = Class.forName(type, true, ObjectFactory.class.getClassLoader());
150         }
151 
152         return clazz;
153     }
154 
155     public static URL getResource(String resource) {
156         URL url;
157 
158         for (ClassLoader classLoader : externalClassLoaders) {
159             url = classLoader.getResource(resource);
160             if (url != null) {
161                 return url;
162             }
163         }
164 
165         ClassLoader cl = Thread.currentThread().getContextClassLoader();
166         url = cl.getResource(resource);
167 
168         if (url == null) {
169             url = ObjectFactory.class.getClassLoader().getResource(resource);
170         }
171 
172         return url;
173     }
174 
175     public static Object createInternalObject(String type) {
176         Object answer;
177 
178         try {
179             Class<?> clazz = internalClassForName(type);
180 
181             answer = clazz.getConstructor().newInstance();
182         } catch (Exception e) {
183             throw new RuntimeException(getString(
184                     "RuntimeError.6", type), e); //$NON-NLS-1$
185 
186         }
187 
188         return answer;
189     }
190 
191     public static JavaTypeResolver createJavaTypeResolver(Context context,
192             List<String> warnings) {
193         JavaTypeResolverConfiguration config = context
194                 .getJavaTypeResolverConfiguration();
195         String type;
196 
197         if (config != null && config.getConfigurationType() != null) {
198             type = config.getConfigurationType();
199             if ("DEFAULT".equalsIgnoreCase(type)) { //$NON-NLS-1$
200                 type = JavaTypeResolverDefaultImpl.class.getName();
201             }
202         } else {
203             type = JavaTypeResolverDefaultImpl.class.getName();
204         }
205 
206         JavaTypeResolver answer = (JavaTypeResolver) createInternalObject(type);
207         answer.setWarnings(warnings);
208 
209         if (config != null) {
210             answer.addConfigurationProperties(config.getProperties());
211         }
212 
213         answer.setContext(context);
214 
215         return answer;
216     }
217 
218     public static Plugin createPlugin(Context context,
219             PluginConfiguration pluginConfiguration) {
220         Plugin plugin = (Plugin) createInternalObject(pluginConfiguration
221                 .getConfigurationType());
222         plugin.setContext(context);
223         plugin.setProperties(pluginConfiguration.getProperties());
224         return plugin;
225     }
226 
227     public static CommentGenerator createCommentGenerator(Context context) {
228 
229         CommentGeneratorConfiguration config = context
230                 .getCommentGeneratorConfiguration();
231         CommentGenerator answer;
232 
233         String type;
234         if (config == null || config.getConfigurationType() == null) {
235             type = DefaultCommentGenerator.class.getName();
236         } else {
237             type = config.getConfigurationType();
238         }
239 
240         answer = (CommentGenerator) createInternalObject(type);
241 
242         if (config != null) {
243             answer.addConfigurationProperties(config.getProperties());
244         }
245 
246         return answer;
247     }
248 
249     public static ConnectionFactory createConnectionFactory(Context context) {
250 
251         ConnectionFactoryConfiguration config = context
252                 .getConnectionFactoryConfiguration();
253         ConnectionFactory answer;
254 
255         String type;
256         if (config == null || config.getConfigurationType() == null) {
257             type = JDBCConnectionFactory.class.getName();
258         } else {
259             type = config.getConfigurationType();
260         }
261 
262         answer = (ConnectionFactory) createInternalObject(type);
263 
264         if (config != null) {
265             answer.addConfigurationProperties(config.getProperties());
266         }
267 
268         return answer;
269     }
270 
271     public static JavaFormatter createJavaFormatter(Context context) {
272         String type = context.getProperty(PropertyRegistry.CONTEXT_JAVA_FORMATTER);
273         if (!stringHasValue(type)) {
274             type = DefaultJavaFormatter.class.getName();
275         }
276 
277         JavaFormatter answer = (JavaFormatter) createInternalObject(type);
278 
279         answer.setContext(context);
280 
281         return answer;
282     }
283 
284     public static KotlinFormatter createKotlinFormatter(Context context) {
285         String type = context.getProperty(PropertyRegistry.CONTEXT_KOTLIN_FORMATTER);
286         if (!stringHasValue(type)) {
287             type = DefaultKotlinFormatter.class.getName();
288         }
289 
290         KotlinFormatter answer = (KotlinFormatter) createInternalObject(type);
291 
292         answer.setContext(context);
293 
294         return answer;
295     }
296 
297     public static XmlFormatter createXmlFormatter(Context context) {
298         String type = context.getProperty(PropertyRegistry.CONTEXT_XML_FORMATTER);
299         if (!stringHasValue(type)) {
300             type = DefaultXmlFormatter.class.getName();
301         }
302 
303         XmlFormatter answer = (XmlFormatter) createInternalObject(type);
304 
305         answer.setContext(context);
306 
307         return answer;
308     }
309 
310     public static IntrospectedTable createIntrospectedTable(
311             TableConfiguration tableConfiguration, FullyQualifiedTable table,
312             Context context) {
313 
314         IntrospectedTable answer = createIntrospectedTableForValidation(context);
315         answer.setFullyQualifiedTable(table);
316         answer.setTableConfiguration(tableConfiguration);
317 
318         return answer;
319     }
320 
321     /**
322      * Creates an introspected table implementation that is only usable for validation .
323      *
324      *
325      * @param context
326      *            the context
327      * @return the introspected table
328      */
329     public static IntrospectedTable createIntrospectedTableForValidation(Context context) {
330         String type = context.getTargetRuntime();
331         if (!stringHasValue(type)) {
332             type = IntrospectedTableMyBatis3DynamicSqlImpl.class.getName();
333         } else if ("MyBatis3".equalsIgnoreCase(type)) { //$NON-NLS-1$
334             type = IntrospectedTableMyBatis3Impl.class.getName();
335         } else if ("MyBatis3Simple".equalsIgnoreCase(type)) { //$NON-NLS-1$
336             type = IntrospectedTableMyBatis3SimpleImpl.class.getName();
337         } else if ("MyBatis3DynamicSql".equalsIgnoreCase(type)) { //$NON-NLS-1$
338             type = IntrospectedTableMyBatis3DynamicSqlImpl.class.getName();
339         } else if ("MyBatis3Kotlin".equalsIgnoreCase(type)) { //$NON-NLS-1$
340             type = IntrospectedTableKotlinImpl.class.getName();
341         }
342 
343         IntrospectedTable answer = (IntrospectedTable) createInternalObject(type);
344         answer.setContext(context);
345 
346         return answer;
347     }
348 
349     public static IntrospectedColumn createIntrospectedColumn(Context context) {
350         String type = context.getIntrospectedColumnImpl();
351         if (!stringHasValue(type)) {
352             type = IntrospectedColumn.class.getName();
353         }
354 
355         IntrospectedColumn answer = (IntrospectedColumn) createInternalObject(type);
356         answer.setContext(context);
357 
358         return answer;
359     }
360 }