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.internal;
17  
18  import static org.mybatis.generator.internal.util.StringUtility.stringValueOrElse;
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.IntrospectedColumn;
29  import org.mybatis.generator.api.JavaFormatter;
30  import org.mybatis.generator.api.JavaTypeResolver;
31  import org.mybatis.generator.api.KnownRuntime;
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.config.CommentGeneratorConfiguration;
36  import org.mybatis.generator.config.ConnectionFactoryConfiguration;
37  import org.mybatis.generator.config.Context;
38  import org.mybatis.generator.config.Defaults;
39  import org.mybatis.generator.config.JavaTypeResolverConfiguration;
40  import org.mybatis.generator.config.PluginConfiguration;
41  import org.mybatis.generator.config.PropertyRegistry;
42  import org.mybatis.generator.exception.InternalException;
43  
44  /**
45   * This class creates the different objects needed by the generator.
46   *
47   * @author Jeff Butler
48   */
49  public class ObjectFactory {
50  
51      private static final List<ClassLoader> externalClassLoaders;
52  
53      static {
54          externalClassLoaders = new ArrayList<>();
55      }
56  
57      /**
58       * Utility class. No instances allowed.
59       */
60      private ObjectFactory() {
61          super();
62      }
63  
64      /**
65       * Clears the class loaders.  This method should be called at the beginning of
66       * a generation run so that and change to the classloading configuration
67       * will be reflected.  For example, if the eclipse launcher changes configuration
68       * it might not be updated if eclipse hasn't been restarted.
69       *
70       */
71      public static void reset() {
72          externalClassLoaders.clear();
73      }
74  
75      /**
76       * Adds a custom classloader to the collection of classloaders searched for "external" classes. These are classes
77       * that do not depend on any of the generator's classes or interfaces. Examples are JDBC drivers, root classes, root
78       * interfaces, etc.
79       *
80       * @param classLoader
81       *            the class loader
82       */
83      public static synchronized void addExternalClassLoader(ClassLoader classLoader) {
84          externalClassLoaders.add(classLoader);
85      }
86  
87      /**
88       * Returns a class loaded from the context classloader, or the classloader supplied by a client. This is
89       * appropriate for JDBC drivers, model root classes, etc. It is not appropriate for any class that extends one of
90       * the supplied classes or interfaces.
91       *
92       * @param type
93       *            the type
94       * @return the Class loaded from the external classloader
95       * @throws ClassNotFoundException
96       *             the class not found exception
97       */
98      @SuppressWarnings("unchecked")
99      public static <T> Class<T> externalClassForName(String type) throws ClassNotFoundException {
100         Class<T> clazz;
101 
102         for (ClassLoader classLoader : externalClassLoaders) {
103             try {
104                 clazz = (Class<T>) Class.forName(type, true, classLoader);
105                 return clazz;
106             } catch (Exception e) {
107                 // ignore - fail safe below
108             }
109         }
110 
111         return internalClassForName(type);
112     }
113 
114     public static <T> T createExternalObject(String type) {
115         T answer;
116 
117         try {
118             Class<T> clazz = externalClassForName(type);
119             answer = clazz.getConstructor().newInstance();
120         } catch (Exception e) {
121             throw new InternalException(getString("RuntimeError.6", type), e); //$NON-NLS-1$
122         }
123 
124         return answer;
125     }
126 
127     @SuppressWarnings("unchecked")
128     public static <T> Class<T> internalClassForName(String type) throws ClassNotFoundException {
129         Class<?> clazz;
130         try {
131             ClassLoader cl = Thread.currentThread().getContextClassLoader();
132             clazz = Class.forName(type, true, cl);
133         } catch (Exception e) {
134             // ignore - failsafe below
135             clazz = null;
136         }
137 
138         if (clazz == null) {
139             clazz = Class.forName(type, true, ObjectFactory.class.getClassLoader());
140         }
141 
142         return (Class<T>) clazz;
143     }
144 
145     public static Optional<URL> getResource(String resource) {
146         URL url;
147 
148         for (ClassLoader classLoader : externalClassLoaders) {
149             url = classLoader.getResource(resource);
150             if (url != null) {
151                 return Optional.of(url);
152             }
153         }
154 
155         ClassLoader cl = Thread.currentThread().getContextClassLoader();
156         url = cl.getResource(resource);
157 
158         if (url == null) {
159             url = ObjectFactory.class.getClassLoader().getResource(resource);
160         }
161 
162         return Optional.ofNullable(url);
163     }
164 
165     public static <T> T createInternalObject(String type) {
166         T answer;
167 
168         try {
169             Class<T> clazz = internalClassForName(type);
170             answer = clazz.getConstructor().newInstance();
171         } catch (Exception e) {
172             throw new InternalException(getString("RuntimeError.6", type), e); //$NON-NLS-1$
173         }
174 
175         return answer;
176     }
177 
178     public static JavaTypeResolver createJavaTypeResolver(Context context, List<String> warnings) {
179         String type = context.getJavaTypeResolverConfiguration()
180                 .map(JavaTypeResolverConfiguration::getImplementationType)
181                 .orElse(Defaults.DEFAULT_JAVA_TYPE_RESOLVER);
182 
183         JavaTypeResolver answer = createInternalObject(type);
184         answer.setWarnings(warnings);
185 
186         context.getJavaTypeResolverConfiguration()
187                 .ifPresent(c -> answer.addConfigurationProperties(c.getProperties()));
188 
189         answer.setContext(context);
190 
191         return answer;
192     }
193 
194     public static Plugin createPlugin(Context context, PluginConfiguration pluginConfiguration,
195                                       CommentGenerator commentGenerator, KnownRuntime knownRuntime) {
196         Plugin plugin = createInternalObject(pluginConfiguration.getConfigurationType().orElseThrow());
197         plugin.setContext(context);
198         plugin.setProperties(pluginConfiguration.getProperties());
199         plugin.setCommentGenerator(commentGenerator);
200         plugin.setKnownRuntime(knownRuntime);
201         return plugin;
202     }
203 
204     public static CommentGenerator createCommentGenerator(Context context) {
205         CommentGenerator answer;
206 
207         String type = context.getCommentGeneratorConfiguration()
208                 .map(CommentGeneratorConfiguration::getImplementationType)
209                 .orElse(Defaults.DEFAULT_COMMENT_GENERATOR);
210 
211         answer = createInternalObject(type);
212 
213         context.getCommentGeneratorConfiguration()
214                 .ifPresent(c -> answer.addConfigurationProperties(c.getProperties()));
215 
216         return answer;
217     }
218 
219     public static ConnectionFactory createConnectionFactory(ConnectionFactoryConfiguration config) {
220         ConnectionFactory answer;
221 
222         String type = config.getImplementationType();
223 
224         answer = createInternalObject(type);
225         answer.addConfigurationProperties(config.getProperties());
226 
227         return answer;
228     }
229 
230     public static JavaFormatter createJavaFormatter(Context context) {
231         String type = stringValueOrElse(context.getProperty(PropertyRegistry.CONTEXT_JAVA_FORMATTER),
232                 Defaults.DEFAULT_JAVA_FORMATTER);
233         JavaFormatter answer = createInternalObject(type);
234 
235         answer.setContext(context);
236 
237         return answer;
238     }
239 
240     public static KotlinFormatter createKotlinFormatter(Context context) {
241         String type = stringValueOrElse(context.getProperty(PropertyRegistry.CONTEXT_KOTLIN_FORMATTER),
242                 Defaults.DEFAULT_KOTLIN_FORMATTER);
243         KotlinFormatter answer = createInternalObject(type);
244 
245         answer.setContext(context);
246 
247         return answer;
248     }
249 
250     public static XmlFormatter createXmlFormatter(Context context) {
251         String type = stringValueOrElse(context.getProperty(PropertyRegistry.CONTEXT_XML_FORMATTER),
252                 Defaults.DEFAULT_XML_FORMATTER);
253         XmlFormatter answer = createInternalObject(type);
254 
255         answer.setContext(context);
256 
257         return answer;
258     }
259 
260     public static IntrospectedColumn createIntrospectedColumn(Context context) {
261         String type = context.getIntrospectedColumnImpl().orElse(IntrospectedColumn.class.getName());
262         IntrospectedColumn answer = createInternalObject(type);
263         answer.setContext(context);
264 
265         return answer;
266     }
267 }