Building context with flexible adapters significantly reduces coding time. The goal is to do the mappings only and retain lazy value fetching.
Following excerpts are taken from Hyperon Motor-Insurance Demo App.
Context Implementation
public class MotorContext extends HyperonContext {
private Quote quote;
private Coverage coverage;
private Driver driver;
private Option option;
...
public Object get(String path) {
String name = getFirstToken(path);
String subpath = skipFirstToken(path);
Adapter adapter = null;
if (name.equals("quote")) {
adapter = new QuoteAdapter(getQuote());
}
if (name.equals("coverage")) {
adapter = new CoverageAdapter(getCoverage());
}
if (name.equals("option")) {
adapter = new OptionAdapter(getOption());
}
if (adapter != null) {
return adapter.get(subpath);
}
return null;
}
private String getFirstToken(String path) {
return StringUtils.substringBefore(path, ".");
}
private String skipFirstToken(String path) {
return StringUtils.substringAfter(path, ".");
}
}
Path argument of get method is divided into tokens ('.' delimited). Based on first token's value the proper adapter is chosen and remaining tokens (subpath) are passed to it.
Sample adapters
These are sample adapters:
public class QuoteAdapter extends Adapter {
private Quote quote;
public QuoteAdapter(Quote quote) {
this.quote = quote;
}
protected Mapping getMapping() {
return new Mapping()
.add("planCode", quote != null ? quote.getPlan() : null)
.add("driver", new DriverAdapter(quote.getDriver()))
.add("vehicle", new VehicleAdapter(quote.getVehicle()))
.add("options", new CollectionAdapter(quote.getOptions(),
option -> new OptionAdapter((Option) option)
));
}
}
public class CoverageAdapter extends Adapter {
private Coverage coverage;
public CoverageAdapter(Coverage coverage) {
this.coverage = coverage;
}
public Mapping getMapping() {
return new Mapping()
.add("code", coverage.getCode())
.add("limit1", coverage.getLimit1())
.add("limit2", coverage.getLimit2())
.add("premium", coverage.getPremium())
.add("option", new OptionAdapter(coverage.getOption()))
.add("quote", new QuoteAdapter(coverage.getQuote()));
}
}
The role of the getMapping() is to match single token extracted from path to final value e.g.
code -> coverage.getCode()
or next adapter in chain e.g.
option -> new OptionAdapter(coverage.getOption())
The subsequent adapter that is called will do exactly the same. This way the entire path is readily covered.
Having context implemented in such way, we can query for all valid paths:
Driver driver = new Driver()
.setFirstName("John")
.setLastName("Potter")
.setGender("M")
.setDateOfBirth(Date.from(LocalDate.now().minusYears(40) ...)
.setAddress(new Address("Lake Jackson", "Allwood St", "77566"));
Quote quote = new Quote("FULL", driver);
Option option1 = new Option("OPT1", 1); s
Option option2 = new Option("OPT2", 2);
quote.addOption(option1);
quote.addOption(option2);
Coverage cov = new Coverage("BI", 310); 1
option1.addCoverage(cov);
HyperonContext ctx = new MotorContext(cov);
assertEquals(ctx.getString("quote.driver.address.zipcode"), "77566");
assertEquals(ctx.getInteger("quote.driver.age").intValue(), 40);
assertEquals(ctx.getString("coverage.option.quote.driver.address.zipcode"), "77566");
assertEquals(ctx.getDecimal("coverage.premium").intValue(), 310);
...