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

mybatis / mybatis-3 / 2731

01 Mar 2025 01:53AM UTC coverage: 87.241% (+0.02%) from 87.226%
2731

Pull #3418

github

web-flow
Merge 24338207d into b90a0f870
Pull Request #3418: Share blank nodes

3683 of 4489 branches covered (82.04%)

5 of 5 new or added lines in 1 file covered. (100.0%)

2 existing lines in 1 file now uncovered.

9675 of 11090 relevant lines covered (87.24%)

0.87 hits per line

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

97.62
/src/main/java/org/apache/ibatis/scripting/xmltags/XMLScriptBuilder.java
1
/*
2
 *    Copyright 2009-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.apache.ibatis.scripting.xmltags;
17

18
import java.util.ArrayList;
19
import java.util.HashMap;
20
import java.util.List;
21
import java.util.Map;
22
import java.util.concurrent.ConcurrentHashMap;
23

24
import org.apache.ibatis.builder.BaseBuilder;
25
import org.apache.ibatis.builder.BuilderException;
26
import org.apache.ibatis.mapping.SqlSource;
27
import org.apache.ibatis.parsing.XNode;
28
import org.apache.ibatis.scripting.defaults.RawSqlSource;
29
import org.apache.ibatis.session.Configuration;
30
import org.w3c.dom.Node;
31
import org.w3c.dom.NodeList;
32

33
/**
34
 * @author Clinton Begin
35
 */
36
public class XMLScriptBuilder extends BaseBuilder {
37

38
  private final XNode context;
39
  private boolean isDynamic;
40
  private final Class<?> parameterType;
41
  private final Map<String, NodeHandler> nodeHandlerMap = new HashMap<>();
1✔
42
  private final static Map<String, StaticTextSqlNode> EMPTY_TEXT_NODE = new ConcurrentHashMap<>();
1✔
43

44
  public XMLScriptBuilder(Configuration configuration, XNode context) {
45
    this(configuration, context, null);
1✔
46
  }
1✔
47

48
  public XMLScriptBuilder(Configuration configuration, XNode context, Class<?> parameterType) {
49
    super(configuration);
1✔
50
    this.context = context;
1✔
51
    this.parameterType = parameterType;
1✔
52
    initNodeHandlerMap();
1✔
53
  }
1✔
54

55
  private void initNodeHandlerMap() {
56
    nodeHandlerMap.put("trim", new TrimHandler());
1✔
57
    nodeHandlerMap.put("where", new WhereHandler());
1✔
58
    nodeHandlerMap.put("set", new SetHandler());
1✔
59
    nodeHandlerMap.put("foreach", new ForEachHandler());
1✔
60
    nodeHandlerMap.put("if", new IfHandler());
1✔
61
    nodeHandlerMap.put("choose", new ChooseHandler());
1✔
62
    nodeHandlerMap.put("when", new IfHandler());
1✔
63
    nodeHandlerMap.put("otherwise", new OtherwiseHandler());
1✔
64
    nodeHandlerMap.put("bind", new BindHandler());
1✔
65
  }
1✔
66

67
  public SqlSource parseScriptNode() {
68
    MixedSqlNode rootSqlNode = parseDynamicTags(context);
1✔
69
    SqlSource sqlSource;
70
    if (isDynamic) {
1✔
71
      sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
1✔
72
    } else {
73
      sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
1✔
74
    }
75
    return sqlSource;
1✔
76
  }
77

78
  protected MixedSqlNode parseDynamicTags(XNode node) {
79
    List<SqlNode> contents = new ArrayList<>();
1✔
80
    NodeList children = node.getNode().getChildNodes();
1✔
81
    for (int i = 0; i < children.getLength(); i++) {
1✔
82
      XNode child = node.newXNode(children.item(i));
1✔
83
      if (child.getNode().getNodeType() == Node.CDATA_SECTION_NODE || child.getNode().getNodeType() == Node.TEXT_NODE) {
1✔
84
        String data = child.getStringBody("");
1✔
85
        if (data.trim().isEmpty()) {
1✔
86
          StaticTextSqlNode staticTextSqlNode = EMPTY_TEXT_NODE.computeIfAbsent(data, StaticTextSqlNode::new);
1✔
87
          contents.add(staticTextSqlNode);
1✔
88
          continue;
1✔
89
        }
90
        TextSqlNode textSqlNode = new TextSqlNode(data);
1✔
91
        if (textSqlNode.isDynamic()) {
1✔
92
          contents.add(textSqlNode);
1✔
93
          isDynamic = true;
1✔
94
        } else {
95
          contents.add(new StaticTextSqlNode(data));
1✔
96
        }
97
      } else if (child.getNode().getNodeType() == Node.ELEMENT_NODE) { // issue #628
1!
98
        String nodeName = child.getNode().getNodeName();
1✔
99
        NodeHandler handler = nodeHandlerMap.get(nodeName);
1✔
100
        if (handler == null) {
1!
UNCOV
101
          throw new BuilderException("Unknown element <" + nodeName + "> in SQL statement.");
×
102
        }
103
        handler.handleNode(child, contents);
1✔
104
        isDynamic = true;
1✔
105
      }
106
    }
107
    return new MixedSqlNode(contents);
1✔
108
  }
109

110
  private interface NodeHandler {
111
    void handleNode(XNode nodeToHandle, List<SqlNode> targetContents);
112
  }
113

114
  private static class BindHandler implements NodeHandler {
115
    public BindHandler() {
1✔
116
      // Prevent Synthetic Access
117
    }
1✔
118

119
    @Override
120
    public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
121
      final String name = nodeToHandle.getStringAttribute("name");
1✔
122
      final String expression = nodeToHandle.getStringAttribute("value");
1✔
123
      final VarDeclSqlNode node = new VarDeclSqlNode(name, expression);
1✔
124
      targetContents.add(node);
1✔
125
    }
1✔
126
  }
