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