PostProcessorUtils

This class groups several small utility functions.

Level and hierarchy tools

These functions generate an ILevelInfo or IHierarchyInfo object from a String with the help of the IActivePivot object.

Formats:

  • level string: “level@hierarchy@dimension”
  • hierarchy string: “hierarchy@dimension”

optionalLevelInfo

Returns null or ILevelInfo object depending on the nullability or emptiness of the input string.

toLevelInfo

Returns a list of ILevelInfo from an input list string, comma-separated by default.

toHierarchyInfo

Returns a list of IHierarchyInfo from an input list string, comma-separated by default.

toLevelInfoMap

Returns a map ILevelInfo => String from an input list string, comma-separated by default with equality between level and value.

toLevelInfoByHierarchy

Returns a map IHierarchyInfo => ILevelInfo from an input list string of levels, the level belonging to the key hierarchy.

Aggregation

createAggregationFunction

Creates an aggregation function from a plugin key or the “aggregationFunction” property.

addPartitioningLevel(s)

Modifies or creates a list of levels by adding one or more extra levels.

removePartitioningLevel(s)

Modifies or creates a list of levels by removing one or more levels.

reduce

Aggregates several partitioned results into one single result.

Location manipulation

This section contains all the functions that can be used to manipulate the location; modifying the ILocation or the ICubeFilter.

truncateToLevelZero

Performs a drill-up to the top-level location for a selected sub-set of hierarchies.

truncateOrSetWildCardsToLevel

Performs a drill-up or drill-down to the selected level, and fills the empty levels with null or a wildcard value.

setWildCardsToLevel

Performs a drill-down to the selected level, and fills the empty levels with null or a wildcard value.

truncateToLevel

Performs a drill-up to the selected level or returns null if the corresponding hierarchy hasn’t reached the selected level.

removeWildcards

Performs a drill-up through every hierarchy until it removes any wildcard.

setCoordinate

Sets the location level to the specified value, and performs a drill-down if needed.

setFilter

Sets the location level to the specified value, only if the current location is a range compatible with the value.

checkFilter

Checks if the current location is a range compatible with the value.

shift

Sets the location level to the specified new value, only if the current location is compatible with the old specified value.

childLocation

Performs a drill-down or drill-up to the levels, with wildcards.

descendentLocation

Performs a drill-down to the levels, with wildcards.

filterMembers

Creates a filter from a list of levels and a list of allowed members (separated with “:”).

createPath

Creates a path with wildcard and the desired member at the selected level.

ancestorLocation

Gets ancestor location along risk factor hierarchy up to level in original location.

Copper Levels and Hierarchy

Converts hierarchy and level identifiers into Copper objects.

copperLevel

Transforms the input String or LevelIdentifier into a CopperLevel object.

copperHierarchy

Transforms the input String or HierarchyIdentifier into a CopperHierarchy object.

Caching

Functions used to handle a concurrent map (like the query cache) into a cache object.

getInCacheOrComputeNeverNull

Retrieves an object from the cache or calls the lambda to compute it if not found. The computation is not synchronized and cannot be done twice. It is assumed that the cached value is never null.

getInCacheOrCompute

Retrieves an object from the cache or calls the lambda to compute it if not found. The computation is not synchronized and can be done twice. The cached value could be null.

Atoti plugin injection

Here are the utility functions used to inject beans into Atoti plugins. To be injected, the plugin must follow this pattern:

public abstract class FXPostProcessor extends ADynamicAggregationPostProcessor implements IFXRatesAware {
    IFxRates fxRates;

    @Override
    public void setFxRates(IFxRates fxRates) {
        this.fxRates = fxRates;
    }
}

The Aware interface must follow this specific pattern:

public interface IFXRatesAware {

    // This is used to find the setter, the value must match.
    // The name of the constant (PROPERTY_NAME) is also fixed.
	String PROPERTY_NAME = "FxRates";

	void setFxRates(IFxRates fxRates);
}

injectAll

Injects a bean to all plugins.

injectAllExtendedPlugin

Injects into extended plugins.

injectAllPlugin

Injects into simple plugins.

dateSearch

Finds the first available date matching the requirements. It calls the lambda to perform an initial date step and then again every time the result value is null. The function IStepDate::stepDate provides a useful stepper feature that steps from one asOfDate to another inside a table.

