1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.mybatis.generator.internal;
17
18 import static org.mybatis.generator.internal.util.messages.Messages.getString;
19
20 import java.io.File;
21 import java.io.FileInputStream;
22 import java.io.IOException;
23 import java.io.InputStreamReader;
24 import java.io.StringReader;
25 import java.nio.charset.StandardCharsets;
26 import java.util.ArrayList;
27 import java.util.List;
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.api.GeneratedXmlFile;
35 import org.mybatis.generator.config.MergeConstants;
36 import org.mybatis.generator.exception.ShellException;
37 import org.w3c.dom.Comment;
38 import org.w3c.dom.Document;
39 import org.w3c.dom.DocumentType;
40 import org.w3c.dom.Element;
41 import org.w3c.dom.NamedNodeMap;
42 import org.w3c.dom.Node;
43 import org.w3c.dom.NodeList;
44 import org.w3c.dom.Text;
45 import org.xml.sax.EntityResolver;
46 import org.xml.sax.InputSource;
47 import org.xml.sax.SAXException;
48
49
50
51
52
53
54 public class XmlFileMergerJaxp {
55 private XmlFileMergerJaxp() {
56 }
57
58 private static class NullEntityResolver implements EntityResolver {
59
60
61
62
63
64 @Override
65 public InputSource resolveEntity(String publicId, String systemId) {
66
67 StringReader sr = new StringReader("");
68
69 return new InputSource(sr);
70 }
71 }
72
73 public static String getMergedSource(GeneratedXmlFile generatedXmlFile,
74 File existingFile) throws ShellException {
75
76 try {
77 return getMergedSource(new InputSource(new StringReader(generatedXmlFile.getFormattedContent())),
78 new InputSource(new InputStreamReader(new FileInputStream(existingFile), StandardCharsets.UTF_8)),
79 existingFile.getName());
80 } catch (IOException | SAXException | ParserConfigurationException e) {
81 throw new ShellException(getString("Warning.13",
82 existingFile.getName()), e);
83 }
84 }
85
86 public static String getMergedSource(InputSource newFile,
87 InputSource existingFile, String existingFileName) throws IOException, SAXException,
88 ParserConfigurationException, ShellException {
89
90 DocumentBuilderFactory factory = DocumentBuilderFactory
91 .newInstance();
92 factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
93 factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
94 factory.setExpandEntityReferences(false);
95 factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
96 DocumentBuilder builder = factory.newDocumentBuilder();
97 builder.setEntityResolver(new NullEntityResolver());
98
99 Document existingDocument = builder.parse(existingFile);
100 Document newDocument = builder.parse(newFile);
101
102 DocumentType newDocType = newDocument.getDoctype();
103 DocumentType existingDocType = existingDocument.getDoctype();
104
105 if (!newDocType.getName().equals(existingDocType.getName())) {
106 throw new ShellException(getString("Warning.12",
107 existingFileName));
108 }
109
110 Element existingRootElement = existingDocument.getDocumentElement();
111 Element newRootElement = newDocument.getDocumentElement();
112
113
114
115
116
117
118 NamedNodeMap attributes = existingRootElement.getAttributes();
119 int attributeCount = attributes.getLength();
120 for (int i = attributeCount - 1; i >= 0; i--) {
121 Node node = attributes.item(i);
122 existingRootElement.removeAttribute(node.getNodeName());
123 }
124
125
126 attributes = newRootElement.getAttributes();
127 attributeCount = attributes.getLength();
128 for (int i = 0; i < attributeCount; i++) {
129 Node node = attributes.item(i);
130 existingRootElement.setAttribute(node.getNodeName(), node
131 .getNodeValue());
132 }
133
134
135
136 List<Node> nodesToDelete = new ArrayList<>();
137 NodeList children = existingRootElement.getChildNodes();
138 int length = children.getLength();
139 for (int i = 0; i < length; i++) {
140 Node node = children.item(i);
141 if (isGeneratedNode(node)) {
142 nodesToDelete.add(node);
143 } else if (isWhiteSpace(node)
144 && isGeneratedNode(children.item(i + 1))) {
145 nodesToDelete.add(node);
146 }
147 }
148
149 for (Node node : nodesToDelete) {
150 existingRootElement.removeChild(node);
151 }
152
153
154 children = newRootElement.getChildNodes();
155 length = children.getLength();
156 Node firstChild = existingRootElement.getFirstChild();
157 for (int i = 0; i < length; i++) {
158 Node node = children.item(i);
159
160 if (i == length - 1 && isWhiteSpace(node)) {
161 break;
162 }
163
164 Node newNode = existingDocument.importNode(node, true);
165 if (firstChild == null) {
166 existingRootElement.appendChild(newNode);
167 } else {
168 existingRootElement.insertBefore(newNode, firstChild);
169 }
170 }
171
172
173 return prettyPrint(existingDocument);
174 }
175
176 private static String prettyPrint(Document document) throws ShellException {
177 DomWriter dw = new DomWriter();
178 return dw.toString(document);
179 }
180
181 private static boolean isGeneratedNode(Node node) {
182 return node != null
183 && node.getNodeType() == Node.ELEMENT_NODE
184 && (isOldFormatNode(node) || isNewFormatNode(node));
185 }
186
187 private static boolean isOldFormatNode(Node node) {
188 Element element = (Element) node;
189 String id = element.getAttribute("id");
190 if (id != null) {
191 return MergeConstants.idStartsWithPrefix(id);
192 }
193
194 return false;
195 }
196
197 private static boolean isNewFormatNode(Node node) {
198
199
200
201
202 NodeList children = node.getChildNodes();
203 int length = children.getLength();
204 for (int i = 0; i < length; i++) {
205 Node childNode = children.item(i);
206 if (childNode != null && childNode.getNodeType() == Node.COMMENT_NODE) {
207 String commentData = ((Comment) childNode).getData();
208 return MergeConstants.commentContainsTag(commentData);
209 }
210 }
211
212 return false;
213 }
214
215 private static boolean isWhiteSpace(Node node) {
216 boolean rc = false;
217
218 if (node != null && node.getNodeType() == Node.TEXT_NODE) {
219 Text tn = (Text) node;
220 if (tn.getData().trim().length() == 0) {
221 rc = true;
222 }
223 }
224
225 return rc;
226 }
227 }