From 14c9e3bd72d65a4dd465fbe0d4d0f178dd40bfeb Mon Sep 17 00:00:00 2001 From: terrymanu Date: Sat, 9 Sep 2017 02:10:19 +0800 Subject: [PATCH] move inline parser to core --- .../config/common/internal/InlineParser.java | 22 +-- .../rdb/sharding/config/common/AllTests.java | 4 +- ...t.java => InlineExpressionParserTest.java} | 5 +- .../rdb/sharding/api/rule/TableRule.java | 25 +++ .../sharding/util/InlineExpressionParser.java | 170 ++++++++++++++++++ .../rdb/sharding/util/AllUtilTests.java | 3 +- .../util/InlineExpressionParserTest.java | 65 +++++++ .../rdb/sharding/example/jdbc/Main.java | 4 +- 8 files changed, 280 insertions(+), 18 deletions(-) rename sharding-jdbc-config-parent/sharding-jdbc-config-common/src/test/java/com/dangdang/ddframe/rdb/sharding/config/common/internal/{InlineParserTest.java => InlineExpressionParserTest.java} (91%) create mode 100644 sharding-jdbc-core/src/main/java/com/dangdang/ddframe/rdb/sharding/util/InlineExpressionParser.java create mode 100644 sharding-jdbc-core/src/test/java/com/dangdang/ddframe/rdb/sharding/util/InlineExpressionParserTest.java diff --git a/sharding-jdbc-config-parent/sharding-jdbc-config-common/src/main/java/com/dangdang/ddframe/rdb/sharding/config/common/internal/InlineParser.java b/sharding-jdbc-config-parent/sharding-jdbc-config-common/src/main/java/com/dangdang/ddframe/rdb/sharding/config/common/internal/InlineParser.java index 7be7c0ac62..7b0ba6b372 100644 --- a/sharding-jdbc-config-parent/sharding-jdbc-config-common/src/main/java/com/dangdang/ddframe/rdb/sharding/config/common/internal/InlineParser.java +++ b/sharding-jdbc-config-parent/sharding-jdbc-config-common/src/main/java/com/dangdang/ddframe/rdb/sharding/config/common/internal/InlineParser.java @@ -135,17 +135,6 @@ public final class InlineParser { return result; } - private String assemblySegment(final List cartesianValue, final GString segment) { - StringBuilder result = new StringBuilder(); - for (int i = 0; i < segment.getStrings().length; i++) { - result.append(segment.getStrings()[i]); - if (i < cartesianValue.size()) { - result.append(cartesianValue.get(i)); - } - } - return result.toString(); - } - @SuppressWarnings("unchecked") private Set> getCartesianValues(final GString segment) { List> result = new ArrayList<>(segment.getValues().length); @@ -167,4 +156,15 @@ public final class InlineParser { } return Sets.cartesianProduct(result); } + + private String assemblySegment(final List cartesianValue, final GString segment) { + StringBuilder result = new StringBuilder(); + for (int i = 0; i < segment.getStrings().length; i++) { + result.append(segment.getStrings()[i]); + if (i < cartesianValue.size()) { + result.append(cartesianValue.get(i)); + } + } + return result.toString(); + } } diff --git a/sharding-jdbc-config-parent/sharding-jdbc-config-common/src/test/java/com/dangdang/ddframe/rdb/sharding/config/common/AllTests.java b/sharding-jdbc-config-parent/sharding-jdbc-config-common/src/test/java/com/dangdang/ddframe/rdb/sharding/config/common/AllTests.java index 27088b723a..8eca5733bb 100644 --- a/sharding-jdbc-config-parent/sharding-jdbc-config-common/src/test/java/com/dangdang/ddframe/rdb/sharding/config/common/AllTests.java +++ b/sharding-jdbc-config-parent/sharding-jdbc-config-common/src/test/java/com/dangdang/ddframe/rdb/sharding/config/common/AllTests.java @@ -19,7 +19,7 @@ package com.dangdang.ddframe.rdb.sharding.config.common; import com.dangdang.ddframe.rdb.sharding.config.common.api.ShardingRuleBuilderTest; import com.dangdang.ddframe.rdb.sharding.config.common.internal.ClosureShardingAlgorithmTest; -import com.dangdang.ddframe.rdb.sharding.config.common.internal.InlineParserTest; +import com.dangdang.ddframe.rdb.sharding.config.common.internal.InlineExpressionParserTest; import org.junit.runner.RunWith; import org.junit.runners.Suite; @@ -27,7 +27,7 @@ import org.junit.runners.Suite; @Suite.SuiteClasses({ ShardingRuleBuilderTest.class, ClosureShardingAlgorithmTest.class, - InlineParserTest.class + InlineExpressionParserTest.class }) public class AllTests { } diff --git a/sharding-jdbc-config-parent/sharding-jdbc-config-common/src/test/java/com/dangdang/ddframe/rdb/sharding/config/common/internal/InlineParserTest.java b/sharding-jdbc-config-parent/sharding-jdbc-config-common/src/test/java/com/dangdang/ddframe/rdb/sharding/config/common/internal/InlineExpressionParserTest.java similarity index 91% rename from sharding-jdbc-config-parent/sharding-jdbc-config-common/src/test/java/com/dangdang/ddframe/rdb/sharding/config/common/internal/InlineParserTest.java rename to sharding-jdbc-config-parent/sharding-jdbc-config-common/src/test/java/com/dangdang/ddframe/rdb/sharding/config/common/internal/InlineExpressionParserTest.java index 9ebe1a3c9c..de1feef3cc 100644 --- a/sharding-jdbc-config-parent/sharding-jdbc-config-common/src/test/java/com/dangdang/ddframe/rdb/sharding/config/common/internal/InlineParserTest.java +++ b/sharding-jdbc-config-parent/sharding-jdbc-config-common/src/test/java/com/dangdang/ddframe/rdb/sharding/config/common/internal/InlineExpressionParserTest.java @@ -26,7 +26,7 @@ import static org.hamcrest.Matchers.hasItems; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertThat; -public final class InlineParserTest { +public final class InlineExpressionParserTest { @Test public void assertSplit() { @@ -37,7 +37,8 @@ public final class InlineParserTest { @Test public void assertEvaluate() { assertThat(new InlineParser(" 1 , 2 ").evaluate(), hasItems("1", "2")); - assertThat(new InlineParser(" 1 , t_order_${1..2} ").evaluate(), hasItems("1", "t_order_1", "t_order_2")); + assertThat(new InlineParser(" 1 , t_order_${[0, 1, 2]} ").evaluate(), hasItems("1", "t_order_0", "t_order_1", "t_order_2")); + assertThat(new InlineParser(" 1 , t_order_${0..2} ").evaluate(), hasItems("1", "t_order_0", "t_order_1", "t_order_2")); assertThat(new InlineParser(" 1 , t_order_${null} ").evaluate(), hasItems("1", "t_order_")); assertThat(new InlineParser(" 1 , t_order_${'xx'} ").evaluate(), hasItems("1", "t_order_xx")); assertThat(new InlineParser(" t_${['new','old']}_order_${1..2} ").evaluate(), hasItems("t_new_order_1", "t_new_order_2", "t_old_order_1", "t_old_order_2")); diff --git a/sharding-jdbc-core/src/main/java/com/dangdang/ddframe/rdb/sharding/api/rule/TableRule.java b/sharding-jdbc-core/src/main/java/com/dangdang/ddframe/rdb/sharding/api/rule/TableRule.java index 9f1c3afd23..b641f409f4 100644 --- a/sharding-jdbc-core/src/main/java/com/dangdang/ddframe/rdb/sharding/api/rule/TableRule.java +++ b/sharding-jdbc-core/src/main/java/com/dangdang/ddframe/rdb/sharding/api/rule/TableRule.java @@ -19,6 +19,7 @@ package com.dangdang.ddframe.rdb.sharding.api.rule; import com.dangdang.ddframe.rdb.sharding.keygen.KeyGenerator; import com.dangdang.ddframe.rdb.sharding.routing.strategy.ShardingStrategy; +import com.dangdang.ddframe.rdb.sharding.util.InlineExpressionParser; import com.google.common.base.Preconditions; import lombok.Getter; import lombok.RequiredArgsConstructor; @@ -235,6 +236,18 @@ public final class TableRule { return this; } + /** + * Build actual tables. + * + * @param inlineExpression actual tables inline expression + * @return this builder + */ + public TableRuleBuilder actualTablesInlineExpression(final String inlineExpression) { + this.actualTables.clear(); + this.actualTables.addAll(new InlineExpressionParser(inlineExpression).evaluate()); + return this; + } + /** * Build data source rule. * @@ -258,6 +271,18 @@ public final class TableRule { return this; } + /** + * Build data sources's names. + * + * @param inlineExpression data sources's names inline expression + * @return this builder + */ + public TableRuleBuilder dataSourceNames(final String inlineExpression) { + this.dataSourceNames.clear(); + this.dataSourceNames.addAll(new InlineExpressionParser(inlineExpression).evaluate()); + return this; + } + /** * Build database sharding strategy. * diff --git a/sharding-jdbc-core/src/main/java/com/dangdang/ddframe/rdb/sharding/util/InlineExpressionParser.java b/sharding-jdbc-core/src/main/java/com/dangdang/ddframe/rdb/sharding/util/InlineExpressionParser.java new file mode 100644 index 0000000000..a42e1c481c --- /dev/null +++ b/sharding-jdbc-core/src/main/java/com/dangdang/ddframe/rdb/sharding/util/InlineExpressionParser.java @@ -0,0 +1,170 @@ +/* + * Copyright 1999-2015 dangdang.com. + *

+ * Licensed 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 com.dangdang.ddframe.rdb.sharding.util; + +import com.google.common.base.Function; +import com.google.common.base.Splitter; +import com.google.common.collect.Collections2; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import groovy.lang.GString; +import groovy.lang.GroovyShell; +import lombok.RequiredArgsConstructor; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Set; + +/** + * Inline expression parser. + * + * @author gaohongtao + * @author zhangliang + */ +@RequiredArgsConstructor +public final class InlineExpressionParser { + + private static final char SPLITTER = ','; + + private final String inlineExpression; + + /** + * Split inline expression. + * + * @return split inline expression segments + */ + public List split() { + return Splitter.on(SPLITTER).trimResults().splitToList(inlineExpression); + } + + /** + * Split and evaluate inline expression. + * + * @return result list + */ + public List evaluate() { + final GroovyShell shell = new GroovyShell(); + return flattenSegments(Lists.transform(splitWithInlineExpression(), new Function() { + + @Override + public Object apply(final String input) { + StringBuilder expression = new StringBuilder(input); + if (!input.startsWith("\"")) { + expression.insert(0, "\""); + } + if (!input.endsWith("\"")) { + expression.append("\""); + } + return shell.evaluate(expression.toString()); + } + })); + } + + List splitWithInlineExpression() { + 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++; + } + 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 flattenSegments(final List segments) { + List result = new ArrayList<>(); + for (Object each : segments) { + if (each instanceof GString) { + result.addAll(assemblyCartesianSegments((GString) each)); + } else { + result.add(each.toString()); + } + } + return result; + } + + private List assemblyCartesianSegments(final GString segment) { + Set> cartesianValues = getCartesianValues(segment); + List result = new ArrayList<>(cartesianValues.size()); + for (List each : cartesianValues) { + result.add(assemblySegment(each, segment)); + } + return result; + } + + @SuppressWarnings("unchecked") + private Set> getCartesianValues(final GString segment) { + List> result = new ArrayList<>(segment.getValues().length); + for (Object each : segment.getValues()) { + if (null == each) { + continue; + } + if (each instanceof Collection) { + result.add(Sets.newHashSet(Collections2.transform((Collection) each, new Function() { + + @Override + public String apply(final Object input) { + return input.toString(); + } + }))); + } else { + result.add(Sets.newHashSet(each.toString())); + } + } + return Sets.cartesianProduct(result); + } + + private String assemblySegment(final List cartesianValue, final GString segment) { + StringBuilder result = new StringBuilder(); + for (int i = 0; i < segment.getStrings().length; i++) { + result.append(segment.getStrings()[i]); + if (i < cartesianValue.size()) { + result.append(cartesianValue.get(i)); + } + } + return result.toString(); + } +} diff --git a/sharding-jdbc-core/src/test/java/com/dangdang/ddframe/rdb/sharding/util/AllUtilTests.java b/sharding-jdbc-core/src/test/java/com/dangdang/ddframe/rdb/sharding/util/AllUtilTests.java index df16e5c377..1d74617599 100644 --- a/sharding-jdbc-core/src/test/java/com/dangdang/ddframe/rdb/sharding/util/AllUtilTests.java +++ b/sharding-jdbc-core/src/test/java/com/dangdang/ddframe/rdb/sharding/util/AllUtilTests.java @@ -23,7 +23,8 @@ import org.junit.runners.Suite; @RunWith(Suite.class) @Suite.SuiteClasses({ NumberUtilTest.class, - StringUtilTest.class, + StringUtilTest.class, + InlineExpressionParserTest.class, SQLUtilTest.class, EventBusInstanceTest.class }) diff --git a/sharding-jdbc-core/src/test/java/com/dangdang/ddframe/rdb/sharding/util/InlineExpressionParserTest.java b/sharding-jdbc-core/src/test/java/com/dangdang/ddframe/rdb/sharding/util/InlineExpressionParserTest.java new file mode 100644 index 0000000000..294fcac6b0 --- /dev/null +++ b/sharding-jdbc-core/src/test/java/com/dangdang/ddframe/rdb/sharding/util/InlineExpressionParserTest.java @@ -0,0 +1,65 @@ +package com.dangdang.ddframe.rdb.sharding.util; + +import org.junit.Test; + +import java.util.Arrays; +import java.util.Collections; + +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsCollectionContaining.hasItems; +import static org.junit.Assert.assertThat; + +public final class InlineExpressionParserTest { + + @Test + public void assertSplit() { + assertThat(new InlineExpressionParser(" 1 ").split(), is(Collections.singletonList("1"))); + assertThat(new InlineExpressionParser(" 1 , 2 ").split(), is(Arrays.asList("1", "2"))); + } + + @Test + public void assertEvaluate() { + assertThat(new InlineExpressionParser(" 1 , 2 ").evaluate(), hasItems("1", "2")); + assertThat(new InlineExpressionParser(" 1 , t_order_${[0, 1, 2]} ").evaluate(), hasItems("1", "t_order_0", "t_order_1", "t_order_2")); + assertThat(new InlineExpressionParser(" 1 , t_order_${0..2} ").evaluate(), hasItems("1", "t_order_0", "t_order_1", "t_order_2")); + assertThat(new InlineExpressionParser(" 1 , t_order_${null} ").evaluate(), hasItems("1", "t_order_")); + assertThat(new InlineExpressionParser(" 1 , t_order_${'xx'} ").evaluate(), hasItems("1", "t_order_xx")); + assertThat(new InlineExpressionParser(" t_${['new','old']}_order_${1..2} ").evaluate(), hasItems("t_new_order_1", "t_new_order_2", "t_old_order_1", "t_old_order_2")); + assertThat(new InlineExpressionParser(" t_${[\"new${1+2}\",'old']}_order_${1..2} ").evaluate(), hasItems("t_new3_order_1", "t_new3_order_2", "t_old_order_1", "t_old_order_2")); + } + + @Test + public void assertSplitWithInlineExpressionForOneRegularValue() { + assertThat(new InlineExpressionParser("order").splitWithInlineExpression(), is(Collections.singletonList("order"))); + } + + @Test + public void assertSplitWithInlineExpressionForOneInlineValue() { + assertThat(new InlineExpressionParser("order_${0..7}").splitWithInlineExpression(), is(Collections.singletonList("order_${0..7}"))); + } + + @Test + public void assertSplitWithInlineExpressionForRegularValuesWithSpace() { + assertThat(new InlineExpressionParser(" order , order_item ").splitWithInlineExpression(), is(Arrays.asList("order", "order_item"))); + } + + @Test + public void assertSplitWithInlineExpressionForMixedValuesWithSpace() { + assertThat(new InlineExpressionParser(" order , order_item_${0..7} ").splitWithInlineExpression(), is(Arrays.asList("order", "order_item_${0..7}"))); + } + + @Test + public void assertSplitWithInlineExpressionForMixedValuesAndWithCommaAtInlineExpression() { + assertThat(new InlineExpressionParser(" order , order_item_${0, 2, 4} ").splitWithInlineExpression(), is(Arrays.asList("order", "order_item_${0, 2, 4}"))); + } + + @Test + public void assertSplitWithInlineExpressionForMixedValuesAndNestedInlineExpression() { + assertThat(new InlineExpressionParser(" order , order_item_${0, ${1..7}} ").splitWithInlineExpression(), is(Arrays.asList("order", "order_item_${0, ${1..7}}"))); + } + + @Test + public void assertSplitWithInlineExpressionForValuesNotInlineExpression() { + assertThat(new InlineExpressionParser(" order , order_item_$ {0, 1} ").splitWithInlineExpression(), is(Arrays.asList("order", "order_item_$ {0", "1}"))); + } +} diff --git a/sharding-jdbc-example/sharding-jdbc-example-jdbc/src/main/java/com/dangdang/ddframe/rdb/sharding/example/jdbc/Main.java b/sharding-jdbc-example/sharding-jdbc-example-jdbc/src/main/java/com/dangdang/ddframe/rdb/sharding/example/jdbc/Main.java index 839acc2559..dba5c8f6f1 100644 --- a/sharding-jdbc-example/sharding-jdbc-example-jdbc/src/main/java/com/dangdang/ddframe/rdb/sharding/example/jdbc/Main.java +++ b/sharding-jdbc-example/sharding-jdbc-example-jdbc/src/main/java/com/dangdang/ddframe/rdb/sharding/example/jdbc/Main.java @@ -118,8 +118,8 @@ public final class Main { private static ShardingDataSource getShardingDataSource() throws SQLException { DataSourceRule dataSourceRule = new DataSourceRule(createDataSourceMap()); - TableRule orderTableRule = TableRule.builder("t_order").actualTables("t_order_0", "t_order_1").dataSourceRule(dataSourceRule).build(); - TableRule orderItemTableRule = TableRule.builder("t_order_item").actualTables("t_order_item_0", "t_order_item_1").dataSourceRule(dataSourceRule).build(); + TableRule orderTableRule = TableRule.builder("t_order").actualTablesInlineExpression("t_order_${[0, 1]}").dataSourceRule(dataSourceRule).build(); + TableRule orderItemTableRule = TableRule.builder("t_order_item").actualTablesInlineExpression("t_order_item_${[0, 1]}").dataSourceRule(dataSourceRule).build(); ShardingRule shardingRule = ShardingRule.builder(dataSourceRule).tableRules(orderTableRule, orderItemTableRule) .bindingTableRules(new BindingTableRule(orderTableRule, orderItemTableRule)) .defaultDatabaseShardingStrategy(new InlineShardingStrategy("user_id", "ds_jdbc_${user_id % 2}")) -- GitLab