View Javadoc
1   /*
2    * Copyright 2004-2022 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.mapping.sql.dynamic;
17  
18  import com.ibatis.sqlmap.engine.impl.SqlMapExecutorDelegate;
19  import com.ibatis.sqlmap.engine.mapping.parameter.InlineParameterMapParser;
20  import com.ibatis.sqlmap.engine.mapping.parameter.ParameterMap;
21  import com.ibatis.sqlmap.engine.mapping.parameter.ParameterMapping;
22  import com.ibatis.sqlmap.engine.mapping.result.ResultMap;
23  import com.ibatis.sqlmap.engine.mapping.sql.Sql;
24  import com.ibatis.sqlmap.engine.mapping.sql.SqlChild;
25  import com.ibatis.sqlmap.engine.mapping.sql.SqlText;
26  import com.ibatis.sqlmap.engine.mapping.sql.dynamic.elements.*;
27  import com.ibatis.sqlmap.engine.mapping.sql.simple.SimpleDynamicSql;
28  import com.ibatis.sqlmap.engine.mapping.statement.MappedStatement;
29  import com.ibatis.sqlmap.engine.scope.StatementScope;
30  
31  import java.io.PrintWriter;
32  import java.io.StringWriter;
33  import java.util.ArrayList;
34  import java.util.Iterator;
35  import java.util.List;
36  
37  /**
38   * The Class DynamicSql.
39   */
40  public class DynamicSql implements Sql, DynamicParent {
41  
42    /** The Constant PARAM_PARSER. */
43    private static final InlineParameterMapParser PARAM_PARSER = new InlineParameterMapParser();
44  
45    /** The children. */
46    private List children = new ArrayList();
47  
48    /** The delegate. */
49    private SqlMapExecutorDelegate delegate;
50  
51    /**
52     * Instantiates a new dynamic sql.
53     *
54     * @param delegate
55     *          the delegate
56     */
57    public DynamicSql(SqlMapExecutorDelegate delegate) {
58      this.delegate = delegate;
59    }
60  
61    public String getSql(StatementScope statementScope, Object parameterObject) {
62      String sql = statementScope.getDynamicSql();
63      if (sql == null) {
64        process(statementScope, parameterObject);
65        sql = statementScope.getDynamicSql();
66      }
67      return sql;
68    }
69  
70    public ParameterMap getParameterMap(StatementScope statementScope, Object parameterObject) {
71      ParameterMap map = statementScope.getDynamicParameterMap();
72      if (map == null) {
73        process(statementScope, parameterObject);
74        map = statementScope.getDynamicParameterMap();
75      }
76      return map;
77    }
78  
79    public ResultMap getResultMap(StatementScope statementScope, Object parameterObject) {
80      return statementScope.getResultMap();
81    }
82  
83    public void cleanup(StatementScope statementScope) {
84      statementScope.setDynamicSql(null);
85      statementScope.setDynamicParameterMap(null);
86    }
87  
88    /**
89     * Process.
90     *
91     * @param statementScope
92     *          the statement scope
93     * @param parameterObject
94     *          the parameter object
95     */
96    private void process(StatementScope statementScope, Object parameterObject) {
97      SqlTagContext ctx = new SqlTagContext();
98      List localChildren = children;
99      processBodyChildren(statementScope, ctx, parameterObject, localChildren.iterator());
100 
101     ParameterMap map = new ParameterMap(delegate);
102     map.setId(statementScope.getStatement().getId() + "-InlineParameterMap");
103     map.setParameterClass(((MappedStatement) statementScope.getStatement()).getParameterClass());
104     map.setParameterMappingList(ctx.getParameterMappings());
105 
106     String dynSql = ctx.getBodyText();
107 
108     // Processes $substitutions$ after DynamicSql
109     if (SimpleDynamicSql.isSimpleDynamicSql(dynSql)) {
110       dynSql = new SimpleDynamicSql(delegate, dynSql).getSql(statementScope, parameterObject);
111     }
112 
113     statementScope.setDynamicSql(dynSql);
114     statementScope.setDynamicParameterMap(map);
115   }
116 
117   /**
118    * Process body children.
119    *
120    * @param statementScope
121    *          the statement scope
122    * @param ctx
123    *          the ctx
124    * @param parameterObject
125    *          the parameter object
126    * @param localChildren
127    *          the local children
128    */
129   private void processBodyChildren(StatementScope statementScope, SqlTagContext ctx, Object parameterObject,
130       Iterator localChildren) {
131     PrintWriter out = ctx.getWriter();
132     processBodyChildren(statementScope, ctx, parameterObject, localChildren, out);
133   }
134 
135   /**
136    * Process body children.
137    *
138    * @param statementScope
139    *          the statement scope
140    * @param ctx
141    *          the ctx
142    * @param parameterObject
143    *          the parameter object
144    * @param localChildren
145    *          the local children
146    * @param out
147    *          the out
148    */
149   private void processBodyChildren(StatementScope statementScope, SqlTagContext ctx, Object parameterObject,
150       Iterator localChildren, PrintWriter out) {
151     while (localChildren.hasNext()) {
152       SqlChild child = (SqlChild) localChildren.next();
153       if (child instanceof SqlText) {
154         SqlText sqlText = (SqlText) child;
155         String sqlStatement = sqlText.getText();
156         if (sqlText.isWhiteSpace()) {
157           out.print(sqlStatement);
158         } else if (!sqlText.isPostParseRequired()) {
159 
160           // BODY OUT
161           out.print(sqlStatement);
162 
163           ParameterMapping[] mappings = sqlText.getParameterMappings();
164           if (mappings != null) {
165             for (int i = 0, n = mappings.length; i < n; i++) {
166               ctx.addParameterMapping(mappings[i]);
167             }
168           }
169         } else {
170 
171           IterateContext itCtx = ctx.peekIterateContext();
172 
173           if (null != itCtx && itCtx.isAllowNext()) {
174             itCtx.next();
175             itCtx.setAllowNext(false);
176             if (!itCtx.hasNext()) {
177               itCtx.setFinal(true);
178             }
179           }
180 
181           if (itCtx != null) {
182             StringBuilder sqlStatementBuffer = new StringBuilder(sqlStatement);
183             iteratePropertyReplace(sqlStatementBuffer, itCtx);
184             sqlStatement = sqlStatementBuffer.toString();
185           }
186 
187           sqlText = PARAM_PARSER.parseInlineParameterMap(delegate.getTypeHandlerFactory(), sqlStatement);
188 
189           ParameterMapping[] mappings = sqlText.getParameterMappings();
190           out.print(sqlText.getText());
191           if (mappings != null) {
192             for (int i = 0, n = mappings.length; i < n; i++) {
193               ctx.addParameterMapping(mappings[i]);
194             }
195           }
196         }
197       } else if (child instanceof SqlTag) {
198         SqlTag tag = (SqlTag) child;
199         SqlTagHandler handler = tag.getHandler();
200         int response = SqlTagHandler.INCLUDE_BODY;
201         do {
202           StringWriter sw = new StringWriter();
203           PrintWriter pw = new PrintWriter(sw);
204 
205           response = handler.doStartFragment(ctx, tag, parameterObject);
206           if (response != SqlTagHandler.SKIP_BODY) {
207 
208             processBodyChildren(statementScope, ctx, parameterObject, tag.getChildren(), pw);
209             pw.flush();
210             pw.close();
211             StringBuilder body = new StringBuilder(sw.getBuffer());
212             response = handler.doEndFragment(ctx, tag, parameterObject, body);
213             handler.doPrepend(ctx, tag, parameterObject, body);
214 
215             if (response != SqlTagHandler.SKIP_BODY) {
216               if (body.length() > 0) {
217                 out.print(body.toString());
218               }
219             }
220 
221           }
222         } while (response == SqlTagHandler.REPEAT_BODY);
223 
224         ctx.popRemoveFirstPrependMarker(tag);
225 
226         if (ctx.peekIterateContext() != null && ctx.peekIterateContext().getTag() == tag) {
227           ctx.setAttribute(ctx.peekIterateContext().getTag(), null);
228           ctx.popIterateContext();
229         }
230 
231       }
232     }
233   }
234 
235   /**
236    * Iterate property replace.
237    *
238    * @param bodyContent
239    *          the body content
240    * @param iterate
241    *          the iterate
242    */
243   protected void iteratePropertyReplace(StringBuilder bodyContent, IterateContext iterate) {
244     if (iterate != null) {
245       String[] mappings = new String[] { "#", "$" };
246       for (int i = 0; i < mappings.length; i++) {
247         int startIndex = 0;
248         int endIndex = -1;
249         while (startIndex > -1 && startIndex < bodyContent.length()) {
250           startIndex = bodyContent.indexOf(mappings[i], endIndex + 1);
251           endIndex = bodyContent.indexOf(mappings[i], startIndex + 1);
252           if (startIndex > -1 && endIndex > -1) {
253             bodyContent.replace(startIndex + 1, endIndex,
254                 iterate.addIndexToTagProperty(bodyContent.substring(startIndex + 1, endIndex)));
255           }
256         }
257       }
258     }
259   }
260 
261   /**
262    * Replace.
263    *
264    * @param builder
265    *          the builder
266    * @param find
267    *          the find
268    * @param replace
269    *          the replace
270    */
271   protected static void replace(StringBuilder builder, String find, String replace) {
272     int pos = builder.toString().indexOf(find);
273     int len = find.length();
274     while (pos > -1) {
275       builder.replace(pos, pos + len, replace);
276       pos = builder.toString().indexOf(find);
277     }
278   }
279 
280   public void addChild(SqlChild child) {
281     children.add(child);
282   }
283 
284 }