127

128
  private class TrimHandler implements NodeHandler {
129
    public TrimHandler() {
1✔
130
      // Prevent Synthetic Access
131
    }
1✔
132

133
    @Override
134
    public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
135
      MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
1✔
136
      String prefix = nodeToHandle.getStringAttribute("prefix");
1✔
137
      String prefixOverrides = nodeToHandle.getStringAttribute("prefixOverrides");
1✔
138
      String suffix = nodeToHandle.getStringAttribute("suffix");
1✔
139
      String suffixOverrides = nodeToHandle.getStringAttribute("suffixOverrides");
1✔
140
      TrimSqlNode trim = new TrimSqlNode(configuration, mixedSqlNode, prefix, prefixOverrides, suffix, suffixOverrides);
1✔
141
      targetContents.add(trim);
1✔
142
    }
1✔
143
  }
144

145
  private class WhereHandler implements NodeHandler {
146
    public WhereHandler() {
1✔
147
      // Prevent Synthetic Access
148
    }
1✔
149

150
    @Override
151
    public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
152
      MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
1✔
153
      WhereSqlNode where = new WhereSqlNode(configuration, mixedSqlNode);
1✔
154
      targetContents.add(where);
1✔
155
    }
1✔
156
  }
157

158
  private class SetHandler implements NodeHandler {
159
    public SetHandler() {
1✔
160
      // Prevent Synthetic Access
161
    }
1✔
162

163
    @Override
164
    public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
165
      MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
1✔
166
      SetSqlNode set = new SetSqlNode(configuration, mixedSqlNode);
1✔
167
      targetContents.add(set);
1✔
168
    }
1✔
169
  }
170

