View Javadoc
1   /**
2    *    Copyright 2006-2016 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    *       http://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.composeFullyQualifiedTableName;
19  import static org.mybatis.generator.internal.util.StringUtility.isTrue;
20  import static org.mybatis.generator.internal.util.StringUtility.stringHasValue;
21  import static org.mybatis.generator.internal.util.messages.Messages.getString;
22  
23  import java.sql.Connection;
24  import java.sql.SQLException;
25  import java.util.ArrayList;
26  import java.util.List;
27  import java.util.Set;
28  
29  import org.mybatis.generator.api.CommentGenerator;
30  import org.mybatis.generator.api.GeneratedJavaFile;
31  import org.mybatis.generator.api.GeneratedXmlFile;
32  import org.mybatis.generator.api.JavaFormatter;
33  import org.mybatis.generator.api.Plugin;
34  import org.mybatis.generator.api.IntrospectedTable;
35  import org.mybatis.generator.api.JavaTypeResolver;
36  import org.mybatis.generator.api.ProgressCallback;
37  import org.mybatis.generator.api.XmlFormatter;
38  import org.mybatis.generator.api.dom.xml.Attribute;
39  import org.mybatis.generator.api.dom.xml.XmlElement;
40  import org.mybatis.generator.internal.ObjectFactory;
41  import org.mybatis.generator.internal.PluginAggregator;
42  import org.mybatis.generator.internal.db.ConnectionFactory;
43  import org.mybatis.generator.internal.db.DatabaseIntrospector;
44  
45  /**
46   * The Class Context.
47   *
48   * @author Jeff Butler
49   */
50  public class Context extends PropertyHolder {
51      
52      /** The id. */
53      private String id;
54  
55      /** The jdbc connection configuration. */
56      private JDBCConnectionConfiguration jdbcConnectionConfiguration;
57  
58      /** The sql map generator configuration. */
59      private SqlMapGeneratorConfiguration sqlMapGeneratorConfiguration;
60  
61      /** The java type resolver configuration. */
62      private JavaTypeResolverConfiguration javaTypeResolverConfiguration;
63  
64      /** The java model generator configuration. */
65      private JavaModelGeneratorConfiguration javaModelGeneratorConfiguration;
66  
67      /** The java client generator configuration. */
68      private JavaClientGeneratorConfiguration javaClientGeneratorConfiguration;
69  
70      /** The table configurations. */
71      private ArrayList<TableConfiguration> tableConfigurations;
72  
73      /** The default model type. */
74      private ModelType defaultModelType;
75  
76      /** The beginning delimiter. */
77      private String beginningDelimiter = "\""; //$NON-NLS-1$
78  
79      /** The ending delimiter. */
80      private String endingDelimiter = "\""; //$NON-NLS-1$
81  
82      /** The comment generator configuration. */
83      private CommentGeneratorConfiguration commentGeneratorConfiguration;
84  
85      /** The comment generator. */
86      private CommentGenerator commentGenerator;
87  
88      /** The plugin aggregator. */
89      private PluginAggregator pluginAggregator;
90  
91      /** The plugin configurations. */
92      private List<PluginConfiguration> pluginConfigurations;
93  
94      /** The target runtime. */
95      private String targetRuntime;
96  
97      /** The introspected column impl. */
98      private String introspectedColumnImpl;
99  
100     /** The auto delimit keywords. */
101     private Boolean autoDelimitKeywords;
102     
103     /** The java formatter. */
104     private JavaFormatter javaFormatter;
105     
106     /** The xml formatter. */
107     private XmlFormatter xmlFormatter;
108 
109     /**
110      * Constructs a Context object.
111      * 
112      * @param defaultModelType
113      *            - may be null
114      */
115     public Context(ModelType defaultModelType) {
116         super();
117 
118         if (defaultModelType == null) {
119             this.defaultModelType = ModelType.CONDITIONAL;
120         } else {
121             this.defaultModelType = defaultModelType;
122         }
123 
124         tableConfigurations = new ArrayList<TableConfiguration>();
125         pluginConfigurations = new ArrayList<PluginConfiguration>();
126     }
127 
128     /**
129      * Adds the table configuration.
130      *
131      * @param tc
132      *            the tc
133      */
134     public void addTableConfiguration(TableConfiguration tc) {
135         tableConfigurations.add(tc);
136     }
137 
138     /**
139      * Gets the jdbc connection configuration.
140      *
141      * @return the jdbc connection configuration
142      */
143     public JDBCConnectionConfiguration getJdbcConnectionConfiguration() {
144         return jdbcConnectionConfiguration;
145     }
146 
147     /**
148      * Gets the java client generator configuration.
149      *
150      * @return the java client generator configuration
151      */
152     public JavaClientGeneratorConfiguration getJavaClientGeneratorConfiguration() {
153         return javaClientGeneratorConfiguration;
154     }
155 
156     /**
157      * Gets the java model generator configuration.
158      *
159      * @return the java model generator configuration
160      */
161     public JavaModelGeneratorConfiguration getJavaModelGeneratorConfiguration() {
162         return javaModelGeneratorConfiguration;
163     }
164 
165     /**
166      * Gets the java type resolver configuration.
167      *
168      * @return the java type resolver configuration
169      */
170     public JavaTypeResolverConfiguration getJavaTypeResolverConfiguration() {
171         return javaTypeResolverConfiguration;
172     }
173 
174     /**
175      * Gets the sql map generator configuration.
176      *
177      * @return the sql map generator configuration
178      */
179     public SqlMapGeneratorConfiguration getSqlMapGeneratorConfiguration() {
180         return sqlMapGeneratorConfiguration;
181     }
182 
183     /**
184      * Adds the plugin configuration.
185      *
186      * @param pluginConfiguration
187      *            the plugin configuration
188      */
189     public void addPluginConfiguration(
190             PluginConfiguration pluginConfiguration) {
191         pluginConfigurations.add(pluginConfiguration);
192     }
193 
194     /**
195      * This method does a simple validate, it makes sure that all required fields have been filled in. It does not do
196      * any more complex operations such as validating that database tables exist or validating that named columns exist
197      *
198      * @param errors
199      *            the errors
200      */
201     public void validate(List<String> errors) {
202         if (!stringHasValue(id)) {
203             errors.add(getString("ValidationError.16")); //$NON-NLS-1$
204         }
205 
206         if (jdbcConnectionConfiguration == null) {
207             errors.add(getString("ValidationError.10", id)); //$NON-NLS-1$
208         } else {
209             jdbcConnectionConfiguration.validate(errors);
210         }
211 
212         if (javaModelGeneratorConfiguration == null) {
213             errors.add(getString("ValidationError.8", id)); //$NON-NLS-1$
214         } else {
215             javaModelGeneratorConfiguration.validate(errors, id);
216         }
217 
218         if (javaClientGeneratorConfiguration != null) {
219             javaClientGeneratorConfiguration.validate(errors, id);
220         }
221 
222         IntrospectedTable it = null;
223         try {
224             it = ObjectFactory.createIntrospectedTableForValidation(this);
225         } catch (Exception e) {
226             errors.add(getString("ValidationError.25", id)); //$NON-NLS-1$
227         }
228         
229         if (it != null && it.requiresXMLGenerator()) {
230             if (sqlMapGeneratorConfiguration == null) {
231                 errors.add(getString("ValidationError.9", id)); //$NON-NLS-1$
232             } else {
233                 sqlMapGeneratorConfiguration.validate(errors, id);
234             }
235         }
236 
237         if (tableConfigurations.size() == 0) {
238             errors.add(getString("ValidationError.3", id)); //$NON-NLS-1$
239         } else {
240             for (int i = 0; i < tableConfigurations.size(); i++) {
241                 TableConfiguration tc = tableConfigurations.get(i);
242 
243                 tc.validate(errors, i);
244             }
245         }
246 
247         for (PluginConfiguration pluginConfiguration : pluginConfigurations) {
248             pluginConfiguration.validate(errors, id);
249         }
250     }
251 
252     /**
253      * Gets the id.
254      *
255      * @return the id
256      */
257     public String getId() {
258         return id;
259     }
260 
261     /**
262      * Sets the id.
263      *
264      * @param id
265      *            the new id
266      */
267     public void setId(String id) {
268         this.id = id;
269     }
270 
271     /**
272      * Sets the java client generator configuration.
273      *
274      * @param javaClientGeneratorConfiguration
275      *            the new java client generator configuration
276      */
277     public void setJavaClientGeneratorConfiguration(
278             JavaClientGeneratorConfiguration javaClientGeneratorConfiguration) {
279         this.javaClientGeneratorConfiguration = javaClientGeneratorConfiguration;
280     }
281 
282     /**
283      * Sets the java model generator configuration.
284      *
285      * @param javaModelGeneratorConfiguration
286      *            the new java model generator configuration
287      */
288     public void setJavaModelGeneratorConfiguration(
289             JavaModelGeneratorConfiguration javaModelGeneratorConfiguration) {
290         this.javaModelGeneratorConfiguration = javaModelGeneratorConfiguration;
291     }
292 
293     /**
294      * Sets the java type resolver configuration.
295      *
296      * @param javaTypeResolverConfiguration
297      *            the new java type resolver configuration
298      */
299     public void setJavaTypeResolverConfiguration(
300             JavaTypeResolverConfiguration javaTypeResolverConfiguration) {
301         this.javaTypeResolverConfiguration = javaTypeResolverConfiguration;
302     }
303 
304     /**
305      * Sets the jdbc connection configuration.
306      *
307      * @param jdbcConnectionConfiguration
308      *            the new jdbc connection configuration
309      */
310     public void setJdbcConnectionConfiguration(
311             JDBCConnectionConfiguration jdbcConnectionConfiguration) {
312         this.jdbcConnectionConfiguration = jdbcConnectionConfiguration;
313     }
314 
315     /**
316      * Sets the sql map generator configuration.
317      *
318      * @param sqlMapGeneratorConfiguration
319      *            the new sql map generator configuration
320      */
321     public void setSqlMapGeneratorConfiguration(
322             SqlMapGeneratorConfiguration sqlMapGeneratorConfiguration) {
323         this.sqlMapGeneratorConfiguration = sqlMapGeneratorConfiguration;
324     }
325 
326     /**
327      * Gets the default model type.
328      *
329      * @return the default model type
330      */
331     public ModelType getDefaultModelType() {
332         return defaultModelType;
333     }
334 
335     /**
336      * Builds an XmlElement representation of this context. Note that the XML
337      * may not necessarily validate if the context is invalid. Call the
338      * <code>validate</code> method to check validity of this context.
339      * 
340      * @return the XML representation of this context
341      */
342     public XmlElement toXmlElement() {
343         XmlElement xmlElement = new XmlElement("context"); //$NON-NLS-1$
344         
345         xmlElement.addAttribute(new Attribute("id", id)); //$NON-NLS-1$
346         
347         if (defaultModelType != ModelType.CONDITIONAL) {
348             xmlElement.addAttribute(new Attribute(
349                     "defaultModelType", defaultModelType.getModelType())); //$NON-NLS-1$
350         }
351 
352         if (stringHasValue(introspectedColumnImpl)) {
353             xmlElement.addAttribute(new Attribute(
354                     "introspectedColumnImpl", introspectedColumnImpl)); //$NON-NLS-1$
355         }
356 
357         if (stringHasValue(targetRuntime)) {
358             xmlElement.addAttribute(new Attribute(
359                     "targetRuntime", targetRuntime)); //$NON-NLS-1$
360         }
361 
362         addPropertyXmlElements(xmlElement);
363         
364         for (PluginConfiguration pluginConfiguration : pluginConfigurations) {
365             xmlElement.addElement(pluginConfiguration.toXmlElement());
366         }
367 
368         if (commentGeneratorConfiguration != null) {
369             xmlElement.addElement(commentGeneratorConfiguration.toXmlElement());
370         }
371 
372         if (jdbcConnectionConfiguration != null) {
373             xmlElement.addElement(jdbcConnectionConfiguration.toXmlElement());
374         }
375 
376         if (javaTypeResolverConfiguration != null) {
377             xmlElement.addElement(javaTypeResolverConfiguration.toXmlElement());
378         }
379 
380         if (javaModelGeneratorConfiguration != null) {
381             xmlElement.addElement(javaModelGeneratorConfiguration
382                     .toXmlElement());
383         }
384 
385         if (sqlMapGeneratorConfiguration != null) {
386             xmlElement.addElement(sqlMapGeneratorConfiguration.toXmlElement());
387         }
388 
389         if (javaClientGeneratorConfiguration != null) {
390             xmlElement.addElement(javaClientGeneratorConfiguration.toXmlElement());
391         }
392 
393         for (TableConfiguration tableConfiguration : tableConfigurations) {
394             xmlElement.addElement(tableConfiguration.toXmlElement());
395         }
396 
397         return xmlElement;
398     }
399 
400     /**
401      * Gets the table configurations.
402      *
403      * @return the table configurations
404      */
405     public List<TableConfiguration> getTableConfigurations() {
406         return tableConfigurations;
407     }
408 
409     /**
410      * Gets the beginning delimiter.
411      *
412      * @return the beginning delimiter
413      */
414     public String getBeginningDelimiter() {
415         return beginningDelimiter;
416     }
417 
418     /**
419      * Gets the ending delimiter.
420      *
421      * @return the ending delimiter
422      */
423     public String getEndingDelimiter() {
424         return endingDelimiter;
425     }
426 
427     /* (non-Javadoc)
428      * @see org.mybatis.generator.config.PropertyHolder#addProperty(java.lang.String, java.lang.String)
429      */
430     @Override
431     public void addProperty(String name, String value) {
432         super.addProperty(name, value);
433 
434         if (PropertyRegistry.CONTEXT_BEGINNING_DELIMITER.equals(name)) {
435             beginningDelimiter = value;
436         } else if (PropertyRegistry.CONTEXT_ENDING_DELIMITER.equals(name)) {
437             endingDelimiter = value;
438         } else if (PropertyRegistry.CONTEXT_AUTO_DELIMIT_KEYWORDS.equals(name)
439                 && stringHasValue(value)) {
440             autoDelimitKeywords = isTrue(value);
441         }
442     }
443 
444     /**
445      * Gets the comment generator.
446      *
447      * @return the comment generator
448      */
449     public CommentGenerator getCommentGenerator() {
450         if (commentGenerator == null) {
451             commentGenerator = ObjectFactory.createCommentGenerator(this);
452         }
453 
454         return commentGenerator;
455     }
456 
457     /**
458      * Gets the java formatter.
459      *
460      * @return the java formatter
461      */
462     public JavaFormatter getJavaFormatter() {
463         if (javaFormatter == null) {
464             javaFormatter = ObjectFactory.createJavaFormatter(this);
465         }
466 
467         return javaFormatter;
468     }
469     
470     /**
471      * Gets the xml formatter.
472      *
473      * @return the xml formatter
474      */
475     public XmlFormatter getXmlFormatter() {
476         if (xmlFormatter == null) {
477             xmlFormatter = ObjectFactory.createXmlFormatter(this);
478         }
479 
480         return xmlFormatter;
481     }
482     
483     /**
484      * Gets the comment generator configuration.
485      *
486      * @return the comment generator configuration
487      */
488     public CommentGeneratorConfiguration getCommentGeneratorConfiguration() {
489         return commentGeneratorConfiguration;
490     }
491 
492     /**
493      * Sets the comment generator configuration.
494      *
495      * @param commentGeneratorConfiguration
496      *            the new comment generator configuration
497      */
498     public void setCommentGeneratorConfiguration(
499             CommentGeneratorConfiguration commentGeneratorConfiguration) {
500         this.commentGeneratorConfiguration = commentGeneratorConfiguration;
501     }
502 
503     /**
504      * Gets the plugins.
505      *
506      * @return the plugins
507      */
508     public Plugin getPlugins() {
509         return pluginAggregator;
510     }
511 
512     /**
513      * Gets the target runtime.
514      *
515      * @return the target runtime
516      */
517     public String getTargetRuntime() {
518         return targetRuntime;
519     }
520 
521     /**
522      * Sets the target runtime.
523      *
524      * @param targetRuntime
525      *            the new target runtime
526      */
527     public void setTargetRuntime(String targetRuntime) {
528         this.targetRuntime = targetRuntime;
529     }
530 
531     /**
532      * Gets the introspected column impl.
533      *
534      * @return the introspected column impl
535      */
536     public String getIntrospectedColumnImpl() {
537         return introspectedColumnImpl;
538     }
539 
540     /**
541      * Sets the introspected column impl.
542      *
543      * @param introspectedColumnImpl
544      *            the new introspected column impl
545      */
546     public void setIntrospectedColumnImpl(String introspectedColumnImpl) {
547         this.introspectedColumnImpl = introspectedColumnImpl;
548     }
549 
550     // methods related to code generation.
551     //
552     // Methods should be called in this order:
553     //
554     // 1. getIntrospectionSteps()
555     // 2. introspectTables()
556     // 3. getGenerationSteps()
557     // 4. generateFiles()
558     //
559 
560     /** The introspected tables. */
561     private List<IntrospectedTable> introspectedTables;
562 
563     /**
564      * Gets the introspection steps.
565      *
566      * @return the introspection steps
567      */
568     public int getIntrospectionSteps() {
569         int steps = 0;
570 
571         steps++; // connect to database
572 
573         // for each table:
574         //
575         // 1. Create introspected table implementation
576 
577         steps += tableConfigurations.size() * 1;
578 
579         return steps;
580     }
581 
582     /**
583      * Introspect tables based on the configuration specified in the
584      * constructor. This method is long running.
585      * 
586      * @param callback
587      *            a progress callback if progress information is desired, or
588      *            <code>null</code>
589      * @param warnings
590      *            any warning generated from this method will be added to the
591      *            List. Warnings are always Strings.
592      * @param fullyQualifiedTableNames
593      *            a set of table names to generate. The elements of the set must
594      *            be Strings that exactly match what's specified in the
595      *            configuration. For example, if table name = "foo" and schema =
596      *            "bar", then the fully qualified table name is "foo.bar". If
597      *            the Set is null or empty, then all tables in the configuration
598      *            will be used for code generation.
599      * 
600      * @throws SQLException
601      *             if some error arises while introspecting the specified
602      *             database tables.
603      * @throws InterruptedException
604      *             if the progress callback reports a cancel
605      */
606     public void introspectTables(ProgressCallback callback,
607             List<String> warnings, Set<String> fullyQualifiedTableNames)
608             throws SQLException, InterruptedException {
609 
610         introspectedTables = new ArrayList<IntrospectedTable>();
611         JavaTypeResolver javaTypeResolver = ObjectFactory
612                 .createJavaTypeResolver(this, warnings);
613 
614         Connection connection = null;
615 
616         try {
617             callback.startTask(getString("Progress.0")); //$NON-NLS-1$
618             connection = getConnection();
619 
620             DatabaseIntrospector databaseIntrospector = new DatabaseIntrospector(
621                     this, connection.getMetaData(), javaTypeResolver, warnings);
622 
623             for (TableConfiguration tc : tableConfigurations) {
624                 String tableName = composeFullyQualifiedTableName(tc.getCatalog(), tc
625                                 .getSchema(), tc.getTableName(), '.');
626 
627                 if (fullyQualifiedTableNames != null
628                         && fullyQualifiedTableNames.size() > 0
629                         && !fullyQualifiedTableNames.contains(tableName)) {
630                     continue;
631                 }
632 
633                 if (!tc.areAnyStatementsEnabled()) {
634                     warnings.add(getString("Warning.0", tableName)); //$NON-NLS-1$
635                     continue;
636                 }
637 
638                 callback.startTask(getString("Progress.1", tableName)); //$NON-NLS-1$
639                 List<IntrospectedTable> tables = databaseIntrospector
640                         .introspectTables(tc);
641 
642                 if (tables != null) {
643                     introspectedTables.addAll(tables);
644                 }
645 
646                 callback.checkCancel();
647             }
648         } finally {
649             closeConnection(connection);
650         }
651     }
652 
653     /**
654      * Gets the generation steps.
655      *
656      * @return the generation steps
657      */
658     public int getGenerationSteps() {
659         int steps = 0;
660 
661         if (introspectedTables != null) {
662             for (IntrospectedTable introspectedTable : introspectedTables) {
663                 steps += introspectedTable.getGenerationSteps();
664             }
665         }
666 
667         return steps;
668     }
669 
670     /**
671      * Generate files.
672      *
673      * @param callback
674      *            the callback
675      * @param generatedJavaFiles
676      *            the generated java files
677      * @param generatedXmlFiles
678      *            the generated xml files
679      * @param warnings
680      *            the warnings
681      * @throws InterruptedException
682      *             the interrupted exception
683      */
684     public void generateFiles(ProgressCallback callback,
685             List<GeneratedJavaFile> generatedJavaFiles,
686             List<GeneratedXmlFile> generatedXmlFiles, List<String> warnings)
687             throws InterruptedException {
688 
689         pluginAggregator = new PluginAggregator();
690         for (PluginConfiguration pluginConfiguration : pluginConfigurations) {
691             Plugin plugin = ObjectFactory.createPlugin(this,
692                     pluginConfiguration);
693             if (plugin.validate(warnings)) {
694                 pluginAggregator.addPlugin(plugin);
695             } else {
696                 warnings.add(getString("Warning.24", //$NON-NLS-1$
697                         pluginConfiguration.getConfigurationType(), id));
698             }
699         }
700 
701         if (introspectedTables != null) {
702             for (IntrospectedTable introspectedTable : introspectedTables) {
703                 callback.checkCancel();
704 
705                 introspectedTable.initialize();
706                 introspectedTable.calculateGenerators(warnings, callback);
707                 generatedJavaFiles.addAll(introspectedTable
708                         .getGeneratedJavaFiles());
709                 generatedXmlFiles.addAll(introspectedTable
710                         .getGeneratedXmlFiles());
711 
712                 generatedJavaFiles.addAll(pluginAggregator
713                         .contextGenerateAdditionalJavaFiles(introspectedTable));
714                 generatedXmlFiles.addAll(pluginAggregator
715                         .contextGenerateAdditionalXmlFiles(introspectedTable));
716             }
717         }
718 
719         generatedJavaFiles.addAll(pluginAggregator
720                 .contextGenerateAdditionalJavaFiles());
721         generatedXmlFiles.addAll(pluginAggregator
722                 .contextGenerateAdditionalXmlFiles());
723     }
724 
725     /**
726      * Gets the connection.
727      *
728      * @return the connection
729      * @throws SQLException
730      *             the SQL exception
731      */
732     private Connection getConnection() throws SQLException {
733         Connection connection = ConnectionFactory.getInstance().getConnection(
734                 jdbcConnectionConfiguration);
735 
736         return connection;
737     }
738 
739     /**
740      * Close connection.
741      *
742      * @param connection
743      *            the connection
744      */
745     private void closeConnection(Connection connection) {
746         if (connection != null) {
747             try {
748                 connection.close();
749             } catch (SQLException e) {
750                 // ignore
751             }
752         }
753     }
754 
755     /**
756      * Auto delimit keywords.
757      *
758      * @return true, if successful
759      */
760     public boolean autoDelimitKeywords() {
761         return autoDelimitKeywords != null
762                 && autoDelimitKeywords.booleanValue();
763     }
764 }