1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package com.ibatis.sqlmap.engine.mapping.parameter;
17
18 import com.ibatis.common.beans.Probe;
19 import com.ibatis.common.beans.ProbeFactory;
20 import com.ibatis.common.resources.Resources;
21 import com.ibatis.sqlmap.client.SqlMapException;
22 import com.ibatis.sqlmap.client.extensions.TypeHandlerCallback;
23 import com.ibatis.sqlmap.engine.mapping.sql.SqlText;
24 import com.ibatis.sqlmap.engine.type.CustomTypeHandler;
25 import com.ibatis.sqlmap.engine.type.DomTypeMarker;
26 import com.ibatis.sqlmap.engine.type.TypeHandler;
27 import com.ibatis.sqlmap.engine.type.TypeHandlerFactory;
28
29 import java.util.ArrayList;
30 import java.util.List;
31 import java.util.StringTokenizer;
32
33
34
35
36 public class InlineParameterMapParser {
37
38
39 private static final Probe PROBE = ProbeFactory.getProbe();
40
41
42 private static final String PARAMETER_TOKEN = "#";
43
44
45 private static final String PARAM_DELIM = ":";
46
47
48
49
50
51
52
53
54
55
56
57 public SqlText parseInlineParameterMap(TypeHandlerFactory typeHandlerFactory, String sqlStatement) {
58 return parseInlineParameterMap(typeHandlerFactory, sqlStatement, null);
59 }
60
61
62
63
64
65
66
67
68
69
70
71
72
73 public SqlText parseInlineParameterMap(TypeHandlerFactory typeHandlerFactory, String sqlStatement,
74 Class parameterClass) {
75
76 List mappingList = new ArrayList<>();
77
78 StringTokenizer parser = new StringTokenizer(sqlStatement, PARAMETER_TOKEN, true);
79 StringBuilder newSqlBuffer = new StringBuilder();
80
81 String token = null;
82 String lastToken = null;
83 while (parser.hasMoreTokens()) {
84 token = parser.nextToken();
85 if (PARAMETER_TOKEN.equals(lastToken)) {
86 if (PARAMETER_TOKEN.equals(token)) {
87 newSqlBuffer.append(PARAMETER_TOKEN);
88 } else {
89 ParameterMapping mapping = null;
90 if (token.indexOf(PARAM_DELIM) > -1) {
91 mapping = oldParseMapping(token, parameterClass, typeHandlerFactory);
92 } else {
93 mapping = newParseMapping(token, parameterClass, typeHandlerFactory);
94 }
95
96 mappingList.add(mapping);
97 newSqlBuffer.append("?");
98 boolean hasMoreTokens = parser.hasMoreTokens();
99 if (hasMoreTokens) {
100 token = parser.nextToken();
101 }
102 if (!hasMoreTokens || !PARAMETER_TOKEN.equals(token)) {
103 throw new SqlMapException(
104 "Unterminated inline parameter in mapped statement near '" + newSqlBuffer.toString() + "'");
105 }
106 }
107 token = null;
108 } else if (!PARAMETER_TOKEN.equals(token)) {
109 newSqlBuffer.append(token);
110 }
111
112 lastToken = token;
113 }
114
115 String newSql = newSqlBuffer.toString();
116
117 ParameterMapping[] mappingArray = (ParameterMapping[]) mappingList
118 .toArray(new ParameterMapping[mappingList.size()]);
119
120 SqlText sqlText = new SqlText();
121 sqlText.setText(newSql);
122 sqlText.setParameterMappings(mappingArray);
123 return sqlText;
124 }
125
126
127
128
129
130
131
132
133
134
135
136
137
138 private ParameterMapping newParseMapping(String token, Class parameterClass, TypeHandlerFactory typeHandlerFactory) {
139 ParameterMapping mapping = new ParameterMapping();
140
141
142
143 StringTokenizer paramParser = new StringTokenizer(token, "=,", false);
144 mapping.setPropertyName(paramParser.nextToken());
145
146 while (paramParser.hasMoreTokens()) {
147 String field = paramParser.nextToken();
148 if (!paramParser.hasMoreTokens()) {
149 throw new SqlMapException("Incorrect inline parameter map format (missmatched name=value pairs): " + token);
150 }
151 String value = paramParser.nextToken();
152 if (field == null) {
153 throw new SqlMapException("Unrecognized parameter mapping field: '" + field + "' in " + token);
154 }
155 switch (field) {
156 case "javaType":
157 value = typeHandlerFactory.resolveAlias(value);
158 mapping.setJavaTypeName(value);
159 break;
160 case "jdbcType":
161 mapping.setJdbcTypeName(value);
162 break;
163 case "mode":
164 mapping.setMode(value);
165 break;
166 case "nullValue":
167 mapping.setNullValue(value);
168 break;
169 case "handler":
170 try {
171 value = typeHandlerFactory.resolveAlias(value);
172 Object impl = Resources.instantiate(value);
173 if (impl instanceof TypeHandlerCallback) {
174 mapping.setTypeHandler(new CustomTypeHandler((TypeHandlerCallback) impl));
175 } else if (impl instanceof TypeHandler) {
176 mapping.setTypeHandler((TypeHandler) impl);
177 } else {
178 throw new SqlMapException(
179 "The class " + value + " is not a valid implementation of TypeHandler or TypeHandlerCallback");
180 }
181 } catch (Exception e) {
182 throw new SqlMapException("Error loading class specified by handler field in " + token + ". Cause: " + e,
183 e);
184 }
185 break;
186 case "numericScale":
187 try {
188 Integer numericScale = Integer.valueOf(value);
189 if (numericScale.intValue() < 0) {
190 throw new SqlMapException("Value specified for numericScale must be greater than or equal to zero");
191 }
192 mapping.setNumericScale(numericScale);
193 } catch (NumberFormatException e) {
194 throw new SqlMapException("Value specified for numericScale is not a valid Integer");
195 }
196 break;
197 default:
198 throw new SqlMapException("Unrecognized parameter mapping field: '" + field + "' in " + token);
199 }
200 }
201
202 if (mapping.getTypeHandler() == null) {
203 TypeHandler handler;
204 if (parameterClass == null) {
205 handler = typeHandlerFactory.getUnkownTypeHandler();
206 } else {
207 handler = resolveTypeHandler(typeHandlerFactory, parameterClass, mapping.getPropertyName(),
208 mapping.getJavaTypeName(), mapping.getJdbcTypeName());
209 }
210 mapping.setTypeHandler(handler);
211 }
212
213 return mapping;
214 }
215
216
217
218
219
220
221
222
223
224
225
226
227
228 private ParameterMapping oldParseMapping(String token, Class parameterClass, TypeHandlerFactory typeHandlerFactory) {
229 ParameterMapping mapping = new ParameterMapping();
230 if (token.indexOf(PARAM_DELIM) <= -1) {
231 mapping.setPropertyName(token);
232 TypeHandler handler;
233 if (parameterClass == null) {
234 handler = typeHandlerFactory.getUnkownTypeHandler();
235 } else {
236 handler = resolveTypeHandler(typeHandlerFactory, parameterClass, token, null, null);
237 }
238 mapping.setTypeHandler(handler);
239 return mapping;
240 }
241 StringTokenizer paramParser = new StringTokenizer(token, PARAM_DELIM, true);
242 int n1 = paramParser.countTokens();
243 if (n1 == 3) {
244 String name = paramParser.nextToken();
245 paramParser.nextToken();
246 String type = paramParser.nextToken();
247 mapping.setPropertyName(name);
248 mapping.setJdbcTypeName(type);
249 TypeHandler handler;
250 if (parameterClass == null) {
251 handler = typeHandlerFactory.getUnkownTypeHandler();
252 } else {
253 handler = resolveTypeHandler(typeHandlerFactory, parameterClass, name, null, type);
254 }
255 mapping.setTypeHandler(handler);
256 } else {
257 if (n1 < 5) {
258 throw new SqlMapException("Incorrect inline parameter map format: " + token);
259 }
260 String name = paramParser.nextToken();
261 paramParser.nextToken();
262 String type = paramParser.nextToken();
263 paramParser.nextToken();
264 StringBuilder nullValue = new StringBuilder().append(paramParser.nextToken());
265 while (paramParser.hasMoreTokens()) {
266 nullValue.append(paramParser.nextToken());
267 }
268 mapping.setPropertyName(name);
269 mapping.setJdbcTypeName(type);
270 mapping.setNullValue(nullValue.toString());
271 TypeHandler handler;
272 if (parameterClass == null) {
273 handler = typeHandlerFactory.getUnkownTypeHandler();
274 } else {
275 handler = resolveTypeHandler(typeHandlerFactory, parameterClass, name, null, type);
276 }
277 mapping.setTypeHandler(handler);
278 }
279 return mapping;
280
281 }
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299 private TypeHandler resolveTypeHandler(TypeHandlerFactory typeHandlerFactory, Class clazz, String propertyName,
300 String javaType, String jdbcType) {
301 TypeHandler handler = null;
302 if (clazz == null) {
303
304 handler = typeHandlerFactory.getUnkownTypeHandler();
305 } else if (DomTypeMarker.class.isAssignableFrom(clazz)) {
306
307 handler = typeHandlerFactory.getTypeHandler(String.class, jdbcType);
308 } else if (java.util.Map.class.isAssignableFrom(clazz)) {
309
310 if (javaType == null) {
311 handler = typeHandlerFactory.getUnkownTypeHandler();
312
313
314
315 } else {
316 try {
317 javaType = typeHandlerFactory.resolveAlias(javaType);
318 Class javaClass = Resources.classForName(javaType);
319 handler = typeHandlerFactory.getTypeHandler(javaClass, jdbcType);
320 } catch (Exception e) {
321 throw new SqlMapException("Error. Could not set TypeHandler. Cause: " + e, e);
322 }
323 }
324 } else if (typeHandlerFactory.getTypeHandler(clazz, jdbcType) != null) {
325
326 handler = typeHandlerFactory.getTypeHandler(clazz, jdbcType);
327 } else {
328
329 if (javaType == null) {
330
331 Class type = PROBE.getPropertyTypeForGetter(clazz, propertyName);
332 handler = typeHandlerFactory.getTypeHandler(type, jdbcType);
333
334 } else {
335 try {
336 javaType = typeHandlerFactory.resolveAlias(javaType);
337 Class javaClass = Resources.classForName(javaType);
338 handler = typeHandlerFactory.getTypeHandler(javaClass, jdbcType);
339 } catch (Exception e) {
340 throw new SqlMapException("Error. Could not set TypeHandler. Cause: " + e, e);
341 }
342 }
343 }
344 return handler;
345 }
346
347 }