VarWriter

Writes a single dictionary variable onto one or more linelist worksheets. On an HList layer the writer targets the main sheet, a printed companion and a CRF companion; on a VList layer it writes the main sheet alone. Each call to WriteVariable applies the label text, the number format, the formula, the dropdown choices, the data validation and the conditional formatting to the cell range it resolves. ValueOf reads one dictionary column of the variable in hand, and entries filed along the way leave through HasCheckings and CheckingValues.

ONE WRITER PER SHEET

The writer is built once per data entry sheet and used for every variable of that sheet. SectionBuilder.Build owns it. What the class holds -- the dictionary block, the column map, the hidden name store, the design format and the translated messages -- covers a whole sheet, and the table anchor is set on the first variable the writer is handed.

HOW A DICTIONARY VALUE IS READ

The dictionary data block and its header row are read once, as two worksheet crossings, and every ValueOf after that answers from memory. A column added to the dictionary after the map was built is picked up on the first miss, the same way LLVariables.ResolveColumnIndex re-reads its header snapshot. A variable name is matched without regard to case here. LLdictionary.Prepare makes the names unique, so a name that differs only by case is a fault in the sheet.

Depends on: LinelistSpecs, LLdictionary, LLVariables, LLSheets, LLFormat, TranslationObject, FormulaData, Formulas, DropdownLists, LLGeo, HiddenNames, Checking, BetterArray, Column index the first variable of a sheet carries when the dictionary is, numbered as LLdictionary.Prepare leaves it. It is the fallback for the table, anchor; the first variable the writer is handed is the primary rule., What an automatic list adds to the name of the variable it collects from, so, its table never lands on the one that variable already owns. An origin very, often has a choice list of its own, and the two used to share a single table:, the first rebuild of the automatic list then replaced the setup choices with, whatever had been typed in the column. EventLinelist carries the same suffix, and rebuilds the table under this name., Checking support, The milestone record of the sheet: one entry per variable written. It is, held apart from internalChecks because the two travel to different places., internalChecks carries the problems and reaches the __check worksheet;, this one reaches the run log's in-memory record and the text file alone.

Version: 1.1 (2026-07-31)

Instantiation

Create #

create

Create a new VarWriter for the given layer and dependencies

Signature:

Public Function Create(ByVal layer As Byte, _
                       ByVal specs As LinelistSpecs, _
                       ByVal wksh As Worksheet, _
                       Optional ByVal printWksh As Worksheet = Nothing, _
                       Optional ByVal crfWksh As Worksheet = Nothing, _
                       Optional ByVal dropdownObj As DropdownLists = Nothing, _
                       Optional ByVal customDropdownObj As DropdownLists = Nothing) As VarWriter

Factory method that instantiates and configures a VarWriter. The caller must supply a valid LinelistSpecs and target Worksheet at minimum. Optional parameters allow attaching printed and CRF companion worksheets (used only for HList layers) and dropdown list managers.

Parameters:

  • layer: Byte. VarWriterLayer enum value (HList=1, VList=2). Use Byte for macOS compatibility.
  • specs: LinelistSpecs. The linelist specifications object providing dictionary, format, translations, etc.
  • wksh: Worksheet. The target main worksheet.
  • printWksh: Optional Worksheet. Printed companion worksheet (HList only). Defaults to Nothing.
  • crfWksh: Optional Worksheet. CRF companion worksheet (HList only). Defaults to Nothing.
  • dropdownObj: Optional DropdownLists. Dropdown lists manager. Defaults to Nothing.
  • customDropdownObj: Optional DropdownLists. Custom dropdown lists manager (for choice_custom). Defaults to Nothing.

Returns: VarWriter. A fully initialised writer bound to the given layer and worksheets.

Throws:

  • ProjectError.ObjectNotInitialized When specs or wksh is Nothing.
  • ProjectError.InvalidArgument When layer names neither VarWriterLayer member.

Entry Point

WriteVariable #

write-variable

Write one variable to the target worksheet(s)

Signature:

Public Sub WriteVariable(ByVal varName As String)