var fx = dateSearch(
        asOfDate,
        (date, first) -> myFunctionThatMoveToTheRightDate(date, first),
        maxFallbackDays,
        date -> myFunctionThatReturnTheValueOrNullIfNotAvailable());

Post-processor helpers

hasANullEntry

Checks that an input value is null. It is useful for Copper::map or the post-processor compute phase.

return Copper.combine(topPnL, childPnL, timePeriod, roundingMethod, quuantile2Rank)
        .map((reader, writer) -> {
            if(hasANullEntry(reader, 0, 1, 3, 4)) {
                writer.writeNull();
            } else {
                writer.writeDouble(componentVaR(reader));
            }}, ILiteralType.DOUBLE);

checkOutputType

The type must be set for every post-processor. However, for most of them the type is fixed and can’t be changed. So this method should be used inside a post-processor to check that the specified type is the expected one.

@QuartetExtendedPluginValue(intf = IPostProcessor.class, key = PLUGIN_KEY)
public class MyPostProcessor extends ABasicPostProcessor {
    @Override public void init(Properties properties) throws ActiveViamException {
        PostProcessorUtils.checkOutputType(properties, ILiteralType.DOUBLE);
        super.init(properties);
    }
}

checkLeafType

For dynamic post-processors, the leaf must also be typed. So this method should be used inside a post-processor to check that the specified type is the expected one.

@QuartetExtendedPluginValue(intf = IPostProcessor.class, key = PLUGIN_KEY)
public class MyPostProcessor extends ABasicPostProcessor {
    @Override public void init(Properties properties) throws ActiveViamException {
        PostProcessorUtils.checkOutputType(properties, ILiteralType.DOUBLE);
        PostProcessorUtils.checkLeafType(properties, ILiteralType.DOUBLE);
        super.init(properties);
    }
}

checkPassThroughUnderlyingType

When a post-processor only has a single parameter it may return a metric of the same type as the underlying parameter. This function checks that the types match.

Use the function as follows:

@QuartetExtendedPluginValue(intf = IPostProcessor.class, key = PLUGIN_KEY)
public class MyPostProcessor extends ABasicPostProcessor {
    @Override
    protected int computeOutputType(final @NonNull Properties properties) throws PostProcessorInitializationException {
        return checkPassThroughUnderlyingType(getMeasuresProvider(), underlyingMeasures.get(0), super.computeOutputType(properties));
    }
}

getDictionary

In the latest version of Atoti, the datastore dictionaries are hidden. This method can be used to access them.

toFieldPath

This function is the reverse of FieldPath::toString. It can be used to stream out a FieldPath.

getKeyFields

This function returns the positions of the key fields inside a table. It can be used to extract the key from a tuple.

transfer

This is a set of functions that are intended to copy a value from one holder to another by taking case of its type. It can copy :

  • From a IRecordReader slot to a IWritableCell
  • From a IArrayReader slot to a IWritableCell
  • From a IArrayReader slot to another slot on a IArrayWriter

registerExtraLeafs

This function is intended to be called on the init phase of a postprocessor just before the call of the super init. It will add some extra required field required by the extra leaf coordinate feature. Here is a sample :

@QuartetExtendedPluginValue(intf = IPostProcessor.class, key = MyPostProcessor.PLUGIN_KEY)
public class MyPostProcessor extends ADynamicAggregationPostProcessor implements ICustomParametersAware {

    protected ICustomParameters customParameters;
    protected LocationFunction locationFunction;

    @Override
    public void init(Properties properties) throws ActiveViamException {
        locationFunction = (customParameters != null) ? customParameters.getLeafCoordinatesFunction(pivot, properties, getType()) : null;
        var orgLeafs = PostProcessorUtils.registerExtraLeafs(locationFunction, pivot, properties);
        super.init(properties);
        // Extra setup
        // orgLeaf contains the leafs passed to the PP in its properties
    }

    @Override
    public void setCustomParameters(ICustomParameters customParameters) {
        this.customParameters = customParameters;
    }

    @Override
    protected void evaluateLeaf(@NonNull ILocation leafLocation, @NonNull IRecordReader underlyingMeasures, @NonNull IWritableCell resultCell) {
        var leafCoordinates = locationFunction != null ? locationFunction.apply(leafLocation, getQueryCache()) : List.of();
        // Call a service that requires leafCoordinates as a parameter
    }
}