-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
#20 implement improved DefaultValueStrategyBuilder
- Loading branch information
Showing
6 changed files
with
333 additions
and
96 deletions.
There are no files selected for viewing
148 changes: 148 additions & 0 deletions
148
...runtime/src/main/java/com/btc/redg/runtime/defaultvalues/DefaultValueStrategyBuilder.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
package com.btc.redg.runtime.defaultvalues; | ||
|
||
import java.util.function.BiFunction; | ||
import java.util.function.Predicate; | ||
import java.util.regex.Pattern; | ||
|
||
import com.btc.redg.models.ColumnModel; | ||
import com.btc.redg.runtime.defaultvalues.pluggable.DefaultDefaultValueProvider; | ||
import com.btc.redg.runtime.defaultvalues.pluggable.PluggableDefaultValueProvider; | ||
import com.btc.redg.runtime.defaultvalues.pluggable.PluggableDefaultValueStrategy; | ||
|
||
public class DefaultValueStrategyBuilder { | ||
|
||
private PluggableDefaultValueStrategy pluggableDefaultValueStrategy = new PluggableDefaultValueStrategy(); | ||
private DefaultValueStrategy fallbackStrategy = new DefaultDefaultValueProvider(); | ||
|
||
public Condition when(final Predicate<ColumnModel> predicate) { | ||
return new PredicateCondition(predicate); | ||
} | ||
|
||
public Condition whenTableNameMatches(final String regex) { | ||
return new TableNameRegexCondition(regex); | ||
} | ||
|
||
public Condition whenColumnNameMatches(final String regex) { | ||
return new ColumnNameRegexCondition(regex); | ||
} | ||
|
||
public void setFallbackStrategy(DefaultValueStrategy fallbackStrategy) { | ||
this.fallbackStrategy = fallbackStrategy; | ||
} | ||
|
||
private void addProvider(Predicate<ColumnModel> conditionPredicate, BiFunction<ColumnModel, Class, Object> valueProducer) { | ||
pluggableDefaultValueStrategy.addProvider(new PluggableDefaultValueProvider() { | ||
@Override | ||
public boolean willProvide(ColumnModel columnModel) { | ||
return conditionPredicate.test(columnModel); | ||
} | ||
|
||
@Override | ||
public <T> T getDefaultValue(ColumnModel columnModel, Class<T> type) { | ||
return (T) valueProducer.apply(columnModel, type); | ||
} | ||
}); | ||
} | ||
|
||
public DefaultValueStrategy build() { | ||
pluggableDefaultValueStrategy.addProvider(new PluggableDefaultValueProvider() { | ||
@Override | ||
public boolean willProvide(ColumnModel columnModel) { | ||
return true; | ||
} | ||
|
||
@Override | ||
public <T> T getDefaultValue(ColumnModel columnModel, Class<T> type) { | ||
return fallbackStrategy.getDefaultValue(columnModel, type); | ||
} | ||
}); | ||
return pluggableDefaultValueStrategy; | ||
} | ||
|
||
public abstract class Condition { | ||
|
||
public abstract boolean evaluate(ColumnModel columnModel); | ||
|
||
public void thenUse(Object staticValue) { | ||
addProvider(this::evaluate, (columnModel, aClass) -> staticValue); | ||
} | ||
|
||
public void thenCompute(BiFunction<ColumnModel, Class, Object> computationFunction) { | ||
addProvider(this::evaluate, computationFunction); | ||
} | ||
|
||
public void thenUseProvider(PluggableDefaultValueProvider provider) { | ||
addProvider(columnModel -> evaluate(columnModel) && provider.willProvide(columnModel), provider::getDefaultValue); | ||
} | ||
|
||
public Condition and(final Predicate<ColumnModel> predicate) { | ||
return new AndCondition(this, new PredicateCondition(predicate)); | ||
} | ||
|
||
public Condition andTableNameMatches(final String regex) { | ||
return new AndCondition(this, new TableNameRegexCondition(regex)); | ||
} | ||
|
||
public Condition andColumnNameMatches(final String regex) { | ||
return new AndCondition(this, new ColumnNameRegexCondition(regex)); | ||
} | ||
} | ||
|
||
class AndCondition extends Condition { | ||
|
||
private final Condition condition1; | ||
private final Condition condition2; | ||
|
||
public AndCondition(Condition condition1, Condition condition2) { | ||
this.condition1 = condition1; | ||
this.condition2 = condition2; | ||
} | ||
|
||
@Override | ||
public boolean evaluate(ColumnModel columnModel) { | ||
return condition1.evaluate(columnModel) && condition2.evaluate(columnModel); | ||
} | ||
} | ||
|
||
class PredicateCondition extends Condition { | ||
|
||
private final Predicate<ColumnModel> predicate; | ||
|
||
PredicateCondition(Predicate<ColumnModel> predicate) { | ||
this.predicate = predicate; | ||
} | ||
|
||
@Override | ||
public boolean evaluate(ColumnModel columnModel) { | ||
return predicate.test(columnModel); | ||
} | ||
} | ||
|
||
class TableNameRegexCondition extends Condition { | ||
|
||
private final Pattern pattern; | ||
|
||
TableNameRegexCondition(String regex) { | ||
pattern = Pattern.compile(regex); | ||
} | ||
|
||
@Override | ||
public boolean evaluate(ColumnModel columnModel) { | ||
return pattern.matcher(columnModel.getDbTableName()).matches(); | ||
} | ||
} | ||
|
||
class ColumnNameRegexCondition extends Condition { | ||
|
||
private final Pattern pattern; | ||
|
||
ColumnNameRegexCondition(String regex) { | ||
pattern = Pattern.compile(regex); | ||
} | ||
|
||
@Override | ||
public boolean evaluate(ColumnModel columnModel) { | ||
return pattern.matcher(columnModel.getDbName()).matches(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
132 changes: 132 additions & 0 deletions
132
...ime/src/test/java/com/btc/redg/runtime/defaultvalues/DefaultValueStrategyBuilderTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
package com.btc.redg.runtime.defaultvalues; | ||
|
||
import org.junit.Assert; | ||
import org.junit.Test; | ||
import org.mockito.Mockito; | ||
|
||
import com.btc.redg.models.ColumnModel; | ||
import com.btc.redg.runtime.defaultvalues.pluggable.IncrementingNumberProvider; | ||
|
||
import static org.junit.Assert.*; | ||
|
||
public class DefaultValueStrategyBuilderTest { | ||
@Test | ||
public void testWhen() throws Exception { | ||
DefaultValueStrategyBuilder builder = new DefaultValueStrategyBuilder(); | ||
|
||
builder.when(columnModel -> false).thenUse(111); | ||
builder.when(columnModel -> true).thenUse(222); | ||
builder.when(columnModel -> false).thenUse(333); | ||
|
||
DefaultValueStrategy strategy = builder.build(); | ||
|
||
Assert.assertEquals(222, strategy.getDefaultValue(Mockito.mock(ColumnModel.class), int.class).intValue()); | ||
} | ||
|
||
@Test | ||
public void testWhenColumnNameMatches() throws Exception { | ||
DefaultValueStrategyBuilder builder = new DefaultValueStrategyBuilder(); | ||
|
||
builder.whenColumnNameMatches(".*X").thenUse(999); | ||
|
||
DefaultValueStrategy strategy = builder.build(); | ||
|
||
ColumnModel columnMock = Mockito.mock(ColumnModel.class); | ||
Mockito.when(columnMock.getDbName()).thenReturn("ASDFX"); | ||
Assert.assertEquals(999, strategy.getDefaultValue(columnMock, int.class).intValue()); | ||
Mockito.when(columnMock.getDbName()).thenReturn("ASDFA"); | ||
Assert.assertNull("should return null since the default default value is null", strategy.getDefaultValue(columnMock, int.class)); | ||
} | ||
|
||
@Test | ||
public void testWhenTableNameMatches() throws Exception { | ||
DefaultValueStrategyBuilder builder = new DefaultValueStrategyBuilder(); | ||
|
||
builder.whenTableNameMatches(".*X").thenUse(999); | ||
|
||
DefaultValueStrategy strategy = builder.build(); | ||
|
||
ColumnModel columnMock = Mockito.mock(ColumnModel.class); | ||
Mockito.when(columnMock.getDbTableName()).thenReturn("ASDFX"); | ||
Assert.assertEquals(999, strategy.getDefaultValue(columnMock, int.class).intValue()); | ||
Mockito.when(columnMock.getDbTableName()).thenReturn("ASDFA"); | ||
Assert.assertNull(strategy.getDefaultValue(columnMock, int.class)); | ||
} | ||
|
||
@Test | ||
public void testThenCompute() throws Exception { | ||
DefaultValueStrategyBuilder builder = new DefaultValueStrategyBuilder(); | ||
|
||
builder.when(columnModel -> true).thenCompute((columnModel, aClass) -> 123); | ||
|
||
DefaultValueStrategy strategy = builder.build(); | ||
|
||
Assert.assertEquals(123, strategy.getDefaultValue(Mockito.mock(ColumnModel.class), int.class).intValue()); | ||
} | ||
|
||
@Test | ||
public void testThenUseProvider() throws Exception { | ||
DefaultValueStrategyBuilder builder = new DefaultValueStrategyBuilder(); | ||
|
||
builder.when(columnModel -> true).thenUseProvider(new IncrementingNumberProvider()); | ||
|
||
DefaultValueStrategy strategy = builder.build(); | ||
|
||
ColumnModel columnModelMock = Mockito.mock(ColumnModel.class); | ||
Mockito.when(columnModelMock.getJavaTypeAsClass()).thenAnswer(invocationOnMock -> Integer.class); | ||
Assert.assertEquals(1, strategy.getDefaultValue(columnModelMock, int.class).intValue()); | ||
Assert.assertEquals(2, strategy.getDefaultValue(columnModelMock, int.class).intValue()); | ||
Assert.assertEquals(3, strategy.getDefaultValue(columnModelMock, int.class).intValue()); | ||
} | ||
|
||
@Test | ||
public void testSetFallbackStrategy() throws Exception { | ||
DefaultValueStrategyBuilder builder = new DefaultValueStrategyBuilder(); | ||
|
||
builder.when(columnModel -> false).thenUse("asdf"); | ||
builder.setFallbackStrategy(new DefaultValueStrategy() { | ||
@Override | ||
public <T> T getDefaultValue(ColumnModel columnModel, Class<T> type) { | ||
return (T) "fallback value"; | ||
} | ||
}); | ||
|
||
DefaultValueStrategy strategy = builder.build(); | ||
|
||
Assert.assertEquals("fallback value", strategy.getDefaultValue(Mockito.mock(ColumnModel.class), String.class)); | ||
} | ||
|
||
@Test | ||
public void testAndConditions() throws Exception { | ||
DefaultValueStrategyBuilder builder = new DefaultValueStrategyBuilder(); | ||
|
||
builder.whenColumnNameMatches("a.*") | ||
.andColumnNameMatches(".*z") | ||
.andTableNameMatches("t.*") | ||
.and(ColumnModel::isPrimitiveType) | ||
.thenUse("matches!"); | ||
|
||
DefaultValueStrategy strategy = builder.build(); | ||
|
||
|
||
ColumnModel columnModel = prepareMock("able", "a__z", true); | ||
Assert.assertEquals(null, strategy.getDefaultValue(columnModel, String.class)); | ||
columnModel = prepareMock("table", "__z", true); | ||
Assert.assertEquals(null, strategy.getDefaultValue(columnModel, String.class)); | ||
columnModel = prepareMock("table", "a__", true); | ||
Assert.assertEquals(null, strategy.getDefaultValue(columnModel, String.class)); | ||
columnModel = prepareMock("table", "a__z", false); | ||
Assert.assertEquals(null, strategy.getDefaultValue(columnModel, String.class)); | ||
columnModel = prepareMock("table", "a__z", true); | ||
Assert.assertEquals("matches!", strategy.getDefaultValue(columnModel, String.class)); | ||
} | ||
|
||
public ColumnModel prepareMock(String tableName, String columnName, boolean isPrimitiveType) { | ||
ColumnModel columnModel = Mockito.mock(ColumnModel.class); | ||
Mockito.when(columnModel.getDbTableName()).thenReturn(tableName); | ||
Mockito.when(columnModel.getDbName()).thenReturn(columnName); | ||
Mockito.when(columnModel.isPrimitiveType()).thenReturn(isPrimitiveType); | ||
return columnModel; | ||
} | ||
|
||
} |
Oops, something went wrong.