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