View Javadoc
1   /*
2    *    Copyright 2015-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 org.mybatis.scripting.freemarker;
17  
18  import java.io.IOException;
19  import java.util.List;
20  import java.util.Map;
21  
22  import freemarker.core.Environment;
23  import freemarker.ext.util.WrapperTemplateModel;
24  import freemarker.template.DefaultListAdapter;
25  import freemarker.template.SimpleScalar;
26  import freemarker.template.TemplateBooleanModel;
27  import freemarker.template.TemplateDateModel;
28  import freemarker.template.TemplateDirectiveBody;
29  import freemarker.template.TemplateDirectiveModel;
30  import freemarker.template.TemplateException;
31  import freemarker.template.TemplateModel;
32  import freemarker.template.TemplateNumberModel;
33  import freemarker.template.TemplateScalarModel;
34  
35  /**
36   * Custom FreeMarker directive for generating "#{paramName}" declarations in convenient way. Problem is FreeMarker
37   * supports this syntax natively and there are no chance to disable this (although it is deprecated). And to get
38   * "#{paramName}" we should write ${r"#{paramName}"}. With this directive you can write more simple:
39   * <p>
40   * <blockquote>
41   *
42   * <pre>
43   *     &lt;@p name="paramName"/&gt;
44   * </pre>
45   *
46   * </blockquote>
47   * <p>
48   * Also directive supports `value` attribute. If it is specified, param will take passed value and create the
49   * corresponding #{}-parameter. This is useful in loops:
50   * </p>
51   * <blockquote>
52   *
53   * <pre>
54   *     &lt;#list ids as id&gt;
55   *       &lt;@p value=id/&gt;
56   *       &lt;#if id_has_next&gt;,&lt;/#if&gt;
57   *     &lt;/#list&gt;
58   * </pre>
59   *
60   * </blockquote>
61   * <p>
62   * will be translated into
63   * </p>
64   * <blockquote>
65   *
66   * <pre>
67   *     #{_p0},#{_p1},#{_p2}
68   * </pre>
69   *
70   * </blockquote>
71   * <p>
72   * And MyBatis engine will convert it to `?`-params finally.
73   * </p>
74   *
75   * @author elwood
76   */
77  public class MyBatisParamDirective implements TemplateDirectiveModel {
78    public static String DEFAULT_KEY = "p";
79    public static String DATABASE_ID_KEY = "_databaseId";
80  
81    @Override
82    public void execute(Environment env, Map params, TemplateModel[] loopVars, TemplateDirectiveBody body)
83        throws TemplateException, IOException {
84      SimpleScalar name = (SimpleScalar) params.get("name");
85      if (params.containsKey("value")) {
86        Object valueObject = params.get("value");
87        Object value;
88        if (valueObject == null) {
89          value = null;
90        } else if (valueObject instanceof WrapperTemplateModel) {
91          value = ((WrapperTemplateModel) valueObject).getWrappedObject();
92        } else if (valueObject instanceof TemplateScalarModel) {
93          value = ((TemplateScalarModel) valueObject).getAsString();
94        } else if (valueObject instanceof TemplateNumberModel) {
95          value = ((TemplateNumberModel) valueObject).getAsNumber();
96        } else if (valueObject instanceof TemplateDateModel) {
97          value = ((TemplateDateModel) valueObject).getAsDate();
98        } else if (valueObject instanceof TemplateBooleanModel) {
99          value = ((TemplateBooleanModel) valueObject).getAsBoolean();
100       } else {
101         throw new UnsupportedOperationException(
102             String.format("Type %s is not supported yet in this context.", valueObject.getClass().getSimpleName()));
103       }
104 
105       TemplateModel generatedParamsObject = env.getGlobalVariables().get(FreeMarkerSqlSource.GENERATED_PARAMS_KEY);
106       List generatedParams;
107       if (generatedParamsObject instanceof DefaultListAdapter) {
108         generatedParams = (List) ((DefaultListAdapter) generatedParamsObject).getWrappedObject();
109       } else {
110         generatedParams = ((GeneratedParamsTemplateModel) generatedParamsObject).getGeneratedParams();
111       }
112       String generatedParamName = "_p" + generatedParams.size();
113       env.getOut().write(String.format("#{%s}", generatedParamName));
114       generatedParams.add(value);
115     } else {
116       env.getOut().write(String.format("#{%s}", name));
117     }
118   }
119 }