Column Calculators
This page provides a description of how we can add a new column calculator. It features an example in which we populate a new field to SaCvaVegaRiskWeight store. The techniques employed are generic examples that can be extended, adapted and repeated for any use case that we need. In all cases, we make minimal changes to the Reference Implementation.
Step 1 - Define datastore field via the customisations config
The first thing we want to do is to add a field that we will populate with our column calculator.
Datastore field additions are made in the addModifications() method of DatastoreCustomisationsConfig at cvarc-application/src/main/java/com/activeviam/cvarc/application/cfg/DatastoreCustomisationsConfig.java. This class lives in your project’s cvarc-application module and is intended to be edited directly.
See more customizations achievable with Datastore Helper.
@Override
public void addModifications(IDatastoreConfigurator configurator) {
configurator.appendField("SaCvaVegaRiskWeight", new CustomField("FileName", ILiteralType.STRING));
configurator.appendField("SaCvaVegaRiskWeight", new CustomField("Math", ILiteralType.DOUBLE));
}
Step 2 - Define the column calculator class
Create your column calculator class in cvarc-application/src/main/java/com/activeviam/cvarc/application/cfg/extensions/. The calculator must implement IColumnCalculator<ILineReader> (from com.activeviam.source.common.api). The CRIF as-of-date calculator (com.activeviam.cvarc.common.source.CrifAsofDateColumnCalculator in cvarc-common-lib) is the canonical reference implementation.
package com.activeviam.cvarc.application.cfg.extensions;
import com.activeviam.source.common.api.IColumnCalculator;
import com.activeviam.source.common.api.IColumnCalculationContext;
import com.activeviam.source.csv.api.ILineReader;
public class FileNameColumnCalculator implements IColumnCalculator<ILineReader> {
private final String columnName;
public FileNameColumnCalculator(String columnName) {
this.columnName = columnName;
}
@Override
public String getColumnName() {
return columnName;
}
@Override
public Object compute(IColumnCalculationContext<ILineReader> context) {
return context.getContext().getCurrentFile().getName();
}
}
A calculator can also derive its value from other columns on the current row using context.getValue("columnName"). The example below populates the Math field from RiskWeight and ParameterC:
package com.activeviam.cvarc.application.cfg.extensions;
import com.activeviam.source.common.api.IColumnCalculator;
import com.activeviam.source.common.api.IColumnCalculationContext;
import com.activeviam.source.csv.api.ILineReader;
public class MathColumnCalculator implements IColumnCalculator<ILineReader> {
private final String columnName;
public MathColumnCalculator(String columnName) {
this.columnName = columnName;
}
@Override
public String getColumnName() {
return columnName;
}
@Override
public Object compute(IColumnCalculationContext<ILineReader> context) {
double riskWeight = (double) context.getValue("RiskWeight");
double parameterC = (double) context.getValue("ParameterC");
return riskWeight + parameterC;
}
}
Step 3 - Attach the calculator to a topic
The DLC framework attaches calculators to topics through a topic’s ChannelDescription. To attach a calculator without re-listing the topic’s column set, extend the relevant *SourceDescription class, override the topic bean, and use .toBuilder() to start from the parent’s CsvTopicDescription and add the calculator inline.
Example — attaching both calculators to cvaDeltaTopic. The same pattern applies to any topic that has an attached channel; chain a .customField(...) call per calculator.
package com.activeviam.cvarc.application.cfg.extensions;
import com.activeviam.cvarc.starter.cfg.impl.dlc.SASourceDescription;
import com.activeviam.io.dlc.impl.description.topic.CsvTopicDescription;
import com.activeviam.io.dlc.impl.description.topic.channel.ChannelDescription;
import com.activeviam.io.dlc.impl.description.topic.channel.column.calc.AnonymousCustomFieldDescription;
import com.activeviam.io.dlc.impl.utils.NamedEntityResolverService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
@Configuration
@Primary
public class ExtendedSASourceDescription extends SASourceDescription {
@Bean
@Override
public CsvTopicDescription cvaDeltaTopic(
NamedEntityResolverService namedEntityResolver,
Environment env) {
CsvTopicDescription base = super.cvaDeltaTopic(namedEntityResolver, env);
return base.toBuilder()
.channel(ChannelDescription.builder(
namedEntityResolver.getTarget(CVA_DELTA_SENSITIVITIES_PUBLISHER))
.customField(AnonymousCustomFieldDescription.of(
s -> new FileNameColumnCalculator("FileName")))
.customField(AnonymousCustomFieldDescription.of(
s -> new MathColumnCalculator("Math")))
.build())
.build();
}
}
The super.cvaDeltaTopic(...) call inherits the parent’s parser and column list; .toBuilder() produces a mutable copy you can extend; each .customField(AnonymousCustomFieldDescription.of(...)) call registers a calculator inline against the channel.
note
The CRIF example in SharedSourceDescription.crifBaseTopic(...) (cvarc-starter/src/main/java/com/activeviam/cvarc/starter/cfg/impl/dlc/SharedSourceDescription.java) shows the same library APIs used to register a column calculator from scratch (without .toBuilder()).
Step 4 - Wire the extension into ApplicationConfig
Add ExtendedSASourceDescription to the @Import list of ApplicationConfig in cvarc-application/src/main/java/com/activeviam/cvarc/application/cfg/ApplicationConfig.java. The @Primary annotation on the extension class makes Spring prefer your overridden topic bean over the parent’s.