Main public method called once per variable. It stores the variable name, resets the cached row and cell ranges, then dispatches to WriteMainSheet. For HList layers it additionally dispatches to WritePrintSheet and WriteCRFSheet for the companion worksheets. A variable the dictionary gives no column index is skipped and filed through Checking, because the cell it would be written to cannot be resolved.

Parameters:

  • varName: String. The variable name as it appears in the dictionary.

Checkings

HasMilestones #

has-milestones

Whether any variable of this sheet was written

Signature:

Public Property Get HasMilestones() As Boolean

Returns: Boolean. True once one milestone entry has been filed.


MilestoneValues #

milestone-values

The per-variable record of this sheet

Signature:

Public Property Get MilestoneValues() As Checking

The store travels up through SectionBuilder and LLDataEntry to the build driver, which takes it into the run log record-only. It never reaches the __check worksheet.

Returns: Checking. The milestone entries, or Nothing.


VariablesWritten #

variables-written

How many variables this writer put on the sheet

Signature:

Public Property Get VariablesWritten() As Long

Counts the variables that reached the worksheet. A variable the dictionary gives no column index is filed as a problem and left out of the count.

Returns: Long. The number of variables written.


Internal members (not exported)

Instance Sealing

Seal #

seal

Seal the instance against further setup writes

Signature:

Public Sub Seal()

GuardNotSealed #

guard-not-sealed

Guard a setup-only setter against post-seal writes

Signature:

Private Sub GuardNotSealed(ByVal propName As String)

Parameters:


Internal Setters

InternalSpecs #

internal-specs

Assign the linelist specifications object

Signature:

Public Property Set InternalSpecs(ByVal specs As LinelistSpecs)

Properties used by the factory only during instantiation.


InternalWksh #

internal-wksh

Assign the target main worksheet

Signature:

Public Property Set InternalWksh(ByVal wksh As Worksheet)

InternalLayer #

internal-layer

Assign the writer layer (HList or VList)

Signature:

Public Property Let InternalLayer(ByVal layer As Byte)

InternalPrintWksh #

internal-print-wksh

Assign the printed companion worksheet

Signature:

Public Property Set InternalPrintWksh(ByVal wksh As Worksheet)

InternalCRFWksh #

internal-crf-wksh

Assign the CRF companion worksheet

Signature:

Public Property Set InternalCRFWksh(ByVal wksh As Worksheet)

InternalDropdown #

internal-dropdown

Assign the dropdown lists manager

Signature:

Public Property Set InternalDropdown(ByVal dropdownObj As DropdownLists)

InternalCustomDropdown #

internal-custom-dropdown

Assign the custom dropdown lists manager

Signature:

Public Property Set InternalCustomDropdown(ByVal dropdownObj As DropdownLists)

Cached Accessors

Dictionary #

dictionary

Return the cached dictionary object

Signature:

Private Property Get Dictionary() As LLdictionary

Every collaborator the writer uses is resolved once and held. Each of these is reached several times per variable, so a pass-through to this.specs on every use was paid a few hundred times per sheet.

Returns: LLdictionary. The linelist dictionary.


Variables #

variables

Return the cached variables helper

Signature:

Private Property Get Variables() As LLVariables

Kept for Contains, which the two conditional formatting routines ask for a variable that is not the one being written. Reading a column of the CURRENT variable goes through ValueOf, which answers from the cached dictionary block.

Returns: LLVariables. The variables helper object.


SheetsObject #

sheets-object

Return the cached sheets helper

Signature:

Private Property Get SheetsObject() As LLSheets

Provides VariableAddress and SheetInfo lookups used when building conditional-formatting formulas and resolving table anchors.

Returns: LLSheets. The sheets helper object.


DesignFormat #

design-format

Return the design format object from specs

Signature:

Private Property Get DesignFormat() As LLFormat

AddLabel, AddFormula, AddChoices, AddCRFLabel, AddCRFType and AddCRFChoices each ask for it, so it is resolved once per writer.

Returns: LLFormat. The design format provider.


Trans #

trans

Return the translation object from specs

Signature:

Private Property Get Trans() As TranslationObject

Returns: TranslationObject. The translation provider.


FormulaDataObj #

