View Javadoc
1   /*
2    * Copyright 2004-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 com.ibatis.sqlmap.engine.builder.xml;
17  
18  import com.ibatis.common.xml.NodeletUtils;
19  import com.ibatis.sqlmap.engine.config.SqlSource;
20  import com.ibatis.sqlmap.engine.mapping.parameter.InlineParameterMapParser;
21  import com.ibatis.sqlmap.engine.mapping.sql.Sql;
22  import com.ibatis.sqlmap.engine.mapping.sql.SqlText;
23  import com.ibatis.sqlmap.engine.mapping.sql.dynamic.DynamicSql;
24  import com.ibatis.sqlmap.engine.mapping.sql.dynamic.elements.DynamicParent;
25  import com.ibatis.sqlmap.engine.mapping.sql.dynamic.elements.IterateTagHandler;
26  import com.ibatis.sqlmap.engine.mapping.sql.dynamic.elements.SqlTag;
27  import com.ibatis.sqlmap.engine.mapping.sql.dynamic.elements.SqlTagHandler;
28  import com.ibatis.sqlmap.engine.mapping.sql.dynamic.elements.SqlTagHandlerFactory;
29  import com.ibatis.sqlmap.engine.mapping.sql.raw.RawSql;
30  
31  import java.util.Properties;
32  
33  import org.w3c.dom.CharacterData;
34  import org.w3c.dom.Node;
35  import org.w3c.dom.NodeList;
36  
37  /**
38   * The Class XMLSqlSource.
39   */
40  public class XMLSqlSource implements SqlSource {
41  
42    /** The Constant PARAM_PARSER. */
43    private static final InlineParameterMapParser PARAM_PARSER = new InlineParameterMapParser();
44  
45    /** The state. */
46    private XmlParserState state;
47  
48    /** The parent node. */
49    private Node parentNode;
50  
51    /**
52     * Instantiates a new XML sql source.
53     *
54     * @param config
55     *          the config
56     * @param parentNode
57     *          the parent node
58     */
59    public XMLSqlSource(XmlParserState config, Node parentNode) {
60      this.state = config;
61      this.parentNode = parentNode;
62    }
63  
64    @Override
65    public Sql getSql() {
66      state.getConfig().getErrorContext().setActivity("processing an SQL statement");
67  
68      boolean isDynamic = false;
69      StringBuilder sqlBuffer = new StringBuilder();
70      DynamicSql dynamic = new DynamicSql(state.getConfig().getClient().getDelegate());
71      isDynamic = parseDynamicTags(parentNode, dynamic, sqlBuffer, isDynamic, false);
72      String sqlStatement = sqlBuffer.toString();
73      if (isDynamic) {
74        return dynamic;
75      }
76      return new RawSql(sqlStatement);
77    }
78  
79    /**
80     * Parses the dynamic tags.
81     *
82     * @param node
83     *          the node
84     * @param dynamic
85     *          the dynamic
86     * @param sqlBuffer
87     *          the sql buffer
88     * @param isDynamic
89     *          the is dynamic
90     * @param postParseRequired
91     *          the post parse required
92     *
93     * @return true, if successful
94     */
95    private boolean parseDynamicTags(Node node, DynamicParent dynamic, StringBuilder sqlBuffer, boolean isDynamic,
96        boolean postParseRequired) {
97      state.getConfig().getErrorContext().setActivity("parsing dynamic SQL tags");
98  
99      NodeList children = node.getChildNodes();
100     for (int i = 0; i < children.getLength(); i++) {
101       Node child = children.item(i);
102       String nodeName = child.getNodeName();
103       if (child.getNodeType() == Node.CDATA_SECTION_NODE || child.getNodeType() == Node.TEXT_NODE) {
104 
105         String data = ((CharacterData) child).getData();
106         data = NodeletUtils.parsePropertyTokens(data, state.getGlobalProps());
107 
108         SqlText sqlText;
109 
110         if (postParseRequired) {
111           sqlText = new SqlText();
112           sqlText.setPostParseRequired(postParseRequired);
113           sqlText.setText(data);
114         } else {
115           sqlText = PARAM_PARSER
116               .parseInlineParameterMap(state.getConfig().getClient().getDelegate().getTypeHandlerFactory(), data, null);
117           sqlText.setPostParseRequired(postParseRequired);
118         }
119 
120         dynamic.addChild(sqlText);
121 
122         sqlBuffer.append(data);
123       } else if ("include".equals(nodeName)) {
124         Properties attributes = NodeletUtils.parseAttributes(child, state.getGlobalProps());
125         String refid = (String) attributes.get("refid");
126         Node includeNode = (Node) state.getSqlIncludes().get(refid);
127         if (includeNode == null) {
128           String nsrefid = state.applyNamespace(refid);
129           includeNode = (Node) state.getSqlIncludes().get(nsrefid);
130           if (includeNode == null) {
131             throw new RuntimeException("Could not find SQL statement to include with refid '" + refid + "'");
132           }
133         }
134         isDynamic = parseDynamicTags(includeNode, dynamic, sqlBuffer, isDynamic, false);
135       } else {
136         state.getConfig().getErrorContext().setMoreInfo("Check the dynamic tags.");
137 
138         SqlTagHandler handler = SqlTagHandlerFactory.getSqlTagHandler(nodeName);
139         if (handler != null) {
140           isDynamic = true;
141 
142           SqlTag tag = new SqlTag();
143           tag.setName(nodeName);
144           tag.setHandler(handler);
145 
146           Properties attributes = NodeletUtils.parseAttributes(child, state.getGlobalProps());
147 
148           tag.setPrependAttr(attributes.getProperty("prepend"));
149           tag.setPropertyAttr(attributes.getProperty("property"));
150           tag.setRemoveFirstPrepend(attributes.getProperty("removeFirstPrepend"));
151 
152           tag.setOpenAttr(attributes.getProperty("open"));
153           tag.setCloseAttr(attributes.getProperty("close"));
154 
155           tag.setComparePropertyAttr(attributes.getProperty("compareProperty"));
156           tag.setCompareValueAttr(attributes.getProperty("compareValue"));
157           tag.setConjunctionAttr(attributes.getProperty("conjunction"));
158 
159           // an iterate ancestor requires a post parse
160 
161           if (dynamic instanceof SqlTag) {
162             SqlTag parentSqlTag = (SqlTag) dynamic;
163             if (parentSqlTag.isPostParseRequired() || tag.getHandler() instanceof IterateTagHandler) {
164               tag.setPostParseRequired(true);
165             }
166           } else if (dynamic instanceof DynamicSql && tag.getHandler() instanceof IterateTagHandler) {
167             tag.setPostParseRequired(true);
168           }
169 
170           dynamic.addChild(tag);
171 
172           if (child.hasChildNodes()) {
173             isDynamic = parseDynamicTags(child, tag, sqlBuffer, isDynamic, tag.isPostParseRequired());
174           }
175         }
176       }
177     }
178     state.getConfig().getErrorContext().setMoreInfo(null);
179     return isDynamic;
180   }
181 
182 }