Skip to content

Commit

Permalink
#20 implement improved DefaultValueStrategyBuilder
Browse files Browse the repository at this point in the history
  • Loading branch information
yamass committed Nov 20, 2017
1 parent 6b4b573 commit 42333bf
Show file tree
Hide file tree
Showing 6 changed files with 333 additions and 96 deletions.
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();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,21 @@ public boolean willProvide(final ColumnModel columnModel) {
return Number.class.isAssignableFrom(columnModel.getJavaTypeAsClass());
}

public static <T> T convertNumber(BigDecimal number, final Class<T> type) {
public static <T> T convertNumber(BigDecimal number, Class<T> type) {
if (type == double.class) {
type = (Class<T>) Double.class;
} else if (type == float.class) {
type = (Class<T>) Float.class;
} else if (type == long.class) {
type = (Class<T>) Long.class;
} else if (type == int.class) {
type = (Class<T>) Integer.class;
} else if (type == byte.class) {
type = (Class<T>) Byte.class;
} else if (type == short.class) {
type = (Class<T>) Short.class;
}

if (BigDecimal.class.equals(type)) {
return type.cast(number);
} else if (Double.class.equals(type)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,49 +62,4 @@ public void addProvider(final PluggableDefaultValueProvider provider) {
this.providers.add(provider);
}

public void removeProvider(final PluggableDefaultValueProvider provider) {
this.providers.remove(provider);
}

public void clearProviders() {
this.providers.clear();
}

public static class Builder {

private PluggableDefaultValueStrategy strategy = new PluggableDefaultValueStrategy();

PluggableDefaultValueProvider lastProvider;

public Builder use(final PluggableDefaultValueProvider provider) {
if (lastProvider != null) {
strategy.addProvider(lastProvider);
}
lastProvider = provider;
return this;
}

public Builder when(final Predicate<ColumnModel> condition) {
this.lastProvider = new CustomConditionalProvider(condition, this.lastProvider);
return this;
}

public Builder useDefault() {
if (lastProvider != null) {
strategy.addProvider(lastProvider);
}
lastProvider = new DefaultDefaultValueProvider();
return this;
}

public PluggableDefaultValueStrategy build() {
if (lastProvider != null) {
strategy.addProvider(lastProvider);
lastProvider = null;
}
return strategy;
}
}


}
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;
}

}
Loading

0 comments on commit 42333bf

Please sign in to comment.