formula-data-obj

Return the formula data object from specs

Signature:

Private Property Get FormulaDataObj() As FormulaData

Provides the lookup tables and metadata needed by the Formulas parser.

Returns: FormulaData. The formula data provider.


EnsureMessages #

ensure-messages

Read the three translated label messages once

Signature:

Private Sub EnsureMessages()

MSG_Calculated, MSG_Mandatory and MSG_CustomChoice are constant for a whole build, and AddLabel used to fetch all three per variable and twice per HList variable.


Dictionary Row Cache

EnsureDataMap #

ensure-data-map

Read the dictionary block and build the two lookups

Signature:

Private Sub EnsureDataMap()

The dictionary block and its header row are read once per writer. Every ValueOf after that is an array read.

Two crossings: the data block and the header row. From them come a header to column-offset map and a variable name to row-offset map. Both keep the first entry when a name repeats, which is the rule LLVariables.HeaderColumn and LLVariables.RowIndex already apply.


RebuildDataMap #

rebuild-data-map

Read the dictionary block again

Signature:

Private Sub RebuildDataMap()

Called on the first use and again whenever a column name misses, which is how a column added to the dictionary after the writer was built resolves.


DictionaryGrewAColumn #

dictionary-grew-a-column

Test whether the dictionary is wider than the map was built from

Signature:

Private Function DictionaryGrewAColumn() As Boolean

One property read against a whole block read. AddLabel asks for list auto on every variable, and a dictionary that has not been through LinelistSpecs.Prepare carries no such column, so a rebuild on every miss made each variable pay for a full re-read of the dictionary and two lookup rebuilds for an answer that was always empty.

Returns: Boolean. True when the header row is wider than it was.


ColumnOffset #

column-offset

Offset of a dictionary column inside a data row

Signature:

Private Function ColumnOffset(ByVal colName As String) As Long

Whole string, without regard to case, which is what LLVariables.HeaderColumn answers. A Collection raises on a key it does not hold, and that is how a miss is read.

Parameters:

Returns: Long. The 1-based offset, or 0 when the column is absent.


RowOffset #

row-offset

Row of one variable inside the dictionary data block

Signature:

Private Function RowOffset(ByVal varName As String) As Long

Parameters:

Returns: Long. The 1-based row, or 0 when the variable is absent.


EnsureRowValues #

ensure-row-values

Hold the current variable's dictionary row in memory

Signature:

Private Sub EnsureRowValues()

BlockValues #

block-values

Read a Range as a two-dimensional 1-based array

Signature:

Private Function BlockValues(ByVal blockRng As Range) As Variant

Range.Value hands back a plain value for one cell and a two-dimensional array otherwise. This flattens both cases so callers see one shape.

Parameters:

Returns: Variant. A 1-based two-dimensional array of cell values.


RowOfValues #

row-of-values

Read a one-row Range as a 1-based list of values

Signature:

Private Function RowOfValues(ByVal rowRng As Range) As Variant

Parameters:

Returns: Variant. A 1-based one-dimensional array of cell values.


CellText #

cell-text

Read one cell value as text

Signature:

Private Function CellText(ByVal cellValue As Variant) As String

A dictionary cell can hold an error value or a Null, and CStr raises on both. Every read of the cached block goes through here, the same way every read in LLVariables goes through its own copy.

Parameters:

Returns: String. The text, or an empty string when there is none.


Value Lookup

ValueOf #

value-of

Read a dictionary column value for the current variable

Signature:

Public Property Get ValueOf(ByVal colName As String) As String

The class's read hook, and the way every other method retrieves variable metadata such as labels, types, formats and control information. An unknown column and an unknown variable both answer an empty string, which is what LLVariables.Value answers for the same two cases.

Parameters:

Returns: String. The column value for the current variable.


NumberOf #

number-of

Read a dictionary column value as a whole number

Signature:

Private Function NumberOf(ByVal colName As String) As Long

column index and crf index were read with a bare CLng(ValueOf(...)) at eight sites, guarded at three of them and unguarded at the rest. This is the one rule: a value that is empty or is not a number answers 0, and every caller tests for 0.

