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.config.xml;
17  
18  import static org.mybatis.generator.internal.util.messages.Messages.getString;
19  
20  import java.io.File;
21  import java.io.FileReader;
22  import java.io.IOException;
23  import java.io.InputStream;
24  import java.io.Reader;
25  import java.util.ArrayList;
26  import java.util.List;
27  import java.util.Properties;
28  
29  import javax.xml.XMLConstants;
30  import javax.xml.parsers.DocumentBuilder;
31  import javax.xml.parsers.DocumentBuilderFactory;
32  import javax.xml.parsers.ParserConfigurationException;
33  
34  import org.mybatis.generator.codegen.XmlConstants;
35  import org.mybatis.generator.config.Configuration;
36  import org.mybatis.generator.exception.XMLParserException;
37  import org.w3c.dom.Document;
38  import org.w3c.dom.DocumentType;
39  import org.w3c.dom.Element;
40  import org.w3c.dom.Node;
41  import org.xml.sax.InputSource;
42  import org.xml.sax.SAXException;
43  import org.xml.sax.SAXParseException;
44  
45  public class ConfigurationParser {
46  
47      private final List<String> warnings;
48      private final List<String> parseErrors;
49      private final Properties extraProperties;
50  
51      public ConfigurationParser(List<String> warnings) {
52          this(null, warnings);
53      }
54  
55      /**
56       * This constructor accepts a properties object which may be used to specify
57       * an additional property set.  Typically this property set will be Ant or Maven properties
58       * specified in the build.xml file or the POM.
59       *
60       * <p>If there are name collisions between the different property sets, they will be
61       * resolved in this order:
62       *
63       * <ol>
64       *   <li>System properties take highest precedence</li>
65       *   <li>Properties specified in the &lt;properties&gt; configuration
66       *       element are next</li>
67       *   <li>Properties specified in this "extra" property set are
68       *       lowest precedence.</li>
69       * </ol>
70       *
71       * @param extraProperties an (optional) set of properties used to resolve property
72       *     references in the configuration file
73       * @param warnings any warnings are added to this array
74       */
75      public ConfigurationParser(Properties extraProperties, List<String> warnings) {
76          super();
77          this.extraProperties = extraProperties;
78  
79          if (warnings == null) {
80              this.warnings = new ArrayList<>();
81          } else {
82              this.warnings = warnings;
83          }
84  
85          parseErrors = new ArrayList<>();
86      }
87  
88      public Configuration parseConfiguration(File inputFile) throws IOException,
89              XMLParserException {
90  
91          FileReader fr = new FileReader(inputFile);
92  
93          return parseConfiguration(fr);
94      }
95  
96      public Configuration parseConfiguration(Reader reader) throws IOException,
97              XMLParserException {
98  
99          InputSource is = new InputSource(reader);
100 
101         return parseConfiguration(is);
102     }
103 
104     public Configuration parseConfiguration(InputStream inputStream)
105             throws IOException, XMLParserException {
106 
107         InputSource is = new InputSource(inputStream);
108 
109         return parseConfiguration(is);
110     }
111 
112     private Configuration parseConfiguration(InputSource inputSource)
113             throws IOException, XMLParserException {
114         parseErrors.clear();
115         DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
116         factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
117         factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
118         factory.setValidating(true);
119 
120         try {
121             factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
122             DocumentBuilder builder = factory.newDocumentBuilder();
123             builder.setEntityResolver(new ParserEntityResolver());
124 
125             ParserErrorHandler handler = new ParserErrorHandler(warnings,
126                     parseErrors);
127             builder.setErrorHandler(handler);
128 
129             Document document = null;
130             try {
131                 document = builder.parse(inputSource);
132             } catch (SAXParseException e) {
133                 throw new XMLParserException(parseErrors);
134             } catch (SAXException e) {
135                 if (e.getException() == null) {
136                     parseErrors.add(e.getMessage());
137                 } else {
138                     parseErrors.add(e.getException().getMessage());
139                 }
140             }
141 
142             if (document == null || !parseErrors.isEmpty()) {
143                 throw new XMLParserException(parseErrors);
144             }
145 
146             Configuration config;
147             Element rootNode = document.getDocumentElement();
148             DocumentType docType = document.getDoctype();
149             if (rootNode.getNodeType() == Node.ELEMENT_NODE
150                     && docType.getPublicId().equals(
151                             XmlConstants.MYBATIS_GENERATOR_CONFIG_PUBLIC_ID)) {
152                 config = parseMyBatisGeneratorConfiguration(rootNode);
153             } else {
154                 throw new XMLParserException(getString("RuntimeError.5")); //$NON-NLS-1$
155             }
156 
157             if (!parseErrors.isEmpty()) {
158                 throw new XMLParserException(parseErrors);
159             }
160 
161             return config;
162         } catch (ParserConfigurationException e) {
163             parseErrors.add(e.getMessage());
164             throw new XMLParserException(parseErrors);
165         }
166     }
167 
168     private Configuration parseMyBatisGeneratorConfiguration(Element rootNode)
169             throws XMLParserException {
170         MyBatisGeneratorConfigurationParser parser = new MyBatisGeneratorConfigurationParser(
171                 extraProperties);
172         return parser.parseConfiguration(rootNode);
173     }
174 }