diff --git a/docs/document/content/user-manual/common-config/builtin-algorithm/expr.cn.md b/docs/document/content/user-manual/common-config/builtin-algorithm/expr.cn.md index 0130187706109..c2264fdf86077 100644 --- a/docs/document/content/user-manual/common-config/builtin-algorithm/expr.cn.md +++ b/docs/document/content/user-manual/common-config/builtin-algorithm/expr.cn.md @@ -1,6 +1,6 @@ +++ title = "行表达式" -weight = 8 +weight = 10 +++ ## 使用 Groovy 语法的行表达式 diff --git a/docs/document/content/user-manual/common-config/builtin-algorithm/expr.en.md b/docs/document/content/user-manual/common-config/builtin-algorithm/expr.en.md index 310d0ee3db38b..f77b8e9de2f7c 100644 --- a/docs/document/content/user-manual/common-config/builtin-algorithm/expr.en.md +++ b/docs/document/content/user-manual/common-config/builtin-algorithm/expr.en.md @@ -1,6 +1,6 @@ +++ title = "Row Value Expressions" -weight = 8 +weight = 10 +++ ## Row Value Expressions that uses the Groovy syntax diff --git a/infra/expr/type/espresso/src/main/java/org/apache/shardingsphere/infra/expr/espresso/EspressoInlineExpressionParser.java b/infra/expr/type/espresso/src/main/java/org/apache/shardingsphere/infra/expr/espresso/EspressoInlineExpressionParser.java index 2106e39ef5306..70aac2441dafc 100644 --- a/infra/expr/type/espresso/src/main/java/org/apache/shardingsphere/infra/expr/espresso/EspressoInlineExpressionParser.java +++ b/infra/expr/type/espresso/src/main/java/org/apache/shardingsphere/infra/expr/espresso/EspressoInlineExpressionParser.java @@ -21,6 +21,7 @@ import com.google.common.collect.Sets; import groovy.lang.GroovyShell; import org.apache.shardingsphere.infra.expr.spi.InlineExpressionParser; +import org.apache.shardingsphere.infra.util.groovy.GroovyUtils; import org.graalvm.polyglot.Context; import org.graalvm.polyglot.Value; @@ -42,8 +43,6 @@ public final class EspressoInlineExpressionParser implements InlineExpressionPar private static final String JAVA_CLASSPATH; - private static final char SPLITTER = ','; - private String inlineExpression; /** @@ -85,48 +84,7 @@ private String handlePlaceHolder(final String inlineExpression) { @Override public List splitAndEvaluate() { - return Strings.isNullOrEmpty(inlineExpression) ? Collections.emptyList() : flatten(evaluate(split(handlePlaceHolder(inlineExpression)), context)); - } - - private List split(final String inlineExpression) { - List result = new ArrayList<>(); - StringBuilder segment = new StringBuilder(); - int bracketsDepth = 0; - for (int i = 0; i < inlineExpression.length(); i++) { - char each = inlineExpression.charAt(i); - switch (each) { - case SPLITTER: - if (bracketsDepth > 0) { - segment.append(each); - } else { - result.add(segment.toString().trim()); - segment.setLength(0); - } - break; - case '$': - if ('{' == inlineExpression.charAt(i + 1)) { - bracketsDepth++; - } - if ("->{".equals(inlineExpression.substring(i + 1, i + 4))) { - bracketsDepth++; - } - segment.append(each); - break; - case '}': - if (bracketsDepth > 0) { - bracketsDepth--; - } - segment.append(each); - break; - default: - segment.append(each); - break; - } - } - if (segment.length() > 0) { - result.add(segment.toString().trim()); - } - return result; + return Strings.isNullOrEmpty(inlineExpression) ? Collections.emptyList() : flatten(evaluate(GroovyUtils.split(handlePlaceHolder(inlineExpression)), context)); } private List evaluate(final List inlineExpressions, final Context context) { diff --git a/infra/expr/type/groovy/src/main/java/org/apache/shardingsphere/infra/expr/groovy/GroovyInlineExpressionParser.java b/infra/expr/type/groovy/src/main/java/org/apache/shardingsphere/infra/expr/groovy/GroovyInlineExpressionParser.java index 92eb078a3c4c5..e3ef59d9d49b3 100644 --- a/infra/expr/type/groovy/src/main/java/org/apache/shardingsphere/infra/expr/groovy/GroovyInlineExpressionParser.java +++ b/infra/expr/type/groovy/src/main/java/org/apache/shardingsphere/infra/expr/groovy/GroovyInlineExpressionParser.java @@ -24,6 +24,7 @@ import groovy.lang.GroovyShell; import groovy.lang.Script; import org.apache.shardingsphere.infra.expr.spi.InlineExpressionParser; +import org.apache.shardingsphere.infra.util.groovy.GroovyUtils; import java.util.ArrayList; import java.util.Collection; @@ -41,8 +42,6 @@ */ public final class GroovyInlineExpressionParser implements InlineExpressionParser { - private static final char SPLITTER = ','; - private static final String INLINE_EXPRESSION_KEY = "inlineExpression"; private static final Map SCRIPTS = new ConcurrentHashMap<>(); @@ -78,7 +77,7 @@ private String handlePlaceHolder(final String inlineExpression) { */ @Override public List splitAndEvaluate() { - return Strings.isNullOrEmpty(inlineExpression) ? Collections.emptyList() : flatten(evaluate(split(handlePlaceHolder(inlineExpression)))); + return Strings.isNullOrEmpty(inlineExpression) ? Collections.emptyList() : flatten(evaluate(GroovyUtils.split(handlePlaceHolder(inlineExpression)))); } /** @@ -117,47 +116,6 @@ private Object evaluate(final String expression) { return script.run(); } - private List split(final String inlineExpression) { - List result = new ArrayList<>(); - StringBuilder segment = new StringBuilder(); - int bracketsDepth = 0; - for (int i = 0; i < inlineExpression.length(); i++) { - char each = inlineExpression.charAt(i); - switch (each) { - case SPLITTER: - if (bracketsDepth > 0) { - segment.append(each); - } else { - result.add(segment.toString().trim()); - segment.setLength(0); - } - break; - case '$': - if ('{' == inlineExpression.charAt(i + 1)) { - bracketsDepth++; - } - if ("->{".equals(inlineExpression.substring(i + 1, i + 4))) { - bracketsDepth++; - } - segment.append(each); - break; - case '}': - if (bracketsDepth > 0) { - bracketsDepth--; - } - segment.append(each); - break; - default: - segment.append(each); - break; - } - } - if (segment.length() > 0) { - result.add(segment.toString().trim()); - } - return result; - } - private List flatten(final List segments) { List result = new ArrayList<>(); for (Object each : segments) { diff --git a/infra/util/src/main/java/org/apache/shardingsphere/infra/util/groovy/GroovyUtils.java b/infra/util/src/main/java/org/apache/shardingsphere/infra/util/groovy/GroovyUtils.java new file mode 100644 index 0000000000000..6c20a424ee281 --- /dev/null +++ b/infra/util/src/main/java/org/apache/shardingsphere/infra/util/groovy/GroovyUtils.java @@ -0,0 +1,87 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.shardingsphere.infra.util.groovy; + +import java.util.ArrayList; +import java.util.List; + +/** + * GroovyShell expression common utils. It mainly serves the use of the following classes. + * - `org.apache.shardingsphere.infra.expr.espresso.EspressoInlineExpressionParser` + * - `org.apache.shardingsphere.infra.expr.groovy.GroovyInlineExpressionParser` + */ +public class GroovyUtils { + + /** + * Split GroovyShell expression to a ArrayList. + * + * @return result ArrayList of GroovyShell expression with {@code $}. + */ + public static List split(final String inlineExpression) { + List result = new ArrayList<>(); + StringBuilder segment = new StringBuilder(); + int bracketsDepth = 0; + for (int i = 0; i < inlineExpression.length(); i++) { + char each = inlineExpression.charAt(i); + switch (each) { + case ',': + handleSplitter(bracketsDepth, segment, each, result); + break; + case '$': + bracketsDepth = handleDollarSign(inlineExpression, i, bracketsDepth, segment, each); + break; + case '}': + bracketsDepth = handleClosingBracket(bracketsDepth, segment, each); + break; + default: + segment.append(each); + break; + } + } + if (segment.length() > 0) { + result.add(segment.toString().trim()); + } + return result; + } + + private static void handleSplitter(final int bracketsDepth, final StringBuilder segment, final char each, final List result) { + if (bracketsDepth > 0) { + segment.append(each); + } else { + result.add(segment.toString().trim()); + segment.setLength(0); + } + } + + private static int handleDollarSign(final String inlineExpression, final int i, final int bracketsDepth, final StringBuilder segment, final char each) { + int bracketsDepthResult = bracketsDepth; + if ('{' == inlineExpression.charAt(i + 1)) { + bracketsDepthResult = bracketsDepthResult + 1; + } + if ("->{".equals(inlineExpression.substring(i + 1, i + 4))) { + bracketsDepthResult = bracketsDepthResult + 1; + } + segment.append(each); + return bracketsDepthResult; + } + + private static int handleClosingBracket(final int bracketsDepth, final StringBuilder segment, final char each) { + segment.append(each); + return bracketsDepth > 0 ? bracketsDepth - 1 : bracketsDepth; + } +} diff --git a/infra/util/src/test/java/org/apache/shardingsphere/infra/util/groovy/GroovyUtilsTest.java b/infra/util/src/test/java/org/apache/shardingsphere/infra/util/groovy/GroovyUtilsTest.java new file mode 100644 index 0000000000000..9e5cfe1c55e71 --- /dev/null +++ b/infra/util/src/test/java/org/apache/shardingsphere/infra/util/groovy/GroovyUtilsTest.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.shardingsphere.infra.util.groovy; + +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.Collections; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +class GroovyUtilsTest { + + @Test + void assertSplit() { + assertThat(GroovyUtils.split(" t_order_0, t_order_1 "), is(Arrays.asList("t_order_0", "t_order_1"))); + assertThat(GroovyUtils.split("t_order_${null}"), is(Collections.singletonList("t_order_${null}"))); + assertThat(GroovyUtils.split("t_order_${'xx'}"), is(Collections.singletonList("t_order_${'xx'}"))); + assertThat(GroovyUtils.split("t_order_${[0, 1, 2]},t_order_item_${[0, 2]}"), + is(Arrays.asList("t_order_${[0, 1, 2]}", "t_order_item_${[0, 2]}"))); + assertThat(GroovyUtils.split("t_order_${0..2},t_order_item_${0..1}"), is(Arrays.asList("t_order_${0..2}", "t_order_item_${0..1}"))); + assertThat(GroovyUtils.split("t_${[\"new${1+2}\",'old']}_order_${1..2}"), + is(Collections.singletonList("t_${[\"new${1+2}\",'old']}_order_${1..2}"))); + assertThat(GroovyUtils.split("t_$->{[\"new$->{1+2}\",'old']}_order_$->{1..2}"), + is(Collections.singletonList("t_$->{[\"new$->{1+2}\",'old']}_order_$->{1..2}"))); + } +}