1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.mybatis.scripting.thymeleaf.processor;
17
18 import java.lang.reflect.Array;
19 import java.util.Collection;
20 import java.util.function.UnaryOperator;
21
22 import org.mybatis.scripting.thymeleaf.MyBatisBindingContext;
23 import org.thymeleaf.context.ITemplateContext;
24 import org.thymeleaf.engine.AttributeName;
25 import org.thymeleaf.engine.EngineEventUtils;
26 import org.thymeleaf.engine.IterationStatusVar;
27 import org.thymeleaf.model.IProcessableElementTag;
28 import org.thymeleaf.processor.element.AbstractAttributeTagProcessor;
29 import org.thymeleaf.processor.element.IElementTagStructureHandler;
30 import org.thymeleaf.standard.expression.IStandardExpression;
31 import org.thymeleaf.standard.expression.StandardExpressionExecutionContext;
32 import org.thymeleaf.templatemode.TemplateMode;
33
34
35
36
37
38
39
40
41
42
43 public class MyBatisParamTagProcessor extends AbstractAttributeTagProcessor {
44
45 private static final int PRECEDENCE = 1400;
46 private static final String ATTR_NAME = "p";
47
48 private final StandardExpressionExecutionContext expressionExecutionContext;
49
50 private UnaryOperator<String> bindVariableRender = BindVariableRender.BuiltIn.MYBATIS;
51
52
53
54
55
56
57
58
59
60 public MyBatisParamTagProcessor(final TemplateMode templateMode, final String prefix) {
61 super(templateMode, prefix, null, false, ATTR_NAME, true, PRECEDENCE, true);
62 expressionExecutionContext = templateMode == TemplateMode.TEXT ? StandardExpressionExecutionContext.RESTRICTED
63 : StandardExpressionExecutionContext.NORMAL;
64 }
65
66
67
68
69
70
71
72
73
74
75 public void setBindVariableRender(UnaryOperator<String> bindVariableRender) {
76 this.bindVariableRender = bindVariableRender;
77 }
78
79
80
81
82 @Override
83 protected void doProcess(ITemplateContext context, IProcessableElementTag tag, AttributeName attributeName,
84 String attributeValue, IElementTagStructureHandler structureHandler) {
85 if (attributeValue.contains("${")) {
86 attributeValue = getExpressionEvaluatedText(context, tag, attributeName, attributeValue);
87 }
88
89 Pair parameterAndOptionPair = Pair.parse(attributeValue, ',');
90 String parameterPath = parameterAndOptionPair.left;
91 String options = parameterAndOptionPair.right;
92
93 Pair objectNameAndPropertyPathPair = Pair.parse(parameterPath, '.');
94 String objectName = objectNameAndPropertyPathPair.left;
95 String nestedPropertyPath = objectNameAndPropertyPathPair.right;
96
97 String body;
98 String iterationObjectName = objectName + "Stat";
99 if (context.containsVariable(iterationObjectName)) {
100 MyBatisBindingContext bindingContext = MyBatisBindingContext.load(context);
101 IterationStatusVar iterationStatus = (IterationStatusVar) context.getVariable(iterationObjectName);
102 String iterationObjectVariableName = bindingContext.generateUniqueName(objectName, iterationStatus);
103 if (!bindingContext.containsCustomBindVariable(iterationObjectVariableName)) {
104 bindingContext.setCustomBindVariable(iterationObjectVariableName, iterationStatus.getCurrent());
105 }
106 if (nestedPropertyPath.isEmpty()) {
107 body = bindVariableRender.apply(iterationObjectVariableName + options);
108 } else {
109 Object value = getExpressionEvaluatedValue(context, tag, attributeName, parameterPath);
110 if (isCollectionOrArray(value)) {
111 body = generateCollectionBindVariables(value, iterationObjectVariableName + nestedPropertyPath, options);
112 } else {
113 body = bindVariableRender.apply(iterationObjectVariableName + nestedPropertyPath + options);
114 }
115 }
116 } else {
117 Object value = nestedPropertyPath.isEmpty() ? context.getVariable(objectName)
118 : getExpressionEvaluatedValue(context, tag, attributeName, parameterPath);
119 if (isCollectionOrArray(value)) {
120 body = generateCollectionBindVariables(value, parameterPath, options);
121 } else {
122 body = bindVariableRender.apply(attributeValue);
123 }
124 }
125 structureHandler.setBody(body, false);
126 }
127
128 private Object getExpressionEvaluatedValue(ITemplateContext context, IProcessableElementTag tag,
129 AttributeName attributeName, String parameterValue) {
130 IStandardExpression expression = EngineEventUtils.computeAttributeExpression(context, tag, attributeName,
131 "${" + parameterValue + "}");
132 return expression.execute(context, this.expressionExecutionContext);
133 }
134
135 private String getExpressionEvaluatedText(ITemplateContext context, IProcessableElementTag tag,
136 AttributeName attributeName, String parameterValue) {
137 IStandardExpression expression = EngineEventUtils.computeAttributeExpression(context, tag, attributeName,
138 "|" + parameterValue + "|");
139 return expression.execute(context, this.expressionExecutionContext).toString();
140 }
141
142 private boolean isCollectionOrArray(Object value) {
143 return value != null && (Collection.class.isAssignableFrom(value.getClass()) || value.getClass().isArray());
144 }
145
146 private String generateCollectionBindVariables(Object value, String parameterPath, String options) {
147 int size = value.getClass().isArray() ? Array.getLength(value) : ((Collection) value).size();
148 if (size == 0) {
149 return "null";
150 } else {
151 StringBuilder sb = new StringBuilder();
152 for (int i = 0; i < size; i++) {
153 if (i != 0) {
154 sb.append(", ");
155 }
156 sb.append(bindVariableRender.apply(parameterPath + "[" + i + "]" + options));
157 }
158 return sb.toString();
159 }
160 }
161
162 private static class Pair {
163
164 private final String left;
165 private final String right;
166
167 private Pair(String left, String right) {
168 this.left = left;
169 this.right = right;
170 }
171
172 private static Pair parse(String value, char separator) {
173 int separatorIndex = value.indexOf(separator);
174 String left;
175 String right;
176 if (separatorIndex == -1) {
177 left = value;
178 right = "";
179 } else {
180 left = value.substring(0, separatorIndex);
181 right = value.substring(separatorIndex);
182 }
183 return new Pair(left, right);
184 }
185
186 }
187
188 }