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