Parameters:

Returns: Long. The stored number, or 0.


Cell Range Resolution

VarRange #

var-range

Resolve the main cell range for the current variable

Signature:

Private Property Get VarRange() As Range

Computes the target cell from the column index dictionary value and the layer-specific start position. For HList the cell is in a column at HLIST_START_ROW + 1; for VList it is in a row at VLIST_START_COL. The result is cached until WriteVariable resets it for the next variable.

Returns: Range. The main cell range, or Nothing when the index is missing.


VarPrintRange #

var-print-range

Resolve the printed companion cell range (HList only)

Signature:

Private Property Get VarPrintRange() As Range

Returns the cell on the printed companion worksheet that corresponds to the current variable. Uses the same column index as VarRange but targets the printWksh. Returns Nothing when no printed worksheet is attached.

Returns: Range. The printed companion cell range, or Nothing.


VarCRFRange #

var-crf-range

Resolve the CRF companion cell range (HList only)

Signature:

Private Property Get VarCRFRange() As Range

Returns the cell on the CRF companion worksheet at the row specified by the "crf index" dictionary value. Returns Nothing when no CRF worksheet is attached or when the crf index is zero or missing.

Returns: Range. The CRF companion cell range, or Nothing.


Sheet Dispatch

WriteMainSheet #

write-main-sheet

Dispatch all writing steps for the main worksheet

Signature:

Private Sub WriteMainSheet()

Orchestrates the sequence of writing operations on the main worksheet: label, formula, type formatting, choices, validation, and conditional formatting. The exact set of conditional formatting steps depends on the active layer (HList applies geo, standard, and unique formatting; VList applies standard formatting only).


WritePrintSheet #

write-print-sheet

Write variable label to the printed companion sheet (HList only)

Signature:

Private Sub WritePrintSheet()

Delegates to AddLabel with usePrinted=True so that the label is written to the printed companion worksheet instead of the main sheet. Exits silently when no printed worksheet is attached.


WriteCRFSheet #

write-crf-sheet

Write variable data to the CRF companion sheet (HList only)

Signature:

Private Sub WriteCRFSheet()

Resolves the CRF cell range and delegates to AddCRFLabel, AddCRFType, and AddCRFChoices. Exits silently when no CRF worksheet is attached or when the variable has no CRF index.


Label Writing

AddLabel #

add-label

Write variable label, sublabel, name, notes, and formatting

Signature:

Private Sub AddLabel(Optional ByVal usePrinted As Boolean = False)

Writes the main label, sublabel, variable name, notes, control info, and design formatting to the target cell range. For HList layers this includes header cells, named ranges for table anchors, comments for notes, hidden control rows, geo-specific formatting, editable labels, and column hiding. For VList layers the layout is row-based with adjacent cells for control info and label formatting.

Parameters:


Type Formatting

AddType #

add-type

Apply Excel number format based on variable type and format

Signature:

Private Sub AddType()

Reads the variable type (text, date, decimal, integer) and optional format string from the dictionary, then sets the NumberFormat property on the main cell range. Supports built-in formats such as round, percentage, euros, and dollars as well as custom format strings. A numeric variable whose format reads text keeps the number format it already has: Excel refuses an empty NumberFormat, and this method sits outside every suppression in WriteMainSheet, so assigning one used to stop the sheet.


Formula Application

AddFormula #

add-formula

Parse and apply a formula to the variable cell

Signature:

Private Sub AddFormula()

Handles formula, case_when, and choice_formula control types. Parses the control details string through the Formulas class, validates the result, and writes the Excel formula to the cell.

A formula the parser rejects leaves the cell alone. The calculated-cell styling and the lock both sit inside the same guard, so a cell that reads as read-only is read-only, and the reason the parser gave is filed through Checking. Before this the styling was applied whatever the parser answered, while AddLabel had already unlocked the cell -- so the user saw a greyed calculated cell they could type into and the value went into the export.


NeverPrinted #

never-printed

Whether a control has nothing to give on a printed sheet

Signature:

Private Function NeverPrinted(ByVal controlValue As String) As Boolean

