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