1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.shardingsphere.infra.expr.groovy;
19
20 import com.google.common.base.Strings;
21 import com.google.common.collect.Sets;
22 import groovy.lang.Closure;
23 import groovy.lang.GString;
24 import groovy.lang.GroovyShell;
25 import groovy.lang.Script;
26 import groovy.util.Expando;
27 import org.apache.shardingsphere.infra.expr.spi.InlineExpressionParser;
28 import org.apache.shardingsphere.infra.util.groovy.GroovyUtils;
29
30 import java.util.ArrayList;
31 import java.util.Collection;
32 import java.util.Collections;
33 import java.util.LinkedHashSet;
34 import java.util.List;
35 import java.util.Map;
36 import java.util.Properties;
37 import java.util.Set;
38 import java.util.concurrent.ConcurrentHashMap;
39 import java.util.stream.Collectors;
40
41
42
43
44 public final class GroovyInlineExpressionParser implements InlineExpressionParser {
45
46 private static final String INLINE_EXPRESSION_KEY = "inlineExpression";
47
48 private static final Map<String, Script> SCRIPTS = new ConcurrentHashMap<>();
49
50 private static final GroovyShell SHELL = new GroovyShell();
51
52 private String inlineExpression;
53
54 @Override
55 public void init(final Properties props) {
56 inlineExpression = props.getProperty(INLINE_EXPRESSION_KEY);
57 }
58
59 @Override
60 public String handlePlaceHolder() {
61 return handlePlaceHolder(inlineExpression);
62 }
63
64
65
66
67
68
69
70 private String handlePlaceHolder(final String inlineExpression) {
71 return inlineExpression.contains("$->{") ? inlineExpression.replaceAll("\\$->\\{", "\\$\\{") : inlineExpression;
72 }
73
74
75
76
77
78
79 @Override
80 public List<String> splitAndEvaluate() {
81 return Strings.isNullOrEmpty(inlineExpression) ? Collections.emptyList() : flatten(evaluate(GroovyUtils.split(handlePlaceHolder(inlineExpression))));
82 }
83
84
85
86
87
88
89 @Override
90 public String evaluateWithArgs(final Map<String, Comparable<?>> map) {
91 Closure<?> result = ((Closure<?>) evaluate("{it -> \"" + handlePlaceHolder(inlineExpression) + "\"}")).rehydrate(new Expando(), null, null);
92 result.setResolveStrategy(Closure.DELEGATE_ONLY);
93 map.forEach(result::setProperty);
94 return result.call().toString();
95 }
96
97 private List<Object> evaluate(final List<String> inlineExpressions) {
98 List<Object> result = new ArrayList<>(inlineExpressions.size());
99 for (String each : inlineExpressions) {
100 StringBuilder expression = new StringBuilder(handlePlaceHolder(each));
101 if (!each.startsWith("\"")) {
102 expression.insert(0, '"');
103 }
104 if (!each.endsWith("\"")) {
105 expression.append('"');
106 }
107 result.add(evaluate(expression.toString()));
108 }
109 return result;
110 }
111
112 private Object evaluate(final String expression) {
113 Script script;
114 if (SCRIPTS.containsKey(expression)) {
115 script = SCRIPTS.get(expression);
116 } else {
117 script = SHELL.parse(expression);
118 SCRIPTS.put(expression, script);
119 }
120 return script.run();
121 }
122
123 private List<String> flatten(final List<Object> segments) {
124 List<String> result = new ArrayList<>();
125 for (Object each : segments) {
126 if (each instanceof GString) {
127 result.addAll(assemblyCartesianSegments((GString) each));
128 } else {
129 result.add(each.toString());
130 }
131 }
132 return result;
133 }
134
135 private List<String> assemblyCartesianSegments(final GString segment) {
136 Set<List<String>> cartesianValues = getCartesianValues(segment);
137 List<String> result = new ArrayList<>(cartesianValues.size());
138 for (List<String> each : cartesianValues) {
139 result.add(assemblySegment(each, segment));
140 }
141 return result;
142 }
143
144 @SuppressWarnings("unchecked")
145 private Set<List<String>> getCartesianValues(final GString segment) {
146 List<Set<String>> result = new ArrayList<>(segment.getValues().length);
147 for (Object each : segment.getValues()) {
148 if (null == each) {
149 continue;
150 }
151 if (each instanceof Collection) {
152 result.add(((Collection<Object>) each).stream().map(Object::toString).collect(Collectors.toCollection(LinkedHashSet::new)));
153 } else {
154 result.add(Sets.newHashSet(each.toString()));
155 }
156 }
157 return Sets.cartesianProduct(result);
158 }
159
160 private String assemblySegment(final List<String> cartesianValue, final GString segment) {
161 StringBuilder result = new StringBuilder();
162 for (int i = 0; i < segment.getStrings().length; i++) {
163 result.append(segment.getStrings()[i]);
164 if (i < cartesianValue.size()) {
165 result.append(cartesianValue.get(i));
166 }
167 }
168 return result.toString();
169 }
170
171 @Override
172 public String getType() {
173 return "GROOVY";
174 }
175 }