A calculated column reads cells that nobody fills in on paper, and an automatic list holds only values that have already been typed somewhere else. Neither belongs on a register book, whatever the register book column says about it, and the same four names decide the CRF: LLdictionary.AddVisibility writes always hidden into crf status for them, and ShowHide.IsLockedStatus holds them hidden on both layers once a linelist is running. The three lists have to be changed together.

Parameters:

Returns: Boolean. True when the column is hidden on a printed sheet.


Choice Setup

AddChoices #

add-choices

Set up dropdown validation for choice, list, and geo variables

Signature:

Private Sub AddChoices()

Handles choice_manual, choice_multiple, choice_custom, list_auto, and geo1 control types. For each type it populates a BetterArray of categories, registers them with the appropriate DropdownLists manager, and calls SetValidation to apply dropdown data validation to the cell. The choice_custom branch additionally creates a hyperlink from the label cell to the dropdown worksheet and adds a return link. The geo1 branch sets up admin1 through admin4 dropdown cascades.

The two dropdown managers are optional at creation, so a writer built without the one a control type needs files a checking and leaves the variable without its dropdown. The same holds for a geo variable in a linelist with no geography.


Data Validation

AddValidation #

add-validation

Add min/max data validation to the variable cell

Signature:

Private Sub AddValidation()

Reads min and max values from the dictionary, converts them to Excel validation formulas via the ExcelFormula helper, and applies whole-number, date, or decimal validation with the configured alert style. Both OS-local and standard formula variants are attempted, which works here because Validation.Add raises on a range that already carries one, so the first success sticks. Text variables are skipped.


Conditional Formatting

AddConditionalFormatting #

add-conditional-formatting

Add expression-based conditional formatting

Signature:

Private Sub AddConditionalFormatting()

Reads the formatting condition variable name from the dictionary, resolves its address, and adds an xlExpression format condition that highlights the cell when the condition variable equals 1. The interior colour, font colour, bold, and italic properties are copied from the formatting values cell in the dictionary.


AddUniqueConditionalFormatting #

add-unique-conditional-formatting

Add unique/duplicate conditional formatting (HList only)

Signature:

Private Sub AddUniqueConditionalFormatting()

When the dictionary marks a variable with unique=yes, adds a duplicate- values format condition that highlights duplicate entries with a red interior. This is typically used for patient IDs or other fields that must be unique across records.


AddGeoConditionalFormatting #

add-geo-conditional-formatting

Add geo pcode conditional formatting (HList only)

Signature:

Private Sub AddGeoConditionalFormatting()

For geo-type variables, adds a format condition that highlights the cell in orange when the corresponding pcode variable is empty but the geo variable itself is not. This alerts users that a geographic name was entered but the pcode lookup failed.


AddOneCondition #

add-one-condition

Give a cell one expression format condition

Signature:

Private Function AddOneCondition(ByVal cellRng As Range, _
                                 ByVal osFormula As String, _
                                 ByVal standardFormula As String) As Boolean

The OS-local formula is tried first and the standard one is used only when the first left the count where it was.

FormatConditions.Add appends whatever it is given, so the two calls this replaces left two conditions on every cell that took both formulas, and only the last of them was given a colour, SetFirstPriority and StopIfTrue. The unstyled one then travelled down the ListObject column as rows were added and into every exported copy. Validation.Add behaves the other way -- it raises on a range that already carries one -- which is why AddValidation keeps the two-call shape.

Parameters:

Returns: Boolean. True when the cell carries a new condition.


CRF Companion

AddCRFLabel #

add-crf-label

Write CRF label for the current variable

Signature:

Private Sub AddCRFLabel(ByVal crfRng As Range)

Writes the main label and sublabel to the CRF row, applies main-label and sublabel character formatting, and hides the row for formula-based or hidden variables. Also sets the table anchor named range on the first CRF row.

Parameters:


AddCRFType #

add-crf-type

Apply CRF-specific type formatting

Signature:

Private Sub AddCRFType(ByVal crfRng As Range)

Maps the variable type and format to a CRF design scope constant (such as CRFText, CRFDate, CRFDecimal) and applies the corresponding visual format to the CRF cell. Skips choice_manual variables because their formatting is handled by AddCRFChoices instead.

