ChoiceFormula
Turns a CHOICE_FORMULA custom function into a CASE_WHEN one and reads the result through a CaseWhen. The first argument is the name of a choice list, and the arguments after it are condition and result pairs. ChoiceSpecifications takes the formula in, ChoiceName answers the list name, Valid says whether the formula can be read, FailureReason names what stopped it, ParsedFormula gives the nested Excel IF text, and Categories lists the result labels.
Depends on: CaseWhen, BetterArray
Version: 1.0 (2026-02-09)
Factory
Create #
create
Instantiate a ChoiceFormula parser bound to a formula
Signature:
Public Function Create(ByVal formula As String) As ChoiceFormula
Trims the incoming formula, stores it in a new instance, and returns the ChoiceFormula for fluent downstream operations. The token check, the argument split and the choice name are all resolved at assignment, because the formula cannot change after Seal. The CASE_WHEN wrapper stays lazy: SetupErrors builds one of these per dictionary row and reads the choice name alone.
Parameters:
formula: String. Representation of a CHOICE_FORMULA call.
Returns: ChoiceFormula. A ready-to-query parser instance.
Validation
Valid #
valid
Determine whether the stored formula is a CHOICE_FORMULA with arguments
Signature:
Public Property Get Valid() As Boolean
Reads the answers computed when the formula was stored.
Requires the CHOICE_FORMULA token, balanced parentheses, and at least three arguments (name plus one condition/result pair). A blank argument is dropped by the splitter, so it lowers the argument count: writing CHOICE_FORMULA(mylist, , "Yes") leaves two arguments and fails the count.
Returns: Boolean. True when the specification can be converted to CASE_WHEN.
FailureReason #
failure-reason
Explain why the specification was rejected
Signature:
Public Property Get FailureReason() As String
Holds the message for the check that failed, and an empty string when the specification is valid. ParsedFormula answers with an empty string for a bad expression, and an empty string on its own reads as "no formula was written". CaseWhen and ValueOfFormula carry the same property.
Returns: String. The failure message, or an empty string.
Conversion
ParsedFormula #
parsed-formula
Convert CHOICE_FORMULA into nested IF statements through CASE_WHEN
Signature:
Public Function ParsedFormula() As String
Core conversion, category extraction, and choice name resolution.
Builds the equivalent CASE_WHEN expression from the parsed arguments, then delegates to the CaseWhen class to produce the nested IF output. Returns an empty string when the formula is invalid or conversion fails.
Returns: String. Expression ready for worksheet consumption.
Categories #
categories
Extract the result labels from the CHOICE_FORMULA results
Signature:
Public Function Categories() As BetterArray
Delegates to the CaseWhen converter to reuse category parsing logic, and hands back what it returns. CaseWhen builds a fresh array on every call, so no copy is made here. Returns an empty BetterArray when the formula is invalid or the CaseWhen wrapper cannot be created.
Returns: BetterArray. Collection of plain category labels.
ChoiceName #
choice-name
Retrieve the name of the choice list referenced by the formula
Signature:
Public Function ChoiceName() As String
Returns the first argument of the CHOICE_FORMULA expression, which identifies the choice list that provides the valid options. It is resolved at creation, and it answers even when the specification is invalid: a formula that names a list and stops still names a list, and SetupErrors reads the name to collect the lists a dictionary uses.
Returns: String. Choice list identifier.
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
ChoiceSpecifications #
choice-specifications
Retrieve the stored CHOICE_FORMULA expression
Signature:
Public Property Get ChoiceSpecifications() As String
Properties that expose internal state.
Returns the original formula text as stored after trimming during assignment. This is the full CHOICE_FORMULA(...) expression. It is the read half of the pair the factory writes through.
Returns: String. Original formula specification.
ChoiceSpecifications #
choice-specifications-set
Store the CHOICE_FORMULA expression and parse it once
Signature:
Public Property Let ChoiceSpecifications(ByVal formula As String)
Trims the incoming formula and stores it, then runs the token check, the argument split and the choice-name resolution right here. Every public answer below reads a field afterwards. The old class ran the sanitiser up to four times and the validity check once per public call, over a string that cannot change after Seal.
Parameters:
formula: String. Expression to parse.
PrivateHelpers
EvaluateFormula #
evaluate-formula
Check the token, split the arguments and resolve the choice name
Signature:
Private Sub EvaluateFormula()
Internal utilities for parsing and CASE_WHEN conversion.
Runs once, from the formula setter. Sets the argument body, the argument list, the choice name, the validity answer and the failure message.
NewTable #
new-table
Build an empty one-based BetterArray
Signature:
Private Function NewTable() As BetterArray
Every array this class hands out or holds starts at index 1.
Returns: BetterArray. An empty array with LowerBound 1.
SplitArguments #
split-arguments
Split the argument body into individual arguments
Signature:
Private Function SplitArguments(ByVal body As String) As BetterArray
Walks the body character by character, tracking quotation state and parenthesis depth. Splits on top-level commas only. Blank segments are dropped, so a blank argument lowers the argument count rather than being stored. CaseWhen.SplitFormulaSegments and ValueOfFormula.ExtractArguments hold the same walk; ValueOfFormula keeps its blank segments, because it counts them. The three stay separate: a class cannot reach a standard module, so a shared copy has nowhere to live.
Parameters:
body: String. The comma-delimited text between the parentheses.
Returns: BetterArray. The choice name followed by condition/result entries.
CaseWhenFormula #
case-when-formula
Build the equivalent CASE_WHEN formula from parsed arguments
Signature:
Private Function CaseWhenFormula() As String
Skips the first argument (choice list name) and joins the remaining condition/result entries into a CASE_WHEN(...) expression string. Returns an empty string when nothing follows the name. The join uses ", ". CaseWhen splits on a top-level comma and trims each segment, so the extra space is dropped again. The two have to keep agreeing on the separator.
Returns: String. CASE_WHEN expression or empty when there is nothing to build.
CaseWhenWrapper #
case-when-wrapper
Provide a lazily created CaseWhen instance
Signature:
Private Property Get CaseWhenWrapper() As CaseWhen
Builds the CASE_WHEN expression from parsed arguments and delegates to CaseWhen.Create to obtain the parser. The instance is cached and the same one is handed to every caller: CaseWhen is read-only after Seal, so sharing it is safe. Making CaseWhen mutable would break this.
Returns: CaseWhen. The converted formula parser.
ThrowError #
throw-error
Raise a project-specific error
Signature:
Private Sub ThrowError(ByVal errNumber As Long, ByVal message As String)
Wrapper around Err.Raise that standardises the source to the class constant CLASSNAME for consistent stack traces. The code is taken as a Long: a named enum type in a parameter position is the macOS trap.
Parameters:
errNumber: Long. The error code describing the failure.message: String. Error message to propagate.
Throws:
- ProjectError.
Always raises the specified error.
Used in (6 file(s))
- CaseWhen.cls
- Formulas.cls
- ValueOfFormula.cls
- EventSetup.cls
- SetupErrors.cls
- TestChoiceFormula.bas