171
  private class ForEachHandler implements NodeHandler {
172
    public ForEachHandler() {
1✔
173
      // Prevent Synthetic Access
174
    }
1✔
175

176
    @Override
177
    public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
178
      MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
1✔
179
      String collection = nodeToHandle.getStringAttribute("collection");
1✔
180
      Boolean nullable = nodeToHandle.getBooleanAttribute("nullable");
1✔
181
      String item = nodeToHandle.getStringAttribute("item");
1✔
182
      String index = nodeToHandle.getStringAttribute("index");
1✔
183
      String open = nodeToHandle.getStringAttribute("open");
1✔
184
      String close = nodeToHandle.getStringAttribute("close");
1✔
185
      String separator = nodeToHandle.getStringAttribute("separator");
1✔
186
      ForEachSqlNode forEachSqlNode = new ForEachSqlNode(configuration, mixedSqlNode, collection, nullable, index, item,
1✔
187
          open, close, separator);
188
      targetContents.add(forEachSqlNode);
1✔
189
    }
1✔
190
  }
191

192
  private class IfHandler implements NodeHandler {
193
    public IfHandler() {
1✔
194
      // Prevent Synthetic Access
195
    }
1✔
196

197
    @Override
198
    public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
199
      MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
1✔
200
      String test = nodeToHandle.getStringAttribute("test");
1✔
201
      IfSqlNode ifSqlNode = new IfSqlNode(mixedSqlNode, test);
1✔
202
      targetContents.add(ifSqlNode);
1✔
203
    }
1✔
204
  }
205

206
  private class OtherwiseHandler implements NodeHandler {
207
    public OtherwiseHandler() {
1✔
208
      // Prevent Synthetic Access
209
    }
1✔
210

211
    @Override
212
    public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
213
      MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
1✔
214
      targetContents.add(mixedSqlNode);
1✔
215
    }
1✔
216
  }
217

218
  private class ChooseHandler implements NodeHandler {
219
    public ChooseHandler() {
1✔
220
      // Prevent Synthetic Access
221
    }
1✔
222

223
    @Override
224
    public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
225
      List<SqlNode> whenSqlNodes = new ArrayList<>();
1✔
226
      List<SqlNode> otherwiseSqlNodes = new ArrayList<>();
1✔
227
      handleWhenOtherwiseNodes(nodeToHandle, whenSqlNodes, otherwiseSqlNodes);
1✔
228
      SqlNode defaultSqlNode = getDefaultSqlNode(otherwiseSqlNodes);
1✔
229
      ChooseSqlNode chooseSqlNode = new ChooseSqlNode(whenSqlNodes, defaultSqlNode);
1✔
230
      targetContents.add(chooseSqlNode);
1✔
231
    }
1✔
232

233
    private void handleWhenOtherwiseNodes(XNode chooseSqlNode, List<SqlNode> ifSqlNodes,
234
        List<SqlNode> defaultSqlNodes) {
235
      List<XNode> children = chooseSqlNode.getChildren();
1✔
236
      for (XNode child : children) {
1✔
237
        String nodeName = child.getNode().getNodeName();
1✔
238
        NodeHandler handler = nodeHandlerMap.get(nodeName);
1✔
239
        if (handler instanceof IfHandler) {
1✔
240
          handler.handleNode(child, ifSqlNodes);
1✔
241
        } else if (handler instanceof OtherwiseHandler) {
1!
242
          handler.handleNode(child, defaultSqlNodes);
1✔
243
        }
244
      }
1✔
245
    }
1✔
246

247
    private SqlNode getDefaultSqlNode(List<SqlNode> defaultSqlNodes) {
248
      SqlNode defaultSqlNode = null;
1✔
249
      if (defaultSqlNodes.size() == 1) {
1!
250
        defaultSqlNode = defaultSqlNodes.get(0);
1✔
251
      } else if (defaultSqlNodes.size() > 1) {
×
UNCOV
252
        throw new BuilderException("Too many default (otherwise) elements in choose statement.");
×
253
      }
254
      return defaultSqlNode;
1✔
255
    }
256
  }
257

258
}
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

© 2025 Coveralls, Inc