Parameters:


AddCRFChoices #

add-crf-choices

Write CRF choice headers and value cells

Signature:

Private Sub AddCRFChoices(ByVal crfRng As Range)

For choice_manual variables, retrieves the short-label categories from specs, writes optional column headers above the CRF row, and applies CRFChoiceValues formatting to each value cell. The headers are only written when the "crf choices" dictionary value is "yes".

Parameters:


Formula Helpers

ScratchRange #

scratch-range

The cell a formula is tried on

Signature:

Private Property Get ScratchRange() As Range

FailedFormula and OSFormula both need a cell to write a formula into and read back. It is A1 of the target worksheet, and both put back what they found.

WHY A1 AND NOT A CELL OUT OF THE WAY

A far cell looks safer and it is not: writing to the last column stretches the worksheet's used range across all 16,384 columns, and from then on every EntireColumn, every merge and every full-column read on that sheet works over that range. A sheet build that took a minute did not finish in nine. The cost is Excel's bookkeeping, so it does not show up in the code that pays it.

What was actually wrong with A1 was FailedFormula reaching it with .Clear, which takes the formatting as well as the contents. Both helpers save the formula and put it back now.

Returns: Range. The scratch cell.


FailedFormula #

failed-formula

Test if a formula fails when applied to a cell

Signature:

Private Function FailedFormula(ByVal frmName As String) As Boolean

Writes the formula to the scratch cell as a test. If the assignment raises an error the function returns True, indicating the formula is invalid. The cell contents are cleared after the test whatever the outcome, and Application.DisplayAlerts is suppressed for the duration.

Parameters:

Returns: Boolean. True if the formula raised an error, False otherwise.


ExcelFormula #

excel-formula

Convert a formula value to an Excel-compatible format for validation

Signature:

Private Function ExcelFormula(ByVal formVal As String, _
                              ByVal validationTypes As Long, _
                              Optional ByVal useOS As Boolean = False) As Variant

Parses the given formula string through the Formulas class, optionally converts it to an OS-local formula via OSFormula, and then converts the result to the type the validation asks for. A whole number takes CLng and a decimal takes CDbl: CInt overflowed above 32,767, which is a legitimate bound for a count or an ID, and CLng on a decimal turned a maximum of 0.5 into 0, so the validation rejected every value the dictionary meant to allow. Returns Empty when the formula is invalid, fails the FailedFormula test, or will not convert -- and the last of those is filed through Checking, because the caller runs under a suppression and would otherwise drop the validation with no message.

Parameters:

Returns: Variant. The converted formula value, or Empty if invalid.


ConvertedBound #

converted-bound

Read a bound as the type the validation asks for

Signature:

Private Function ConvertedBound(ByVal boundText As String, _
                                ByVal validationTypes As Long) As Variant

Answers Empty when the text will not convert, which is how the caller knows to hand the bound over as formula text instead. A whole number takes CLng and a decimal takes the separator-independent read: CInt overflowed above 32,767, which is a legitimate bound for a count or an ID, and CLng on a decimal turned a maximum of 0.5 into 0, so the validation rejected every value the dictionary meant to allow.

Parameters:

Returns: Variant. The converted bound, or Empty.


AsNumber #

as-number

Read a bound as a number whatever decimal separator it carries

Signature:

Private Function AsNumber(ByVal numberText As String) As Double

CDbl reads the separator of the host, and a dictionary is authored on whatever machine the user has. A bound typed as 0.5 on one and read back on a comma machine is text CDbl refuses, and a bound of 0,5 is text a dot machine refuses. The separator is taken from the host and both are mapped onto it.

Val was the other candidate and it answers 0 for 0,5, which is the same silent truncation this method exists to stop.

Parameters:

Returns: Double. The number.


OSFormula #

os-formula

Convert a formula to OS-specific local format

Signature:

Private Function OSFormula(ByVal formVal As String) As String

On macOS with VList layers, returns the formula as-is because FormulaLocal is unreliable. On Windows or for HList layers, temporarily writes the formula to the scratch cell, reads back FormulaLocal, and restores the previous cell content. This ensures validation formulas use the correct locale-aware separator characters.

