View Javadoc
1   /*
2    *    Copyright 2009-2024 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.apache.ibatis.submitted.language;
17  
18  import java.sql.ResultSet;
19  import java.util.ArrayList;
20  import java.util.List;
21  import java.util.Map;
22  
23  import org.apache.ibatis.builder.BaseBuilder;
24  import org.apache.ibatis.builder.BuilderException;
25  import org.apache.ibatis.builder.ParameterExpression;
26  import org.apache.ibatis.builder.StaticSqlSource;
27  import org.apache.ibatis.mapping.ParameterMapping;
28  import org.apache.ibatis.mapping.SqlSource;
29  import org.apache.ibatis.parsing.GenericTokenParser;
30  import org.apache.ibatis.parsing.TokenHandler;
31  import org.apache.ibatis.reflection.MetaClass;
32  import org.apache.ibatis.session.Configuration;
33  import org.apache.ibatis.type.JdbcType;
34  
35  /**
36   * Just a test case. Not a real Velocity implementation.
37   */
38  public class VelocitySqlSourceBuilder extends BaseBuilder {
39  
40    private static final String parameterProperties = "javaType,jdbcType,mode,numericScale,resultMap,typeHandler,jdbcTypeName";
41  
42    public VelocitySqlSourceBuilder(Configuration configuration) {
43      super(configuration);
44    }
45  
46    public SqlSource parse(String originalSql, Class<?> parameterType) {
47      ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(configuration, parameterType);
48      GenericTokenParser parser = new GenericTokenParser("@{", "}", handler);
49      String sql = parser.parse(originalSql);
50      return new StaticSqlSource(configuration, sql, handler.getParameterMappings());
51    }
52  
53    private static class ParameterMappingTokenHandler extends BaseBuilder implements TokenHandler {
54  
55      private final List<ParameterMapping> parameterMappings = new ArrayList<>();
56      private final Class<?> parameterType;
57  
58      public ParameterMappingTokenHandler(Configuration configuration, Class<?> parameterType) {
59        super(configuration);
60        this.parameterType = parameterType;
61      }
62  
63      public List<ParameterMapping> getParameterMappings() {
64        return parameterMappings;
65      }
66  
67      @Override
68      public String handleToken(String content) {
69        parameterMappings.add(buildParameterMapping(content));
70        return "?";
71      }
72  
73      private ParameterMapping buildParameterMapping(String content) {
74        Map<String, String> propertiesMap = parseParameterMapping(content);
75        String property = propertiesMap.get("property");
76        String jdbcType = propertiesMap.get("jdbcType");
77        Class<?> propertyType;
78        if (typeHandlerRegistry.hasTypeHandler(parameterType)) {
79          propertyType = parameterType;
80        } else if (JdbcType.CURSOR.name().equals(jdbcType)) {
81          propertyType = ResultSet.class;
82        } else if (property != null) {
83          MetaClass metaClass = MetaClass.forClass(parameterType, configuration.getReflectorFactory());
84          if (metaClass.hasGetter(property)) {
85            propertyType = metaClass.getGetterType(property);
86          } else {
87            propertyType = Object.class;
88          }
89        } else {
90          propertyType = Object.class;
91        }
92        ParameterMapping.Builder builder = new ParameterMapping.Builder(configuration, property, propertyType);
93        if (jdbcType != null) {
94          builder.jdbcType(resolveJdbcType(jdbcType));
95        }
96        Class<?> javaType = null;
97        String typeHandlerAlias = null;
98        for (Map.Entry<String, String> entry : propertiesMap.entrySet()) {
99          String name = entry.getKey();
100         String value = entry.getValue();
101         if (name != null) {
102           switch (name) {
103             case "javaType":
104               javaType = resolveClass(value);
105               builder.javaType(javaType);
106               break;
107             case "jdbcType":
108               builder.jdbcType(resolveJdbcType(value));
109               break;
110             case "mode":
111               builder.mode(resolveParameterMode(value));
112               break;
113             case "numericScale":
114               builder.numericScale(Integer.valueOf(value));
115               break;
116             case "resultMap":
117               builder.resultMapId(value);
118               break;
119             case "typeHandler":
120               typeHandlerAlias = value;
121               break;
122             case "jdbcTypeName":
123               builder.jdbcTypeName(value);
124               break;
125             case "property":
126               break;
127             case "expression":
128               builder.expression(value);
129               break;
130             default:
131               throw new BuilderException("An invalid property '" + name + "' was found in mapping @{" + content
132                   + "}.  Valid properties are " + parameterProperties);
133           }
134         } else {
135           throw new BuilderException("An invalid property '" + name + "' was found in mapping @{" + content
136               + "}.  Valid properties are " + parameterProperties);
137         }
138       }
139       if (typeHandlerAlias != null) {
140         builder.typeHandler(resolveTypeHandler(javaType, typeHandlerAlias));
141       }
142       return builder.build();
143     }
144 
145     private Map<String, String> parseParameterMapping(String content) {
146       try {
147         return new ParameterExpression(content);
148       } catch (BuilderException ex) {
149         throw ex;
150       } catch (Exception ex) {
151         throw new BuilderException("Parsing error was found in mapping @{" + content
152             + "}.  Check syntax #{property|(expression), var1=value1, var2=value2, ...} ", ex);
153       }
154     }
155   }
156 
157 }