FormulaCondition
Pairs the variables of an analysis formula with their condition fragments and builds the Excel predicates from them. LstVars takes the variable list and LstConditions the matching condition list. Valid says whether the two lists line up and whether every variable sits in one table, VariablesTable names that table, ConditionString renders one condition wrapped in an IF, and ConditionPredicate joins the conditions into a single predicate. Variables and Conditions give the two lists back, and entries filed on the way leave through HasCheckings and CheckingValues.
Depends on: BetterArray, Checking, LLVariables, LLdictionary
Version: 1.0 (2026-02-09)
Factory
Create #
create
Construct a FormulaCondition instance from paired arrays
Signature:
Public Function Create(ByVal formVar As BetterArray, ByVal formCond As BetterArray) As FormulaCondition
Validate paired arrays and expose the ready-to-use condition helper.
Validates that both arrays are non-Nothing and have equal lengths, then stores normalised copies with a lower bound of 1.
Parameters:
formVar: BetterArray. The array containing variable names.formCond: BetterArray. The array containing predicate fragments.
Returns: FormulaCondition. A ready-to-use condition helper.
Throws:
- ProjectError.InvalidArgument When formVar or formCond is Nothing or lengths differ.
PublicAccessors
LstConditions #
conditions
Retrieve the stored condition fragments
Signature:
Public Property Get LstConditions() As BetterArray
Provide controlled access to stored collections.
Returns the internal BetterArray holding the condition predicates in the same order they were supplied at creation.
Returns: BetterArray. The condition fragments preserving the original order.
LstVars #
variables
Retrieve the stored variables list
Signature:
Public Property Get LstVars() As BetterArray
Returns the internal BetterArray holding the variable names in the same order they were supplied at creation.
Returns: BetterArray. The variable names preserving the original order.
InternalState
HasCheckings #
has-checkings
Determine whether diagnostics have been captured
Signature:
Public Property Get HasCheckings() As Boolean
Returns True when at least one LogInfo call has been made since the last validation run, indicating that diagnostic information is available.
Returns: Boolean. True when internal checks exist.
MetadataHelpers
VariablesTable #
variables-table
Resolve the table name shared by all tracked variables
Signature:
Public Property Get VariablesTable(ByVal dict As LLdictionary) As String
Provide metadata derived from the last validation run.
Returns the cached table name when validation has already occurred. When the cache is empty, triggers a full validation pass against the supplied dictionary before returning the result. Formulas.ParsedCustomFormula calls this as a fallback when the table name is unknown, so it sits on the hot path for N and N() formulas.
Parameters:
dict: LLdictionary. The dictionary used when validation has not occurred yet.
Returns: String. Table identifier or empty string when unavailable.
Validation
Valid #
valid
Validate variable/condition lists against the dictionary
Signature:
Public Function Valid(ByVal dict As LLdictionary, Optional ByVal tablename As String = vbNullString) As Boolean
Confirm that variables/conditions are aligned and belong to the same table.
Checks that the lists are non-empty, then verifies each variable exists in the dictionary and belongs to the expected table. When a tablename argument is supplied it overrides the table resolved from the first variable. The result is cached against BOTH the tablename argument and the dictionary object. Keying on the tablename alone handed the answer computed for one dictionary back to a caller asking about another one.
Parameters:
dict: LLdictionary. The metadata provider used for variable lookups.tablename: Optional String. When supplied, forces the expected table name. Defaults to vbNullString.
Returns: Boolean. True when validation succeeds.
AdoptVariablesHelper #
adopt-variables-helper
Take the helper the owner already holds for this dictionary
Signature:
Public Sub AdoptVariablesHelper(ByVal varsHelper As LLVariables, ByVal dict As LLdictionary)
A fresh instance of this class is built for every analysis formula, and each one used to read the dictionary sheet again to build its own LLVariables: several block reads per formula, repeated for every column of every table. Formulas holds one helper per table and hands it in here before Valid runs, so VariablesProvider finds the pair already bound. The helper handed in must have been built over dict; the caller owns that pairing.
Parameters:
varsHelper: LLVariables. The helper to reuse.dict: LLdictionary. The dictionary the helper was built over.
FormulaRendering
ConditionString #
condition-string
Wrap the predicate in an IF expression referencing the supplied variable
Signature:
Public Function ConditionString(ByVal tablename As String, ByVal varName As String, _
Optional ByVal Connector As String = "*") As String
Produce Excel-ready predicates and wrapper formulas.
Joins all variable/condition pairs using ConditionPredicate, then wraps the result in an IF function that returns the variable value when the predicate evaluates to True. Returns an empty string when the predicate is empty. The space before the comma is deliberate: CrossTableFormula compares generated formulas in its tests.
Parameters:
tablename: String. Structured reference table name.varName: String. Variable/column name to return when the condition is met.Connector: Optional String. Connector used between predicates. Defaults to "*".
Returns: String. An IF expression or empty string when the predicate is empty.
ConditionPredicate #
condition-predicate
Join the variable predicates using the supplied connector
Signature:
Public Function ConditionPredicate(ByVal tablename As String, Optional ByVal Connector As String = "*") As String
Iterates through all variable/condition pairs and concatenates them into a single predicate string using the supplied connector (typically "*" for AND logic). Each fragment is wrapped in parentheses using structured table references, and the condition fragment carries its own operator: ">= 5" becomes (f_table[age]>= 5). Formulas.ClearCountIf rewrites those fragments for COUNTIFS, so the two have to keep agreeing on the shape. The answer is held against the table name and the connector. The lists cannot change after Seal, so the same pair always gives the same string, and Formulas.ParsedAnalysisFormula asks for it once per variable token of the same formula.
Parameters:
tablename: String. Structured reference table name.Connector: Optional String. Connector between predicate fragments. Defaults to "*".
Returns: String. Concatenated predicate or empty string when inputs are missing.
PublicDiagnostics
Variables #
variables-clone
Retrieve a clone of the stored variable names
Signature:
Public Property Get Variables() As BetterArray
Defensive copies of the tracked lists and the diagnostic log.
Returns a BetterArray copy of the variable identifiers in the order they were supplied at creation.
Returns: BetterArray. The variable names preserving the original order.
Conditions #
conditions-clone
Retrieve a clone of the stored condition fragments
Signature:
Public Property Get Conditions() As BetterArray
Returns a BetterArray copy of the predicate fragments in the order they were supplied at creation.
Returns: BetterArray. The condition fragments preserving the original order.
CheckingValues #
checking-values
Retrieve the internal checking log
Signature:
Public Property Get CheckingValues() As Checking
Returns the accumulated Checking instance containing all diagnostic messages from the last validation run, or Nothing when none recorded.
Returns: Checking. The diagnostic log.
Internal members (not exported)
Factory
Seal #
seal
Prevent further changes to setup-only properties
Signature:
Public Sub Seal()
Marks the instance as sealed so guarded setters raise when invoked after construction. Called by the factory before returning.
GuardNotSealed #
guard-not-sealed
Reject writes to setup-only properties after sealing
Signature:
Private Sub GuardNotSealed(ByVal propName As String)
Raises an error when a guarded setter is invoked on a sealed instance.
Parameters:
propName: String. Name of the property being guarded.
PublicAccessors
LstConditions #
conditions-set
Store the condition fragments and reset caches
Signature:
Public Property Set LstConditions(ByVal formCond As BetterArray)
Copies the incoming array into a new BetterArray normalised to a lower bound of 1, then resets all cached validation artefacts so the next call to Valid triggers a fresh check. Both setters copy with LowerBound 1, so the two lists always share one lower bound. ConditionPredicate indexes both lists from that bound and it is correct because of this.
Parameters:
formCond: BetterArray. The array containing predicate fragments.
LstVars #
variables-set
Store the variables list, normalise bounds, and reset caches
Signature:
Public Property Set LstVars(ByVal formVar As BetterArray)
Copies the incoming array into a new BetterArray normalised to a lower bound of 1, then invalidates all cached data.
Parameters:
formVar: BetterArray. The array containing variable identifiers.
InternalState
ResetCaches #
reset-caches
Clear cached table names, validation results, and diagnostics
Signature:
Private Sub ResetCaches()
Maintain cached validation artefacts and diagnostics.
Resets all memoised state so the next call to Valid, VariablesTable or ConditionPredicate starts fresh. Its two callers are the setters above, and both are guarded by Seal, so this runs at construction only.
ClearCheckings #
clear-checkings
Drop the diagnostic entries of the previous validation run
Signature:
Private Sub ClearCheckings()
LogInfo #
log-info
Append a diagnostic entry to the internal checking log
Signature:
Private Sub LogInfo(ByVal message As String, Optional ByVal scope As Byte = checkingInfo)
Creates the checking instance on first use and appends the message using a monotonically increasing counter key. The log is what says WHY a formula failed: the analysis path gives up quietly when Valid is False, and the reason lives here.
Parameters:
message: String. The event description to record.scope: Optional Byte. Severity level from CheckingLogType. Defaults to checkingInfo.
Validation
VariablesProvider #
variables-provider
Build the variables helper once per dictionary
Signature:
Private Function VariablesProvider(ByVal dict As LLdictionary) As LLVariables
Valid used to build a fresh LLVariables on every call, and Formulas.ParsedAnalysisFormula calls Valid once per variable token of every analysis formula. Each helper carries its own block reads over the same dictionary worksheet. The helper is held against the dictionary it was built over, so a call naming another dictionary builds a new one.
Parameters:
dict: LLdictionary. The dictionary to read.
Returns: LLVariables. Helper bound to that dictionary.
ErrorHandling
ThrowError #
throw-error
Raise a VBA error tagged with the class name
Signature:
Private Sub ThrowError(ByVal errNumb As Long, ByVal errorMessage As String)
Centralised helper to raise ProjectError-compliant errors.
Wrapper around Err.Raise that standardises the source to "FormulaCondition", providing a consistent stack trace.
Parameters:
errNumb: Long. ProjectError code.errorMessage: String. Descriptive text.
Throws:
- ProjectError.
Always raises the specified error.
Used in (6 file(s))
- FormulaBuilder.cls
- Formulas.cls
- Checking.cls
- TestFormulaBuilder.bas
- TestFormulaCondition.bas
- TestFormulas.bas