LinelistSpecs
Modern facade that wraps a specifications workbook and exposes lazy-loaded, cached accessors for every collaborator object (dictionary, choices, translations, geo, analysis, passwords, format, exports, formulas). Provides lifecycle orchestration methods for importing data from external setup workbooks, preparing the dictionary with preserved sheet names, exporting specifications to a destination linelist workbook, and surfacing user-facing error dialogs. Uses a PredeclaredId factory pattern so callers construct instances via LinelistSpecs.Create(workbook) and receive an ILinelistSpecs interface reference.
Depends on: ILLdictionary, ILLChoices, ILLTranslation, IDesignerTranslation, ILLGeo, ILLExport, IAnalysis, IPasswords, ILLFormat, ILinelistBuildService, ITemporaryRepos, TemporaryRepos, IFormulaData, ILLVariables, IFormulas, ITranslationObject, IApplicationState, ApplicationState, BetterArray, LLdictionary, LLChoices, LLTranslation, DesignerTranslation, LLGeo, LLExport, Analysis, Passwords, LLFormat, FormulaData, LLVariables, Formulas
Version: 1.1 (2026-02-16)
Factory helpers
Create #
create
Create a new specification facade from a workbook
Signature:
Public Function Create(ByVal specsWorkbook As Workbook) As ILinelistSpecs
Construction and identity accessors. Validates the workbook structure upfront (checking for all required worksheets and named ranges), then constructs a new LinelistSpecs instance with caches initialised. Returns the ILinelistSpecs interface.
Parameters:
specsWorkbook: Workbook. The specifications workbook to wrap.
Returns: ILinelistSpecs. A fully initialised facade ready for use.
Throws:
- ProjectError.ObjectNotInitialized When specsWorkbook is Nothing.
- ProjectError.ElementNotFound When required worksheets are missing.
DesWorkbook #
des-workbook
Designer workbook backing all specification queries
Signature:
Public Property Get DesWorkbook() As Workbook
Returns the designer/specifications workbook reference. Raises an error if the workbook has not been assigned via the factory method.
Returns: Workbook. The designer workbook.
Throws:
- ProjectError.ObjectNotInitialized When the workbook is not set.
State management
ResetCaches #
reset-caches
Reset every cached collaborator
Signature:
Public Sub ResetCaches()
Cache invalidation for collaborator objects, translations, and categories. Clears all cached collaborator objects, the translation scope cache, and the category cache. Call after importing new data so subsequent reads query the workbook again.
Category helpers
Categories #
categories
Resolve category values for a variable with caching
Signature:
Public Function Categories(ByVal varName As String, Optional ByVal useShortlabels As Boolean = False) As BetterArray
Category resolution and caching for choice-type variables. Returns the list of category values for a choice-type variable (choice_manual, choice_multiple, choice_custom, or choice_formula). Results are cached using parallel key/value BetterArrays for efficient repeated access. Always returns a cloned BetterArray to prevent callers from modifying the cached copy.
Parameters:
varName: String. The variable name whose categories to resolve.useShortlabels: Boolean. When True, returns short labels. Defaults to False.
Returns: BetterArray. Cloned list of category values.
Throws:
- ProjectError.InvalidArgument When varName is empty.
- ProjectError.ElementNotFound When the variable has no selectable categories.
Core accessors
Dictionary #
dictionary
Linelist dictionary
Signature:
Public Property Get Dictionary() As ILLdictionary
Public property accessors for each collaborator object, exposing the lazy-loaded instances through their respective interfaces. Returns the ILLdictionary instance providing column and row access to the Dictionary worksheet. Lazy-loaded on first access.
Returns: ILLdictionary. The dictionary collaborator.
Choices #
choices
Choices catalogue
Signature:
Public Property Get Choices() As ILLChoices
Returns the ILLChoices instance providing category lookups from the Choices worksheet. Lazy-loaded on first access.
Returns: ILLChoices. The choices collaborator.
DesignFormat #
design-format
Visual formatting specifications
Signature:
Public Property Get DesignFormat() As ILLFormat
Returns the ILLFormat instance providing sheet and cell formatting methods based on the __formatter worksheet. Lazy-loaded on first access.
Returns: ILLFormat. The format collaborator.
AnalysisObject #
analysis
Analysis setup specifications
Signature:
Public Property Get AnalysisObject() As IAnalysis
Returns the IAnalysis instance providing access to the Analysis worksheet configuration. Lazy-loaded on first access.
Returns: IAnalysis. The analysis collaborator.
ExportObject #
export-object
Export column specifications
Signature:
Public Property Get ExportObject() As ILLExport
Returns the ILLExport instance providing access to export column definitions from the Exports worksheet. Lazy-loaded on first access.
Returns: ILLExport. The export collaborator.
Password #
password
Password manager
Signature:
Public Property Get Password() As IPasswords
Returns the IPasswords instance providing workbook and sheet protection utilities from the __pass worksheet. Lazy-loaded on first access.
Returns: IPasswords. The password collaborator.
GeoObject #
geo-object
Geographic data manager
Signature:
Public Property Get GeoObject() As ILLGeo
Returns the ILLGeo instance providing geographic hierarchy lookups from the Geo worksheet. Lazy-loaded on first access.
Returns: ILLGeo. The geo collaborator.
FormulaDataObject #
formula-data-object
Formula data provider
Signature:
Public Property Get FormulaDataObject() As IFormulaData
Returns the IFormulaData instance providing formula parsing support from the __formula worksheet. Lazy-loaded on first access.
Returns: IFormulaData. The formula data collaborator.
HasTemplate #
has-template
Whether the workbook uses a ribbon template
Signature:
Public Property Get HasTemplate() As Boolean
Reads the RNG_MainTemp named range on the Main worksheet and returns True when the value equals "has template".
Returns: Boolean. True when a template is configured.
Translations #
translations
Translation tables manager
Signature:
Public Property Get Translations() As ILLTranslation
Returns the ILLTranslation instance providing access to the linelist translation worksheet. Lazy-loaded on first access.
Returns: ILLTranslation. The translations collaborator.
DesignerTranslations #
designer-translations
Designer translation accessor
Signature:
Public Property Get DesignerTranslations() As IDesignerTranslation
Returns the IDesignerTranslation instance providing designer UI translations from the DesignerTranslation worksheet. Lazy-loaded on first access.
Returns: IDesignerTranslation. The designer translation collaborator.
TransObject #
trans-object
Scoped translation object
Signature:
Public Property Get TransObject(Optional ByVal translationScope As Byte = TranslationOfMessages) As ITranslationObject
Returns an ITranslationObject for the requested translation scope. Results are cached per scope using direct fields for each TranslationScope value (0-4), avoiding generic container overhead.
Parameters:
translationScope: Byte. The translation scope constant (e.g., TranslationOfMessages, TranslationOfDictionary). Defaults to TranslationOfMessages.
Returns: ITranslationObject. The scoped translation object.
Throws:
- ProjectError.ElementNotFound When the scope is not available.
TemporarySheetName #
temporary-sheet-name
Temporary sheet name for a given scope
Signature:
Public Property Get TemporarySheetName(ByVal scope As Byte) As String
Returns the fixed worksheet name used for temporary internal sheets, mapped from the TempSheetScope enum (TempSheetAnalysis, TempSheetList, etc.) to its canonical name string.
Parameters:
scope: Byte. A TempSheetScope enum value.
Returns: String. The worksheet name string.
Throws:
- ProjectError.InvalidArgument When the scope is not recognised.
Value #
value
Configuration value by tag name
Signature:
Public Property Get Value(ByVal tagName As String) As String
Returns a configuration value from the Main worksheet for the given tag name. The special tag "numberofexports" returns the export count from the Exports worksheet; all other tags resolve the named range directly on the Main worksheet.
Parameters:
tagName: String. The configuration key to look up.
Returns: String. The configuration value, or empty string if tagName is empty.
Workbook orchestration
Prepare #
prepare
Prepare all specifications using a build service
Signature:
Public Sub Prepare(ByVal buildService As ILinelistBuildService, ByVal setupPath As String)
Lifecycle methods for importing, preparing, exporting, and error-managing the specifications workbook. Orchestrates the full specification preparation workflow:
- Creates a temporary repository for working files.
- Creates a new output workbook (from template if configured).
- Exports pass-through components directly from setup to linelist.
- Exports designer-sourced components (geo, passwords, translations, formatter, language HiddenNames) to the linelist.
- Builds domain manager instances on the linelist workbook.
- Retrieves exported domain instances from the service.
- Prepares the dictionary with preserved sheet names and geo references.
- Adds the "list auto" column if not already present.
- Translates and sorts all collaborators using dictionary translations. Handles errors with cleanup and busy-state restoration.
Parameters:
buildService: ILinelistBuildService. The configured build service.setupPath: String. Full path to the setup workbook.
Throws:
- ProjectError.ObjectNotInitialized When buildService is Nothing.
- ProjectError.ErrorUnexpectedState When an unexpected error occurs.
LLWorkbook #
ll-workbook
The output linelist workbook created during Prepare
Signature:
Public Property Get LLWorkbook() As Workbook
Returns the linelist workbook created by Prepare that received exported data from the import service. Returns Nothing before Prepare is called.
Returns: Workbook. The linelist workbook, or Nothing.
OutputTemporaryRepos #
temporary-repos
The temporary repository created during Prepare
Signature:
Public Property Get OutputTemporaryRepos() As ITemporaryRepos
Returns the ITemporaryRepos instance created by Prepare for working files. Returns Nothing before Prepare is called.
Returns: ITemporaryRepos. The temporary repository, or Nothing.
ErrorManage #
error-manage
Display an error dialog for specification failures
Signature:
Public Sub ErrorManage(Optional ByVal textMessage As String = vbNullString)
Shows a message box describing the error and informing the user that the linelist creation process is being aborted.
Parameters:
textMessage: String. Error description. Defaults to vbNullString.
Internal members (not exported)
Factory helpers
Self #
self
Interface-cast self reference
Signature:
Public Property Get Self() As ILinelistSpecs
Returns the current instance cast as ILinelistSpecs for fluent construction patterns within the factory method.
Returns: ILinelistSpecs. This instance as its interface type.
InternalWorkbook #
internal-workbook
Setter for the backing workbook reference
Signature:
Public Property Set InternalWorkbook(ByVal specsWorkbook As Workbook)
Used during construction to assign the workbook before caches are initialised. Not part of the public interface.
State management
ResetObjectCaches #
reset-object-caches
Clear all collaborator object references
Signature:
Private Sub ResetObjectCaches()
ResetTranslationCache #
reset-translation-cache
Clear the translation scope cache
Signature:
Private Sub ResetTranslationCache()
ResetCategoryCache #
reset-category-cache
Clear the category list cache
Signature:
Private Sub ResetCategoryCache()
Guard helpers
EnsureWorkbook #
ensure-workbook
Verify that the backing workbook is assigned
Signature:
Private Sub EnsureWorkbook()
Lazy-initialisation guards for the workbook and category arrays.
Throws:
- ProjectError.ObjectNotInitialized When the workbook is Nothing.
EnsureCategoryArrays #
ensure-category-arrays
Create the parallel category arrays if they do not exist
Signature:
Private Sub EnsureCategoryArrays()
Worksheet resolution
ResolveSheet #
resolve-sheet
Resolve a worksheet by name with optional error surfacing
Signature:
Private Function ResolveSheet(ByVal sheetName As String, _
Optional ByVal allowMissing As Boolean = False) As Worksheet
Direct worksheet lookups by name within the specifications workbook. Looks up a worksheet in the specifications workbook by name. When the sheet is not found and allowMissing is False, raises a descriptive error.
Parameters:
sheetName: String. The worksheet name to find.allowMissing: Boolean. When True, returns Nothing instead of raising.
Returns: Worksheet. The resolved worksheet, or Nothing if allowMissing.
Throws:
- ProjectError.InvalidArgument When sheetName is empty and not allowMissing.
- ProjectError.ElementNotFound When the sheet is missing and not allowMissing.
External workbook helpers
EnterBusyState #
enter-busy-state
Transition the application into a busy state
Signature:
Private Sub EnterBusyState(ByRef busyScope As IApplicationState)
Utilities for opening, closing, and validating external workbooks during the Prepare workflow. Creates or refreshes an ApplicationState snapshot and applies the busy state (suppressing screen updates and calculation) if not already busy.
Parameters:
busyScope: IApplicationState (ByRef). The state object to manage.
ImportGeobase #
import-geobase
Import geobase data from the user-specified path
Signature:
Private Sub ImportGeobase(ByVal geoObj As ILLGeo)
Reads the geobase file path from the Main worksheet. If the path is not empty, opens the geobase workbook, sets the dictionary language on the exported geo worksheet via RNG_MetaLang, calls geoObj.Import to populate the output geo sheet, then closes the geobase workbook. Silently skips if the path is empty or the file cannot be opened.
Parameters:
geoObj: ILLGeo. The geo instance on the output workbook.dictLang: String. The dictionary language code for RNG_MetaLang.
ExitBusyState #
exit-busy-state
Restore the application from a busy state
Signature:
Private Sub ExitBusyState(ByRef busyScope As IApplicationState)
Parameters:
busyScope: IApplicationState (ByRef). The state object to restore and release.
BuildPreservedSheetNames #
build-preserved-sheet-names
Build the list of sheet names to preserve during dictionary preparation
Signature:
Private Function BuildPreservedSheetNames(ByVal messageTranslations As ITranslationObject) As BetterArray
Collects all specification sheet names and temporary sheet names into a BetterArray, then appends translated linelist sheet names (Admin, Analysis, Spatial, etc.) when message translations are available.
Parameters:
messageTranslations: ITranslationObject. The message translations for resolving linelist sheet names, or Nothing to skip translated names.
Returns: BetterArray. The list of preserved sheet names.
SafeTranslatedValue #
safe-translated-value
Look up a translation key, falling back to the key itself on failure
Signature:
Private Function SafeTranslatedValue(ByVal translations As ITranslationObject, ByVal key As String) As String
Parameters:
translations: ITranslationObject. The translation object to query.key: String. The translation key.
Returns: String. The translated value, or the key if translation fails.
ResetPostImportCaches #
reset-post-import-caches
Clear caches that become stale after importing new data
Signature:
Private Sub ResetPostImportCaches()
Resets the variables object, category cache, and translation cache so they are rebuilt from the freshly imported data.
Workbook validation
ValidateWorkbookReference #
validate-workbook-reference
Validate a workbook reference and its structure
Signature:
Private Sub ValidateWorkbookReference(ByVal specsWorkbook As Workbook)
Structural validation of specifications workbooks.
Throws:
- ProjectError.ObjectNotInitialized When specsWorkbook is Nothing.
- ProjectError.ElementNotFound When required sheets are missing.
ValidateWorkbookStructure #
validate-workbook-structure
Check that a workbook contains all required worksheets and named ranges
Signature:
Private Sub ValidateWorkbookStructure(ByVal specsWorkbook As Workbook)
Iterates the list of required sheet names and collects any that are missing. If the format sheet exists, also validates that the DESIGNTYPE named range is present.
Parameters:
specsWorkbook: Workbook. The workbook to validate.
Throws:
- ProjectError.ElementNotFound When required sheets or ranges are missing.
RequiredSheetNames #
required-sheet-names
Return the array of required designer worksheet names
Signature:
Private Function RequiredSheetNames() As Variant
Only includes sheets that LinelistSpecs reads directly from the designer workbook. Pass-through sheets (Dictionary, Choices, Analysis, Export) are excluded because the build service imports them from the setup file directly into the linelist.
Returns: Variant. Array of sheet name strings.
WorksheetExistsInternal #
worksheet-exists-internal
Test whether a worksheet exists in a workbook
Signature:
Private Function WorksheetExistsInternal(ByVal specsWorkbook As Workbook, _
ByVal sheetName As String) As Boolean
Parameters:
specsWorkbook: Workbook. The workbook to search.sheetName: String. The worksheet name to look for.
Returns: Boolean. True when the worksheet exists.
RangeExistsInternal #
range-exists-internal
Test whether a named range exists on a worksheet
Signature:
Private Function RangeExistsInternal(ByVal hostSheet As Worksheet, _
ByVal rangeName As String) As Boolean
Parameters:
hostSheet: Worksheet. The worksheet to search.rangeName: String. The named range to look for.
Returns: Boolean. True when the named range exists.
JoinNames #
join-names
Join a BetterArray of names into a comma-separated, quoted string
Signature:
Private Function JoinNames(ByVal values As BetterArray) As String
Parameters:
values: BetterArray. The names to join.
Returns: String. Formatted string like "'Sheet1', 'Sheet2'".
SafeWorkbookName #
safe-workbook-name
Safely retrieve a workbook's name, returning "
Signature:
Private Function SafeWorkbookName(ByVal specsWorkbook As Workbook) As String
Parameters:
specsWorkbook: Workbook. The workbook whose name to retrieve.
Returns: String. The workbook name or "
Collaborator ensures
EnsureExports #
ensure-exports
Lazy-load the export collaborator
Signature:
Private Sub EnsureExports()
Lazy-initialisation guards for each collaborator object. Each EnsureXxx method creates the collaborator from its backing worksheet on first access, then caches it for subsequent calls.
EnsureDictionary #
ensure-dictionary
Lazy-load the dictionary collaborator
Signature:
Private Sub EnsureDictionary()
Requires the export collaborator to be available first (for the number of exports parameter), then creates the dictionary from the Dictionary worksheet.
EnsureChoices #
ensure-choices
Lazy-load the choices collaborator
Signature:
Private Sub EnsureChoices()
EnsureTranslations #
ensure-translations
Lazy-load the translations collaborator
Signature:
Private Sub EnsureTranslations()
Creates the LLTranslation instance from the linelist translation worksheet.
EnsureDesignerTranslations #
ensure-designer-translations
Lazy-load the designer translations collaborator
Signature:
Private Sub EnsureDesignerTranslations()
EnsureGeo #
ensure-geo
Lazy-load the geo collaborator
Signature:
Private Sub EnsureGeo()
EnsureAnalysis #
ensure-analysis
Lazy-load the analysis collaborator
Signature:
Private Sub EnsureAnalysis()
EnsurePasswords #
ensure-passwords
Lazy-load the passwords collaborator
Signature:
Private Sub EnsurePasswords()
EnsureFormulaData #
ensure-formula-data
Lazy-load the formula data collaborator
Signature:
Private Sub EnsureFormulaData()
EnsureVariables #
ensure-variables
Lazy-load the variables collaborator
Signature:
Private Sub EnsureVariables()
Requires the dictionary to be available first, then creates the LLVariables instance from it.
EnsureFormat #
ensure-format
Lazy-load the format collaborator
Signature:
Private Sub EnsureFormat()
Resolves the design name from the DESIGNTYPE named range on the format sheet, then creates the LLFormat instance.
ResolveDesignName #
resolve-design-name
Extract the design name from the format worksheet's DESIGNTYPE range
Signature:
Private Function ResolveDesignName(ByVal formatSheet As Worksheet) As String
Parameters:
formatSheet: Worksheet. The format worksheet.
Returns: String. The trimmed design type name.
Throws:
- ProjectError.ElementNotFound When the DESIGNTYPE range is missing.
EnsureCategoryLookup #
ensure-category-lookup
Ensure choices, variables, and category arrays are ready
Signature:
Private Sub EnsureCategoryLookup()
NumberOfExports #
number-of-exports
Return the number of exports defined in the export specifications
Signature:
Private Function NumberOfExports() As Long
Returns: Long. The export count.
List auto helpers
AddListAuto #
add-list-auto
Populate the "list auto" dictionary column for list_auto variables
Signature:
Private Sub AddListAuto()
Automatic list column generation for formula-based variables. Adds a "list auto" column to the dictionary, then finds all variables with control type "list_auto" and marks their control details with "list_auto_origin". For formula-type variables (formula, case_when, choice_formula), recursively traces referenced variables and marks them as well.
RecursiveListAuto #
recursive-list-auto
Recursively mark referenced variables in formula chains as list_auto
Signature:
Private Sub RecursiveListAuto(ByVal varName As String)
Parses the control details of a formula-type variable using the Formulas engine, extracts the list of referenced variables, marks each with "list_auto_origin", and recurses into any that are themselves formulas or case_when types.
Parameters:
varName: String. The formula variable whose references to trace.
Category helpers
FindCategoryIndex #
find-category-index
Search the category keys array for a matching key
Signature:
Private Function FindCategoryIndex(ByVal cacheKey As String) As Long
Parameters:
cacheKey: String. The key to search for.
Returns: Long. The 1-based index if found, or 0 if not found.
ResolveCategoryList #
resolve-category-list
Build a category list from the choices sheet for a given variable
Signature:
Private Function ResolveCategoryList(ByVal varName As String, _
ByVal useShortlabels As Boolean) As BetterArray
Determines the category name from the variable's control type and control details, then queries the choices collaborator.
Parameters:
varName: String. The variable name.useShortlabels: Boolean. Whether to use short labels.
Returns: BetterArray. The resolved category list.
Throws:
- ProjectError.InvalidArgument When varName is empty.
- ProjectError.ElementNotFound When the variable or category is not found.
NormaliseControlType #
normalise-control-type
Strip parenthesised suffixes from a control type string
Signature:
Private Function NormaliseControlType(ByVal controlValue As String) As String
Parameters:
controlValue: String. The raw control type value.
Returns: String. The normalised, lower-cased control type.
ExtractChoiceFormula #
extract-choice-formula
Extract the choice name from a CHOICE_FORMULA(...) control details string
Signature:
Private Function ExtractChoiceFormula(ByVal controlDetails As String) As String
Parameters:
controlDetails: String. The raw control details value.
Returns: String. The extracted choice name.
Core accessors
ResolveTranslationScope #
resolve-translation-scope
Query the translations collaborator for a given scope
Signature:
Private Function ResolveTranslationScope(ByVal translationScope As Byte) As ITranslationObject
Parameters:
translationScope: Byte. The translation scope to resolve.
Returns: ITranslationObject. The resolved translation object.
Throws:
- ProjectError.ElementNotFound When the scope returns Nothing.
Error handling
ThrowError #
throw-error
Raise a project error with the class name as source
Signature:
Private Sub ThrowError(ByVal errNumber As Long, ByVal messageText As String)
Internal error-raising utility.
Parameters:
errNumber: Long. The ProjectError constant.messageText: String. The error description.
Test support
TestAssignDesignerTranslations #
test-assign-designer-translations
Inject a mock IDesignerTranslation instance for testing
Signature:
Public Sub TestAssignDesignerTranslations(ByVal designerTrads As IDesignerTranslation)
Backdoor setters for injecting mock collaborators during testing.
Parameters:
designerTrads: IDesignerTranslation. The mock designer translations object.
TestAssignTranslations #
test-assign-translations
Inject a mock ILLTranslation instance for testing
Signature:
Public Sub TestAssignTranslations(ByVal translations As ILLTranslation)
Also resets the translation cache to ensure the mock object is used for subsequent TransObject lookups.
Parameters:
translations: ILLTranslation. The mock translations object.
Interface implementation
ILinelistSpecs_ResetCaches #
Signature:
Private Sub ILinelistSpecs_ResetCaches()
ILinelistSpecs delegation stubs. Each member forwards to its corresponding public member on the concrete class. No @label tags are needed on delegation stubs.
Used in (18 file(s))
- AnalysisOutput.cls
- CrossTable.cls
- GraphSpecs.cls
- ILinelist.cls
- ILinelistSpecs.cls
- Linelist.cls
- LinelistSpecs.cls
- ListBuilder.cls
- SectionBuilder.cls
- VarWriter.cls
- DesignerMain.bas
- EventsDesignerAdvanced.bas
- GenerationReport.bas
- LinelistSpecsWorkbookStub.cls
- LinelistStub.cls
- LLVarContextSpecsStub.cls
- TableSpecsLinelistStub.cls
- TestLinelistSpecs.bas