The implementation reads sequentially the user's input changing the type to build according to user choices.
There are two main components a conditional structural sharing (Conditional
) that runs code with preconditions...
import java.util.function.Predicate;
public class Conditional<T> {
private static final Conditional<Object> EMPTY = new Conditional<>() { protected void on(Object t) {} };
@SuppressWarnings("unchecked")
public static final <U> Conditional<U> empty() {
Conditional<U> conditional = new Conditional<U>((Conditional<U>) EMPTY);
conditional.then = new Conditional<U>((Conditional<U>) EMPTY);
return conditional;
}
private Runnable toRun;
private Predicate<T> filter;
private Conditional<T> alternative, then;
{ this.filter = (tested) -> { return false; }; }
private Conditional() {}
private Conditional(Conditional<T> alternative) { this.alternative = alternative; }
public Runner<T> when(Predicate<T> filter) {
this.filter = filter;
return new Runner<T>(this);
}
public static class Runner<U> {
private Conditional<U> conditional;
private Runner(Conditional<U> conditional) { this.conditional = conditional; }
public Or<U> run(Runnable toRun) {
this.conditional.toRun = toRun;
return new Or<U>(this.conditional);
}
}
protected void on(T t) {
if ( this.filter.test(t) ) {
this.toRun.run();
this.then.on(t);
} else { this.alternative.on(t); }
}
public static final class Or<R> {
private Conditional<R> conditional;
private Or(Conditional<R> conditional) { this.conditional = conditional; }
public Conditional<R> or() {
this.conditional.alternative = Conditional.empty();
return this.conditional.alternative;
}
public Conditional<R> then() {
this.conditional.then = Conditional.empty();
return this.conditional.then;
}
public void on(R t) { this.conditional.on(t); }
}
}
…and a property source (PropertySource
) with four components (1) an event driven base type builder (AType.Builder
) (2) an abstract factory with registrable types (TypeFactories
) (3) type factories (AType.Factory
,CType.Factory
) (4) a one time call enabler (Clapper
) to dynamically choose type factory according with user input by notifying the type event listeners…
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import java.util.function.Consumer;
import java.util.function.Function;
public class PropertySource {
private final Scanner scanner = new Scanner(System.in);
private String property;
private Function<String, String> reader = (text) -> {
this.clapper.clap();
System.out.println(text);
this.property = scanner.nextLine();
return property;
};
private Function<String, String> returner = (text) -> { return this.property; };
private Clapper<Function<String, String>> clapper = Clapper.of(reader, returner);
public String read(String text) { return this.clapper.get().apply(text); }
public String read() { return this.clapper.get().apply(""); }
public void reset() { this.clapper.clap(); }
private static final class Clapper<T> {
public static final <T> Clapper<T> of(T tin, T tinn) {
return new Clapper<T>(tin, tinn);
}
@SafeVarargs
private static final <U> U[] array(U... tintins) { return tintins; }
private T[] tintins;
private Clapper(T tin, T tinn) { this.tintins = array(tin, tinn); }
public T get() { return this.tintins[0]; }
public T clap() { return clap(this.tintins[0], this.tintins[1]); }
private T clap(T tin, T tinn) {
this.tintins = array(tinn, tin);
return tin;
}
}
public static class AType {
private static class Factory implements TypeFactory {
@Override
@SuppressWarnings("unchecked")
public AType get() { return new AType(); }
}
public static final AType.Builder builder() { return new AType.Builder(); }
public static class Builder {
private static class Event {
public static Event of(Runnable notifier) {
return new Event(notifier);
}
private Runnable notifier;
public Event(Runnable notifier) { this.notifier = notifier; }
public void on() { this.notifier.run(); }
}
private Map<Class, Event> typeEvents = new HashMap<>();
private Clapper<Consumer<Class>> typer = Clapper.of(
(typeClass) -> {
this.typer.clap();
this.typeFactory = TypeFactories.get(typeClass, this);
}
, (typeClass) -> {});
private TypeFactory typeFactory;
public String aProperty, bProperty, cProperty, cSpecificProperty;
private Builder() { this.typeFactory = TypeFactories.get(AType.class, this); }
{
this.registerTypeEvent(CType.class
, Event.of(() -> { this.typer.get().accept(CType.class); }));
}
private boolean registerTypeEvent(Class typeClass, Event event) {
return this.typeEvents.put(typeClass, event) != null;
}
public Builder propertyA(String property) {
this.aProperty = property;
return this;
}
public Builder propertyB(String property) {
this.bProperty = property;
return this;
}
public Builder propertyC(String property) {
this.cProperty = property;
return this;
}
public Builder cSpecificProperty(String property) {
this.cSpecificProperty = property;
return diffuse(this.typeEvents.get(CType.class), this);
}
private Builder diffuse(Event event, Builder returnee) {
event.on();
return returnee;
}
public <T extends AType> T on() {
T aType = this.typeFactory.get();
aType.aProperty = this.aProperty;
aType.bProperty = this.bProperty;
aType.cProperty = this.cProperty;
return aType;
}
}
public String aProperty, bProperty, cProperty;
public String toString() {
return "Property A: " + aProperty
+ System.lineSeparator() + "Property B: " + bProperty
+ System.lineSeparator() + "Property C: " + cProperty;
}
}
public static class CType extends AType {
public static class Factory implements TypeFactory {
private AType.Builder builder;
private Factory(AType.Builder builder) { this.builder = builder; }
@Override
@SuppressWarnings("unchecked")
public CType get() {
CType cType = new CType();
cType.cSpecificProperty = builder.cSpecificProperty;
return cType;
}
}
public String cSpecificProperty;
public String toString() {
return super.toString() + System.lineSeparator()
+ "C specific property: " + cSpecificProperty;
}
}
private static class TypeFactories {
private static final Map<Class, Function<AType.Builder, TypeFactory>> FACTORIES = new HashMap<>();
static {
TypeFactories.register(AType.class
, (builder) -> { return new AType.Factory(); });
TypeFactories.register(CType.class, (builder) -> { return new CType.Factory(builder); });
}
public static boolean register(Class typeClass, Function<AType.Builder, TypeFactory> factory) {
return FACTORIES.put(typeClass.getClass(), factory) != null;
}
public static TypeFactory get(Class typeClass, AType.Builder builder) {
return TypeFactories.FACTORIES.get(typeClass.getClass()).apply(builder);
}
}
private interface TypeFactory { <B extends AType> B get(); }
}
Following is a use case to detail the usage...
import Conditional.Or;
import PropertySource.AType;
public class PropertySourceTest {
public static void main(String[] args) {
AType.Builder builder = AType.builder();
PropertySource propertySource = new PropertySource();
Conditional<PropertySource> conditional = Conditional.empty();
Runnable aPropertyBuilder = () -> {
builder.propertyA(propertySource.read());
propertySource.reset();
};
Runnable bPropertyBuilder = () -> {
builder.propertyB(propertySource.read());
propertySource.reset();
};
Runnable cPropertyBuilder = () -> {
builder.cSpecificProperty(propertySource.read());
propertySource.reset();
};
Runnable propertyCBuilder = () -> {
builder.propertyC(propertySource.read());
propertySource.reset();
};
Or<PropertySource> propertyA = conditional.when((tested) -> {
return tested.read("Property A [a property, property a]: ").equals("property a");
}).run(() -> { aPropertyBuilder.run(); });
propertyA
.then()
.when((tested) -> {
return tested.read("Property B [property b]: ").equals("property b");
}).run(() -> { bPropertyBuilder.run(); });
propertyA
.or()
.when((tested) -> {
return tested.read().equals("a property");
}).run(() -> { aPropertyBuilder.run(); })
.then()
.when((tested) -> {
return tested.read("Property C [c property, property c]: ").equals("property c");
}).run(() -> { propertyCBuilder.run(); })
.or()
.when((tested) -> {
return tested.read().equals("c property");
}).run(() -> { cPropertyBuilder.run(); });
propertyA.on(propertySource);
System.out.println(builder.on());
}
}
clap
,tintin
) that make no sense; plus it's difficult to imagine any application for which this isn't vastly over-designed. Without a compelling use case, I can only consider this code hypothetical and ineligible for review. \$\endgroup\$