• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mybatis / generator / 1646

21 Apr 2025 10:17PM UTC coverage: 88.157% (-0.2%) from 88.328%
1646

push

github

hazendaz
[ci] Run auto formatting

2518 of 3412 branches covered (73.8%)

994 of 1117 new or added lines in 164 files covered. (88.99%)

23 existing lines in 12 files now uncovered.

10578 of 11999 relevant lines covered (88.16%)

0.88 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.91
/core/mybatis-generator-core/src/main/java/org/mybatis/generator/internal/XmlFileMergerJaxp.java
1
/*
2
 *    Copyright 2006-2025 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.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
 * This class handles the task of merging changes into an existing XML file.
51
 *
52
 * @author Jeff Butler
53
 */
54
public class XmlFileMergerJaxp {
55
    private XmlFileMergerJaxp() {
56
    }
57

58
    private static class NullEntityResolver implements EntityResolver {
59
        /**
60
         * returns an empty reader. This is done so that the parser doesn't attempt to read a DTD. We don't need that
61
         * support for the merge, and it can cause problems on systems that aren't Internet connected.
62
         */
63
        @Override
64
        public InputSource resolveEntity(String publicId, String systemId) {
65

66
            StringReader sr = new StringReader(""); //$NON-NLS-1$
1✔
67

68
            return new InputSource(sr);
1✔
69
        }
70
    }
71

72
    public static String getMergedSource(GeneratedXmlFile generatedXmlFile, File existingFile) throws ShellException {
73

74
        try {
75
            return getMergedSource(new InputSource(new StringReader(generatedXmlFile.getFormattedContent())),
×
76
                    new InputSource(new InputStreamReader(new FileInputStream(existingFile), StandardCharsets.UTF_8)),
NEW
77
                    existingFile.getName());
×
78
        } catch (IOException | SAXException | ParserConfigurationException e) {
×
79
            throw new ShellException(getString("Warning.13", //$NON-NLS-1$
×
80
                    existingFile.getName()), e);
×
81
        }
82
    }
83

84
    public static String getMergedSource(InputSource newFile, InputSource existingFile, String existingFileName)
85
            throws IOException, SAXException, ParserConfigurationException, ShellException {
86

87
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
1✔
88
        factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
1✔
89
        factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
1✔
90
        factory.setExpandEntityReferences(false);
1✔
91
        factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
1✔
92
        DocumentBuilder builder = factory.newDocumentBuilder();
1✔
93
        builder.setEntityResolver(new NullEntityResolver());
1✔
94

95
        Document existingDocument = builder.parse(existingFile);
1✔
96
        Document newDocument = builder.parse(newFile);
1✔
97

98
        DocumentType newDocType = newDocument.getDoctype();
1✔
99
        DocumentType existingDocType = existingDocument.getDoctype();
1✔
100

101
        if (!newDocType.getName().equals(existingDocType.getName())) {
1!
102
            throw new ShellException(getString("Warning.12", //$NON-NLS-1$
×
103
                    existingFileName));
104
        }
105

106
        Element existingRootElement = existingDocument.getDocumentElement();
1✔
107
        Element newRootElement = newDocument.getDocumentElement();
1✔
108

109
        // reconcile the root element attributes -
110
        // take all attributes from the new element and add to the existing
111
        // element
112

113
        // remove all attributes from the existing root element
114
        NamedNodeMap attributes = existingRootElement.getAttributes();
1✔
115
        int attributeCount = attributes.getLength();
1✔
116
        for (int i = attributeCount - 1; i >= 0; i--) {
1✔
117
            Node node = attributes.item(i);
1✔
118
            existingRootElement.removeAttribute(node.getNodeName());
1✔
119
        }
120

121
        // add attributes from the new root node to the old root node
122
        attributes = newRootElement.getAttributes();
1✔
123
        attributeCount = attributes.getLength();
1✔
124
        for (int i = 0; i < attributeCount; i++) {
1✔
125
            Node node = attributes.item(i);
1✔
126
            existingRootElement.setAttribute(node.getNodeName(), node.getNodeValue());
1✔
127
        }
128

129
        // remove the old generated elements and any
130
        // white space before the old nodes
131
        List<Node> nodesToDelete = new ArrayList<>();
1✔
132
        NodeList children = existingRootElement.getChildNodes();
1✔
133
        int length = children.getLength();
1✔
134
        for (int i = 0; i < length; i++) {
1✔
135
            Node node = children.item(i);
1✔
136
            if (isGeneratedNode(node)) {
1✔
137
                nodesToDelete.add(node);
1✔
138
            } else if (isWhiteSpace(node) && isGeneratedNode(children.item(i + 1))) {
1✔
139
                nodesToDelete.add(node);
1✔
140
            }
141
        }
142

143
        for (Node node : nodesToDelete) {
1✔
144
            existingRootElement.removeChild(node);
1✔
145
        }
1✔
146

147
        // add the new generated elements
148
        children = newRootElement.getChildNodes();
1✔
149
        length = children.getLength();
1✔
150
        Node firstChild = existingRootElement.getFirstChild();
1✔
151
        for (int i = 0; i < length; i++) {
1!
152
            Node node = children.item(i);
1✔
153
            // don't add the last node if it is only white space
154
            if (i == length - 1 && isWhiteSpace(node)) {
1!
155
                break;
1✔
156
            }
157

158
            Node newNode = existingDocument.importNode(node, true);
1✔
159
            if (firstChild == null) {
1!
160
                existingRootElement.appendChild(newNode);
×
161
            } else {
162
                existingRootElement.insertBefore(newNode, firstChild);
1✔
163
            }
164
        }
165

166
        // pretty print the result
167
        return prettyPrint(existingDocument);
1✔
168
    }
169

170
    private static String prettyPrint(Document document) throws ShellException {
171
        DomWriter dw = new DomWriter();
1✔
172
        return dw.toString(document);
1✔
173
    }
174

175
    private static boolean isGeneratedNode(Node node) {
176
        return node != null && node.getNodeType() == Node.ELEMENT_NODE
1✔
177
                && (isOldFormatNode(node) || isNewFormatNode(node));
1✔
178
    }
179

180
    private static boolean isOldFormatNode(Node node) {
181
        Element element = (Element) node;
1✔
182
        String id = element.getAttribute("id"); //$NON-NLS-1$
1✔
183
        return MergeConstants.idStartsWithPrefix(id);
1✔
184

185
    }
186

187
    private static boolean isNewFormatNode(Node node) {
188
        // check for new node format - if the first non-whitespace node
189
        // is an XML comment, and the comment includes
190
        // one of the old element tags,
191
        // then it is a generated node
192
        NodeList children = node.getChildNodes();
1✔
193
        int length = children.getLength();
1✔
194
        for (int i = 0; i < length; i++) {
1✔
195
            Node childNode = children.item(i);
1✔
196
            if (childNode != null && childNode.getNodeType() == Node.COMMENT_NODE) {
1!
197
                String commentData = ((Comment) childNode).getData();
1✔
198
                return MergeConstants.commentContainsTag(commentData);
1✔
199
            }
200
        }
201

202
        return false;
1✔
203
    }
204

205
    private static boolean isWhiteSpace(Node node) {
206
        boolean rc = false;
1✔
207

208
        if (node != null && node.getNodeType() == Node.TEXT_NODE) {
1!
209
            Text tn = (Text) node;
1✔
210
            if (tn.getData().trim().isEmpty()) {
1!
211
                rc = true;
1✔
212
            }
213
        }
214

215
        return rc;
1✔
216
    }
217
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc