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

mybatis / generator / 1945

14 Jan 2026 02:23PM UTC coverage: 88.838% (+0.04%) from 88.799%
1945

Pull #1414

github

web-flow
Merge 5eec5583d into 5d3363986
Pull Request #1414: Major Refactoring - Context is now configuration only, Plugins Potentially Impacted

2347 of 3184 branches covered (73.71%)

490 of 582 new or added lines in 133 files covered. (84.19%)

2 existing lines in 1 file now uncovered.

11517 of 12964 relevant lines covered (88.84%)

0.89 hits per line

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

90.0
/core/mybatis-generator-core/src/main/java/org/mybatis/generator/internal/XmlFileMergerJaxp.java
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.messages.Messages.getString;
19

20
import java.io.File;
21
import java.io.IOException;
22
import java.io.InputStreamReader;
23
import java.io.StringReader;
24
import java.nio.charset.StandardCharsets;
25
import java.nio.file.Files;
26
import java.util.ArrayList;
27
import java.util.List;
28
import javax.xml.XMLConstants;
29
import javax.xml.parsers.DocumentBuilder;
30
import javax.xml.parsers.DocumentBuilderFactory;
31
import javax.xml.parsers.ParserConfigurationException;
32

33
import org.jspecify.annotations.Nullable;
34
import org.mybatis.generator.config.MergeConstants;
35
import org.mybatis.generator.exception.ShellException;
36
import org.w3c.dom.Comment;
37
import org.w3c.dom.Document;
38
import org.w3c.dom.DocumentType;
39
import org.w3c.dom.Element;
40
import org.w3c.dom.NamedNodeMap;
41
import org.w3c.dom.Node;
42
import org.w3c.dom.NodeList;
43
import org.w3c.dom.Text;
44
import org.xml.sax.EntityResolver;
45
import org.xml.sax.InputSource;
46
import org.xml.sax.SAXException;
47

48
/**
49
 * This class handles the task of merging changes into an existing XML file.
50
 *
51
 * @author Jeff Butler
52
 */
53
public class XmlFileMergerJaxp {
54
    private XmlFileMergerJaxp() {
55
    }
56

57
    private static class NullEntityResolver implements EntityResolver {
58
        /**
59
         * returns an empty reader. This is done so that the parser doesn't
60
         * attempt to read a DTD. We don't need that support for the merge, and
61
         * 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(String generatedXmlFile, File existingFile) throws ShellException {
73
        try {
NEW
74
            return getMergedSource(new InputSource(new StringReader(generatedXmlFile)),
×
75
                new InputSource(new InputStreamReader(
76
                        Files.newInputStream(existingFile.toPath()), StandardCharsets.UTF_8)),
×
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,
85
            InputSource existingFile, String existingFileName) throws IOException, SAXException,
86
            ParserConfigurationException, ShellException {
87

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

97
        Document existingDocument = builder.parse(existingFile);
1✔
98
        Document newDocument = builder.parse(newFile);
1✔
99

100
        DocumentType newDocType = newDocument.getDoctype();
1✔
101
        DocumentType existingDocType = existingDocument.getDoctype();
1✔
102

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

108
        Element existingRootElement = existingDocument.getDocumentElement();
1✔
109
        Element newRootElement = newDocument.getDocumentElement();
1✔
110

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

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

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

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

147
        for (Node node : nodesToDelete) {
1✔
148
            existingRootElement.removeChild(node);
1✔
149
        }
1✔
150

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

162
            Node newNode = existingDocument.importNode(node, true);
1✔
163
            if (firstChild == null) {
1!
164
                existingRootElement.appendChild(newNode);
×
165
            } else {
166
                existingRootElement.insertBefore(newNode, firstChild);
1✔
167
            }
168
        }
169

170
        // pretty print the result
171
        return prettyPrint(existingDocument);
1✔
172
    }
173

174
    private static String prettyPrint(Document document) throws ShellException {
175
        return new DomWriter(document).getFormattedDocument();
1✔
176
    }
177

178
    private static boolean isGeneratedNode(@Nullable Node node) {
179
        return node != null
1✔
180
                && node.getNodeType() == Node.ELEMENT_NODE
1✔
181
                && (isOldFormatNode(node) || isNewFormatNode(node));
1✔
182
    }
183

184
    private static boolean isOldFormatNode(Node node) {
185
        Element element = (Element) node;
1✔
186
        String id = element.getAttribute("id"); //$NON-NLS-1$
1✔
187
        return MergeConstants.idStartsWithPrefix(id);
1✔
188

189
    }
190

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

206
        return false;
1✔
207
    }
208

209
    private static boolean isWhiteSpace(Node node) {
210
        boolean rc = false;
1✔
211

212
        if (node.getNodeType() == Node.TEXT_NODE) {
1✔
213
            Text tn = (Text) node;
1✔
214
            if (tn.getData().trim().isEmpty()) {
1!
215
                rc = true;
1✔
216
            }
217
        }
218

219
        return rc;
1✔
220
    }
221
}
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