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