The Mac carve-out covers VList alone and the HList branch calls FormulaLocal whatever the machine is. If FormulaLocal is unreliable on Mac for one layer it is unreliable for the other, and that is still open.

Parameters:

Returns: String. The OS-local formula string.


Error Handling

ThrowError #

throw-error

Raise an error with the class name as source

Signature:

Private Sub ThrowError(ByVal errNumber As Long, ByVal errorMessage As String)

Raises the number it is given with the class name as the source. Every guard in this class names a ProjectError member.

Parameters:


Checkings

LogCheck #

log-check

File one entry against the current variable

Signature:

Private Sub LogCheck(ByVal label As String, Optional ByVal scope As Byte = checkingWarning)

What internalChecks files is a problem: a variable it could not place, a formula the parser rejected, a validation bound that would not convert, a dropdown manager it was not given. That store reaches the __check worksheet through SectionBuilder and LLDataEntry.

THE MILESTONE STORE IS SEPARATE

milestoneChecks holds one entry per variable written, keyed by the variable name, and it travels to the run log's in-memory record and the text file alone. The two stores are kept apart because of what the worksheet costs: CheckingOutput ends every row it writes with an EntireColumn.AutoFit, so a 500-variable linelist put 500 autofits into the build when these entries went to the worksheet. That is why an earlier per-variable entry filed into internalChecks was removed; the record keeps the trace and the worksheet keeps the problems.

KEYS ARE UNIQUE ACROSS A BUILD

Checking.Add raises on a duplicate key. Every entry filed here is keyed by the worksheet and the variable, so a second subsection, a second sheet and a second writer can all be merged into one Checking. The counter this replaces started at 0 in every instance, so the second subsection of a sheet re-added key "0" and stopped the build.

Collaborator entries are re-keyed on the way in for the same reason: Formulas also keys with a counter from 0, and AddFormula builds a fresh Formulas per variable.

Parameters:


HarvestCollaboratorCheckings #

harvest-collaborator-checkings

Merge another object's entries into this writer's store

Signature:

Private Sub HarvestCollaboratorCheckings(ByVal collaboratorChecks As Checking)

The entries are re-keyed under this variable rather than appended, so two collaborators that both key from 0 do not collide. The severity is read back through the type label Checking prints, which is the only way it is exposed.

Parameters:


EnsureChecking #

ensure-checking

Build the internal store on first use

Signature:

Private Sub EnsureChecking()

LogMilestone #

log-milestone

File the milestone of the variable just written

Signature:

Private Sub LogMilestone()

Keyed by the variable name, which the dictionary keeps unique inside one sheet, and the store is per sheet because the writer is. A name that repeats all the same is numbered rather than raising, since Checking.Add refuses a duplicate key and a milestone must never stop a build.


EntryKeyRoot #

entry-key-root

The key an entry of the current variable starts from

Signature:

Private Function EntryKeyRoot() As String

Returns: String. Worksheet name, then the variable name.


UniqueKey #

unique-key

Answer a key the internal store does not hold yet

Signature:

Private Function UniqueKey(ByVal keyRoot As String) As String

A variable can file more than one entry -- a rejected formula and a validation bound that would not convert, for instance -- so the root is numbered when it is already taken.

Parameters:

Returns: String. A key that is free.


UniqueKeyIn #

unique-key-in

Answer a key one given store does not hold yet

Signature:

Private Function UniqueKeyIn(ByVal store As Checking, ByVal keyRoot As String) As String

The writer keeps two stores -- the problems and the milestones -- and each needs its own free key.

Parameters:

Returns: String. A key that is free.


ScopeOf #

scope-of

Read the severity of one entry of another checking

Signature:

Private Function ScopeOf(ByVal source As Checking, ByVal keyName As String) As Byte

Checking exposes a scope as the decorated type label it prints in the report, so the byte is recovered from that text. Each of the five labels carries its own word after the icon, and matching on the word keeps this free of the icon code points.

Parameters:

Returns: Byte. A CheckingScope member.


Used in (20 file(s))