From 6d082ef5b2e6bf551fbe73ce9f4511cced54dc44 Mon Sep 17 00:00:00 2001 From: Paul Date: Thu, 6 Aug 2026 18:25:03 +1200 Subject: [PATCH 1/4] Include source file and line number in build error messages. Reasonably accurate in "debug" mode, with the pre- and post-processor extension hooks disabled. Source location is lost when these hooks run in normal builds. --- .../build_standard_debug.bat | 3 + .../main/kotlin/org/sdpi/AsciidocConverter.kt | 15 ++- .../org/sdpi/ConvertAndVerifySupplement.kt | 4 + .../src/main/kotlin/org/sdpi/asciidoc/Util.kt | 15 +++ .../extension/AddQueryTablePlaceholder.kt | 6 +- .../extension/BibliographyCollector.kt | 10 +- .../ContentModuleIncludeProcessor.kt | 8 +- .../extension/DocumentAnchorCollector.kt | 2 +- .../sdpi/asciidoc/extension/PopulateTables.kt | 22 ++-- .../extension/ReferenceMacroProcessors.kt | 21 +-- .../extension/RequirementBlockProcessor2.kt | 8 +- .../extension/SdpiInformationCollector.kt | 122 ++++++++++-------- .../SupportUseCaseIncludeProcessor.kt | 15 ++- .../extension/TransactionActorsProcessor.kt | 4 +- .../extension/TransactionIncludeProcessor.kt | 3 +- .../extension/UseCaseIncludeProcessor.kt | 12 +- 16 files changed, 162 insertions(+), 108 deletions(-) create mode 100644 .ci/asciidoc-converter/build_standard_debug.bat diff --git a/.ci/asciidoc-converter/build_standard_debug.bat b/.ci/asciidoc-converter/build_standard_debug.bat new file mode 100644 index 00000000..ef05aee8 --- /dev/null +++ b/.ci/asciidoc-converter/build_standard_debug.bat @@ -0,0 +1,3 @@ +mkdir ..\..\sdpi-documents +mkdir ..\..\sdpi-documents\sdpi-standard +gradlew.bat run --args="--input-file ../../asciidoc/sdpi-standard.adoc --output-folder ../../sdpi-documents/sdpi-standard --backend html --debug" diff --git a/.ci/asciidoc-converter/src/main/kotlin/org/sdpi/AsciidocConverter.kt b/.ci/asciidoc-converter/src/main/kotlin/org/sdpi/AsciidocConverter.kt index 7f113053..453e8f37 100644 --- a/.ci/asciidoc-converter/src/main/kotlin/org/sdpi/AsciidocConverter.kt +++ b/.ci/asciidoc-converter/src/main/kotlin/org/sdpi/AsciidocConverter.kt @@ -42,6 +42,12 @@ class ConverterOptions( */ val generateTestOutput: Boolean = false, + /** + * When true, features implemented in pre- and post-processors + * are disabled so source file and line numbers are accurate. + */ + val debugMode: Boolean = false, + /** * Folder where extracts (requirements, use-cases, etc.) should * be placed. If null, the extracts won't be written. @@ -97,7 +103,6 @@ class AsciidocConverter( .sourcemap(true) .headerFooter(!conversionOptions.generateTestOutput) .toStream(outputFile).build() - val bEnablePrePostProcessing = true val asciidoctor = Asciidoctor.Factory.create() @@ -158,12 +163,16 @@ class AsciidocConverter( asciidoctor.javaExtensionRegistry().inlineMacro(TransactionReferenceMacroProcessor(infoCollector)) asciidoctor.javaExtensionRegistry().inlineMacro(ProfileReferenceMacroProcessor(infoCollector)) - if (bEnablePrePostProcessing) { + // Essential for document processing but breaks line number and source references, so + // we disable when debugging to simplify troubleshooting. + if (!conversionOptions.debugMode) { asciidoctor.javaExtensionRegistry().preprocessor(IssuesSectionPreprocessor(conversionOptions.githubToken)) asciidoctor.javaExtensionRegistry().preprocessor(DisableSectNumsProcessor()) } - if (bEnablePrePostProcessing) { + // Essential for document processing but breaks line number and source references, so + // we disable when debugging to simplify troubleshooting. + if (!conversionOptions.debugMode) { println("Enable pre post processing.") val referenceSanitizerPre = ReferenceSanitizerPreprocessor(anchorReplacements) asciidoctor.javaExtensionRegistry().preprocessor(referenceSanitizerPre) diff --git a/.ci/asciidoc-converter/src/main/kotlin/org/sdpi/ConvertAndVerifySupplement.kt b/.ci/asciidoc-converter/src/main/kotlin/org/sdpi/ConvertAndVerifySupplement.kt index d37d7622..2257a1c8 100644 --- a/.ci/asciidoc-converter/src/main/kotlin/org/sdpi/ConvertAndVerifySupplement.kt +++ b/.ci/asciidoc-converter/src/main/kotlin/org/sdpi/ConvertAndVerifySupplement.kt @@ -52,6 +52,9 @@ class ConvertAndVerifySupplement : CliktCommand("convert-supplement") { private val testGenerator by option("--test", help = "Writes document without headers for test output") .flag(default = false) + private val debugGenerator by option("--debug", help="Simplified processing for accurate source and line number diagnostics") + .flag(default = false) + override fun run() { runCatching { val asciidocErrorChecker = AsciidocErrorChecker() @@ -74,6 +77,7 @@ class ConvertAndVerifySupplement : CliktCommand("convert-supplement") { outputFormat = backend, dumpStructure = dumpStructure, generateTestOutput = testGenerator, + debugMode = debugGenerator, ) ) converter.run() diff --git a/.ci/asciidoc-converter/src/main/kotlin/org/sdpi/asciidoc/Util.kt b/.ci/asciidoc-converter/src/main/kotlin/org/sdpi/asciidoc/Util.kt index c40f4525..bbe7932d 100644 --- a/.ci/asciidoc-converter/src/main/kotlin/org/sdpi/asciidoc/Util.kt +++ b/.ci/asciidoc-converter/src/main/kotlin/org/sdpi/asciidoc/Util.kt @@ -2,6 +2,7 @@ package org.sdpi.asciidoc import org.apache.logging.log4j.kotlin.loggerOf import org.asciidoctor.ast.ContentNode +import org.asciidoctor.ast.Cursor import org.asciidoctor.ast.StructuralNode import org.sdpi.asciidoc.extension.Roles import org.sdpi.asciidoc.model.BlockOwner @@ -141,6 +142,20 @@ fun getLocation(block: StructuralNode): String { } } +fun findSourceLocation(node: ContentNode): String { + var current: ContentNode? = node + + while (current != null) { + if (current is StructuralNode) { + current.sourceLocation?.let { return it.toString() } + } + + current = current.parent + } + + return "Unknown" +} + fun getTitleFrom(block: StructuralNode): String { if (block.reftext != null) { return block.reftext diff --git a/.ci/asciidoc-converter/src/main/kotlin/org/sdpi/asciidoc/extension/AddQueryTablePlaceholder.kt b/.ci/asciidoc-converter/src/main/kotlin/org/sdpi/asciidoc/extension/AddQueryTablePlaceholder.kt index 6c35dd55..bee144e2 100644 --- a/.ci/asciidoc-converter/src/main/kotlin/org/sdpi/asciidoc/extension/AddQueryTablePlaceholder.kt +++ b/.ci/asciidoc-converter/src/main/kotlin/org/sdpi/asciidoc/extension/AddQueryTablePlaceholder.kt @@ -66,7 +66,7 @@ class AddTransactionQueryPlaceholder : BlockMacroProcessor(BLOCK_MACRO_NAME_TRAN // Add filter attributes to the table for the tree processor to consume. val strProfile = attributes[Roles.Profile.ID.key] checkNotNull(strProfile) { - logger.error("$BLOCK_MACRO_NAME_TRANSACTION_TABLE missing required attribute '${Roles.Profile.ID.key}'") + logger.error("${parent.sourceLocation} -> $BLOCK_MACRO_NAME_TRANSACTION_TABLE missing required attribute '${Roles.Profile.ID.key}'") } placeholderTable.attributes[Roles.Profile.ID.key] = strProfile @@ -98,7 +98,7 @@ class AddContentModuleQueryPlaceholder : BlockMacroProcessor(BLOCK_MACRO_NAME_CO // Add filter attributes to the table for the tree processor to consume. val strProfile = attributes[Roles.Profile.ID.key] checkNotNull(strProfile) { - logger.error("$BLOCK_MACRO_NAME_CONTENT_MODULE_TABLE missing required attribute '${Roles.Profile.ID.key}'") + logger.error("${parent.sourceLocation} -> $BLOCK_MACRO_NAME_CONTENT_MODULE_TABLE missing required attribute '${Roles.Profile.ID.key}'") } placeholderTable.attributes[Roles.Profile.ID.key] = strProfile @@ -125,7 +125,7 @@ class AddOidQueryPlaceholder : BlockMacroProcessor(BLOCK_MACRO_NAME_OID_TABLE) { // Add filter attributes to the table for the tree processor to consume. val strRootArcs = attributes[TableAttributes.OidTable.ROOT_ARC.key]?.toString() checkNotNull(strRootArcs) { - logger.error("$BLOCK_MACRO_NAME_OID_TABLE missing required attribute '${TableAttributes.OidTable.ROOT_ARC.key}'") + logger.error("${parent.sourceLocation} -> $BLOCK_MACRO_NAME_OID_TABLE missing required attribute '${TableAttributes.OidTable.ROOT_ARC.key}'") } placeholderTable.attributes[TableAttributes.OidTable.ROOT_ARC.key] = strRootArcs diff --git a/.ci/asciidoc-converter/src/main/kotlin/org/sdpi/asciidoc/extension/BibliographyCollector.kt b/.ci/asciidoc-converter/src/main/kotlin/org/sdpi/asciidoc/extension/BibliographyCollector.kt index 1deccb95..5aa7c951 100644 --- a/.ci/asciidoc-converter/src/main/kotlin/org/sdpi/asciidoc/extension/BibliographyCollector.kt +++ b/.ci/asciidoc-converter/src/main/kotlin/org/sdpi/asciidoc/extension/BibliographyCollector.kt @@ -44,26 +44,26 @@ class BibliographyCollector : Treeprocessor() { val mParsed = reBibParser.find(strItem) checkNotNull(mParsed) { - "${getLocation(bibEntry)} invalid format '$strItem'".also { logger.error { it } } + "${document.sourceLocation} -> ${getLocation(bibEntry)} invalid format '$strItem'".also { logger.error { it } } } val strRef = mParsed.groups["ref"]?.value checkNotNull(strRef) { - "${getLocation(bibEntry)} missing reference".also { logger.error { it } } + "${document.sourceLocation} -> ${getLocation(bibEntry)} missing reference".also { logger.error { it } } } val strRefText = mParsed.groups["reftxt"]?.value checkNotNull(strRefText) { - "${getLocation(bibEntry)} missing reference text for $strRef".also { logger.error { it } } + "${document.sourceLocation} -> ${getLocation(bibEntry)} missing reference text for $strRef".also { logger.error { it } } } val strSource = mParsed.groups["entry"]?.value checkNotNull(strSource) { - "${getLocation(bibEntry)} missing reference source for $strRef".also { logger.error { it } } + "${document.sourceLocation} -> ${getLocation(bibEntry)} missing reference source for $strRef".also { logger.error { it } } } if (bibliographyEntries.contains(strRef)) { - "${getLocation(bibEntry)} duplicate reference id $strRef".also { logger.error { it } } + "${document.sourceLocation} -> ${getLocation(bibEntry)} duplicate reference id $strRef".also { logger.error { it } } } bibliographyEntries[strRef] = BibliographyEntry(strRef, strRefText, strSource) } diff --git a/.ci/asciidoc-converter/src/main/kotlin/org/sdpi/asciidoc/extension/ContentModuleIncludeProcessor.kt b/.ci/asciidoc-converter/src/main/kotlin/org/sdpi/asciidoc/extension/ContentModuleIncludeProcessor.kt index 704e410e..a1d70bad 100644 --- a/.ci/asciidoc-converter/src/main/kotlin/org/sdpi/asciidoc/extension/ContentModuleIncludeProcessor.kt +++ b/.ci/asciidoc-converter/src/main/kotlin/org/sdpi/asciidoc/extension/ContentModuleIncludeProcessor.kt @@ -32,7 +32,7 @@ class ContentModuleIncludeProcessor : BlockMacroProcessor(BLOCK_MACRO_NAME_INCLU ): Any? { val (strProfileId, strProfileOptionId) = findProfileId(parent) checkNotNull(strProfileId) { - logger.error("$BLOCK_MACRO_NAME_INCLUDE_CONTENT_MODULE requires a ancestor block within the 'profile' role") + logger.error("${parent.sourceLocation} -> $BLOCK_MACRO_NAME_INCLUDE_CONTENT_MODULE requires a ancestor block within the 'profile' role") } val strActor = attributes[ContentModuleAttributes.ACTOR.key]?.toString() ?: findIdFromParent( @@ -41,16 +41,16 @@ class ContentModuleIncludeProcessor : BlockMacroProcessor(BLOCK_MACRO_NAME_INCLU ContentModuleAttributes.ACTOR.key ) checkNotNull(strActor) { - logger.error("$BLOCK_MACRO_NAME_INCLUDE_CONTENT_MODULE requires an ${ContentModuleAttributes.ACTOR.key} attribute or parent container") + logger.error("${parent.sourceLocation} -> $BLOCK_MACRO_NAME_INCLUDE_CONTENT_MODULE requires an ${ContentModuleAttributes.ACTOR.key} attribute or parent container") } val strObligation = attributes[ContentModuleAttributes.OBLIGATION.key]?.toString() checkNotNull(strObligation) { - logger.error("$BLOCK_MACRO_NAME_INCLUDE_CONTENT_MODULE requires an ${ContentModuleAttributes.OBLIGATION.key} attribute") + logger.error("${parent.sourceLocation} -> $BLOCK_MACRO_NAME_INCLUDE_CONTENT_MODULE requires an ${ContentModuleAttributes.OBLIGATION.key} attribute") } val obligation = parseObligation(strObligation) checkNotNull(obligation) { - logger.error("$BLOCK_MACRO_NAME_INCLUDE_CONTENT_MODULE requires valid ${ContentModuleAttributes.OBLIGATION.key} attribute") + logger.error("${parent.sourceLocation} -> $BLOCK_MACRO_NAME_INCLUDE_CONTENT_MODULE requires valid ${ContentModuleAttributes.OBLIGATION.key} attribute") } val strPlaceholderName = attributes[ContentModuleAttributes.PLACEHOLDER_NAME.key]?.toString() diff --git a/.ci/asciidoc-converter/src/main/kotlin/org/sdpi/asciidoc/extension/DocumentAnchorCollector.kt b/.ci/asciidoc-converter/src/main/kotlin/org/sdpi/asciidoc/extension/DocumentAnchorCollector.kt index fbdc33c8..c5a89398 100644 --- a/.ci/asciidoc-converter/src/main/kotlin/org/sdpi/asciidoc/extension/DocumentAnchorCollector.kt +++ b/.ci/asciidoc-converter/src/main/kotlin/org/sdpi/asciidoc/extension/DocumentAnchorCollector.kt @@ -48,7 +48,7 @@ class DocumentAnchorCollector : Treeprocessor() { if (strId != null) { if (knownAnchors.contains(strId)) { - logger.error("Found duplicate id $strId; ids should be unique.") + logger.error("${block.sourceLocation} -> Found duplicate id $strId; ids should be unique.") } knownAnchors[strId] = strRefText.toString() } diff --git a/.ci/asciidoc-converter/src/main/kotlin/org/sdpi/asciidoc/extension/PopulateTables.kt b/.ci/asciidoc-converter/src/main/kotlin/org/sdpi/asciidoc/extension/PopulateTables.kt index 85cb9555..98f70cf7 100644 --- a/.ci/asciidoc-converter/src/main/kotlin/org/sdpi/asciidoc/extension/PopulateTables.kt +++ b/.ci/asciidoc-converter/src/main/kotlin/org/sdpi/asciidoc/extension/PopulateTables.kt @@ -175,7 +175,7 @@ class PopulateTables(private val docInfo: SdpiInformationCollector, private val val strProfile = table.attributes[Roles.Profile.ID.key]?.toString() checkNotNull(strProfile) { - logger.error("Table missing required attribute '${Roles.Profile.ID.key}'") + logger.error("${table.sourceLocation} -> Table missing required attribute '${Roles.Profile.ID.key}'") } val strProfileOption = table.attributes[Roles.Profile.ID_PROFILE_OPTION.key]?.toString() @@ -188,7 +188,7 @@ class PopulateTables(private val docInfo: SdpiInformationCollector, private val val profile: SdpiProfile? = docInfo.getProfile(strProfile) checkNotNull(profile) { - logger.error("Unknown profile $strProfile") + logger.error("${table.sourceLocation} -> Unknown profile $strProfile") } val tableBuilder = TransactionTableBuilder(this, table, strActorId == null) @@ -197,7 +197,7 @@ class PopulateTables(private val docInfo: SdpiInformationCollector, private val if (strActorId != null) { val actor = profile.getActor(strActorId) checkNotNull(actor) { - logger.error("Actor $strActorId is not defined in profile $strProfile") + logger.error("${table.sourceLocation} -> Actor $strActorId is not defined in profile $strProfile") } addActorTransactions(tableBuilder, profile, actor, profileFilter) } else { @@ -220,7 +220,7 @@ class PopulateTables(private val docInfo: SdpiInformationCollector, private val val strTransactionId = transactionReference.transactionId val transaction: SdpiTransaction? = getTransaction(transactionReference) checkNotNull(transaction) { - logger.error("Unknown transaction id $strTransactionId") + logger.error("${tableBuilder.table.sourceLocation} -> Unknown transaction id $strTransactionId") } val obligationsForTransaction = @@ -300,7 +300,7 @@ class PopulateTables(private val docInfo: SdpiInformationCollector, private val val strRefId = ref.contentModuleId val module: SdpiContentModule? = getContentModule(ref) checkNotNull(module) { - logger.error("Unknown content-module id $strRefId") + logger.error("${tableBuilder.table.sourceLocation} -> Unknown content-module id $strRefId") } tableBuilder.addRow( @@ -318,7 +318,7 @@ class PopulateTables(private val docInfo: SdpiInformationCollector, private val val strRefId = ref.contentModuleId val module: SdpiContentModule? = getContentModule(ref) checkNotNull(module) { - logger.error("Unknown content-module id $strRefId") + logger.error("${tableBuilder.table.sourceLocation} -> Unknown content-module id $strRefId") } tableBuilder.addRow( @@ -355,7 +355,7 @@ class PopulateTables(private val docInfo: SdpiInformationCollector, private val val strRootArcs = table.attributes[TableAttributes.OidTable.ROOT_ARC.key]?.toString() checkNotNull(strRootArcs) { - logger.error("$BLOCK_MACRO_NAME_OID_TABLE missing required attribute '${TableAttributes.OidTable.ROOT_ARC.key}'") + logger.error("${table.sourceLocation} -> $BLOCK_MACRO_NAME_OID_TABLE missing required attribute '${TableAttributes.OidTable.ROOT_ARC.key}'") } val oidsToTable = mutableListOf() @@ -376,9 +376,9 @@ class PopulateTables(private val docInfo: SdpiInformationCollector, private val } else if (strArc == WellKnownOid.DEV_REQUIREMENT.id) { gatherRequirementOids(oidsToTable) } else if (strArc == "use-case-support") { - gatherUseCaseSupportOids(oidsToTable) + gatherUseCaseSupportOids(table, oidsToTable) } else { - logger.error("Oid tables don't support $strArc (yet?)") + logger.error("${table.sourceLocation} -> Oid tables don't support $strArc (yet?)") } } @@ -484,12 +484,12 @@ class PopulateTables(private val docInfo: SdpiInformationCollector, private val } } - private fun gatherUseCaseSupportOids(oidsToTable: MutableList) { + private fun gatherUseCaseSupportOids(table:Table, oidsToTable: MutableList) { for (profile in docInfo.profiles()) { for(support in profile.useCaseSupport) { val useCase = docInfo.useCases()[support.useCaseId] checkNotNull(useCase) { - logger.error("Unknown use case `${support.useCaseId}` supported by profile `${profile.profileId}`") + logger.error("${table.sourceLocation} -> Unknown use case `${support.useCaseId}` supported by profile `${profile.profileId}`") } for(strOid in support.oid) { val oid = SdpiOidReference( diff --git a/.ci/asciidoc-converter/src/main/kotlin/org/sdpi/asciidoc/extension/ReferenceMacroProcessors.kt b/.ci/asciidoc-converter/src/main/kotlin/org/sdpi/asciidoc/extension/ReferenceMacroProcessors.kt index 2915f45e..3b3f0abc 100644 --- a/.ci/asciidoc-converter/src/main/kotlin/org/sdpi/asciidoc/extension/ReferenceMacroProcessors.kt +++ b/.ci/asciidoc-converter/src/main/kotlin/org/sdpi/asciidoc/extension/ReferenceMacroProcessors.kt @@ -7,6 +7,7 @@ import org.asciidoctor.extension.InlineMacroProcessor import org.asciidoctor.extension.Name import org.sdpi.asciidoc.ExternalStandardAttributes import org.sdpi.asciidoc.LinkStyles +import org.sdpi.asciidoc.findSourceLocation import org.sdpi.asciidoc.parseRequirementNumber /** @@ -34,12 +35,12 @@ class RequirementReferenceMacroProcessor(private val documentInfo: SdpiInformati // Reference to a requirement defined locally in the supplement val nRequirementId = parseRequirementNumber(strTarget) checkNotNull(nRequirementId) { - "$strTarget is not a valid requirement number for a requirement reference".also { logger.error { it } } + "${findSourceLocation(parent)} -> $strTarget is not a valid requirement number for a requirement reference".also { logger.error { it } } } val req = documentInfo.requirements()[nRequirementId] checkNotNull(req) { - "Requirement '$strTarget' ($nRequirementId) doesn't exist".also { logger.error { it } } + "${findSourceLocation(parent)} -> Requirement '$strTarget' ($nRequirementId) doesn't exist".also { logger.error { it } } } val strHref = "#${req.getBlockId()}" @@ -51,17 +52,17 @@ class RequirementReferenceMacroProcessor(private val documentInfo: SdpiInformati // Referencing a requirement defined in an included standard. val standard = externalStandardsProcessor.getStandard(strStandardId) checkNotNull(standard) { - "Standard '$strStandardId' doesn't exist".also { logger.error { it } } + "${findSourceLocation(parent)} -> Standard '$strStandardId' doesn't exist".also { logger.error { it } } } val citation = bibliographyCollector.findEntry(standard.citationKey) checkNotNull(citation) { - "Bibliography doesn't included a reference for key '${standard.citationKey}'".also { logger.error { it } } + "${findSourceLocation(parent)} -> Bibliography doesn't included a reference for key '${standard.citationKey}'".also { logger.error { it } } } val req = standard.getRequirement(strTarget) checkNotNull(req) { - "Standard '$strStandardId' doesn't include requirement '$strTarget'".also { logger.error { it } } + "${findSourceLocation(parent)} -> Standard '$strStandardId' doesn't include requirement '$strTarget'".also { logger.error { it } } } val strLinkText = "$strTarget in [${citation.referenceText}]" @@ -90,7 +91,7 @@ class UseCaseReferenceMacroProcessor(private val documentInfo: SdpiInformationCo override fun process(parent: ContentNode, strTarget: String, attributes: MutableMap): PhraseNode { val useCase = documentInfo.useCases()[strTarget] checkNotNull(useCase) { - "Use case '$strTarget' doesn't exist".also { logger.error { it } } + "${findSourceLocation(parent)} -> Use case '$strTarget' doesn't exist".also { logger.error { it } } } val strHref = "#${useCase.anchor}" @@ -110,7 +111,7 @@ class ActorReferenceMacroProcessor(private val documentInfo: SdpiInformationColl override fun process(parent: ContentNode, strTarget: String, attributes: MutableMap): PhraseNode { val actor = documentInfo.findActor(strTarget) checkNotNull(actor) { - "Actor '$strTarget' doesn't exist".also { logger.error { it } } + "${findSourceLocation(parent)} -> Actor '$strTarget' doesn't exist".also { logger.error { it } } } val strHref = "#${actor.anchor}" @@ -131,7 +132,7 @@ class ContentModuleReferenceMacroProcessor(private val documentInfo: SdpiInforma override fun process(parent: ContentNode, strTarget: String, attributes: MutableMap): PhraseNode { val contentModule = documentInfo.contentModules()[strTarget] checkNotNull(contentModule) { - "Content module '$strTarget' doesn't exist".also { logger.error { it } } + "${findSourceLocation(parent)} -> Content module '$strTarget' doesn't exist".also { logger.error { it } } } val strHref = "#${contentModule.anchor}" @@ -150,7 +151,7 @@ class TransactionReferenceMacroProcessor(private val documentInfo: SdpiInformati override fun process(parent: ContentNode, strTarget: String, attributes: MutableMap): PhraseNode { val transaction = documentInfo.transactions()[strTarget] checkNotNull(transaction) { - "Transaction '$strTarget' doesn't exist".also { logger.error { it } } + "${findSourceLocation(parent)} -> Transaction '$strTarget' doesn't exist".also { logger.error { it } } } val strHref = "#${transaction.anchor}" @@ -170,7 +171,7 @@ class ProfileReferenceMacroProcessor(private val documentInfo: SdpiInformationCo override fun process(parent: ContentNode, strTarget: String, attributes: MutableMap): PhraseNode { val profile = documentInfo.getProfile(strTarget) checkNotNull(profile) { - "Profile '$strTarget' doesn't exist".also { logger.error { it } } + "${findSourceLocation(parent)} -> Profile '$strTarget' doesn't exist".also { logger.error { it } } } val strOption = attributes[Roles.Profile.ID_PROFILE_OPTION.key]?.toString() diff --git a/.ci/asciidoc-converter/src/main/kotlin/org/sdpi/asciidoc/extension/RequirementBlockProcessor2.kt b/.ci/asciidoc-converter/src/main/kotlin/org/sdpi/asciidoc/extension/RequirementBlockProcessor2.kt index 8ace4a6f..d23adb75 100644 --- a/.ci/asciidoc-converter/src/main/kotlin/org/sdpi/asciidoc/extension/RequirementBlockProcessor2.kt +++ b/.ci/asciidoc-converter/src/main/kotlin/org/sdpi/asciidoc/extension/RequirementBlockProcessor2.kt @@ -68,7 +68,7 @@ class RequirementBlockProcessor2 : BlockProcessor(BLOCK_NAME_SDPI_REQUIREMENT) { } override fun process(parent: StructuralNode, reader: Reader, attributes: MutableMap): Any { - val requirementNumber: Int = getRequirementNumber(attributes) + val requirementNumber: Int = getRequirementNumber(parent, attributes) val strGlobalId = getRequirementOid(requirementNumber) val strLinkId = String.format("r%04d", requirementNumber) @@ -106,7 +106,7 @@ class RequirementBlockProcessor2 : BlockProcessor(BLOCK_NAME_SDPI_REQUIREMENT) { * Requirement numbers must match the format defined by REQUIREMENT_NUMBER_FORMAT * or REQUIREMENT_TITLE_FORMAT for the id or title, respectively. */ - private fun getRequirementNumber(mutableAttributes: MutableMap): Int { + private fun getRequirementNumber(parent: StructuralNode, mutableAttributes: MutableMap): Int { val strTitle = mutableAttributes["title"] val nTitleRequirementNumber = REQUIREMENT_TITLE_FORMAT.findAll(strTitle.toString()) .map { it.groupValues[2] }.toList().first().toInt() @@ -114,13 +114,13 @@ class RequirementBlockProcessor2 : BlockProcessor(BLOCK_NAME_SDPI_REQUIREMENT) { // Check the id, if present, matches. val strId = mutableAttributes["id"] checkNotNull(strId) { - "Requirement '$strTitle' does not have a matching id".also { logger.error { it } } + "${parent.sourceLocation} -> Requirement '$strTitle' does not have a matching id".also { logger.error { it } } } val nId = parseRequirementNumber(strId.toString()) check(nId == nTitleRequirementNumber) { - "Requirement '$strTitle' does not have a matching id ($nId)".also { logger.error { it } } + "${parent.sourceLocation} -> Requirement '$strTitle' does not have a matching id ($nId)".also { logger.error { it } } } return nTitleRequirementNumber diff --git a/.ci/asciidoc-converter/src/main/kotlin/org/sdpi/asciidoc/extension/SdpiInformationCollector.kt b/.ci/asciidoc-converter/src/main/kotlin/org/sdpi/asciidoc/extension/SdpiInformationCollector.kt index bbf0886f..34b77aa9 100644 --- a/.ci/asciidoc-converter/src/main/kotlin/org/sdpi/asciidoc/extension/SdpiInformationCollector.kt +++ b/.ci/asciidoc-converter/src/main/kotlin/org/sdpi/asciidoc/extension/SdpiInformationCollector.kt @@ -1,6 +1,7 @@ package org.sdpi.asciidoc.extension import org.apache.logging.log4j.kotlin.Logging +import org.apache.logging.log4j.kotlin.logger import org.asciidoctor.ast.ContentNode import org.asciidoctor.ast.Document import org.asciidoctor.ast.StructuralNode @@ -202,7 +203,8 @@ class SdpiInformationCollector( val match = reTitle.find(strDocTitle) val strTitle = match?.groups?.get(2)?.value checkNotNull(strTitle) { - logger.error("Profile option title '$strDocTitle' is not formatted correctly") + "${block.sourceLocation} -> Profile option title '$strDocTitle' is not formatted correctly" + .also{logger.error{it}} } return strTitle } @@ -241,7 +243,8 @@ class SdpiInformationCollector( val strAnchor = block.id val strId = block.attributes[Roles.Actor.ID.key]?.toString() checkNotNull(strId) { - logger.error("Block with ${Roles.Actor.SECTION_ROLE.key} role requires an ${Roles.Actor.ID.key}") + "${block.sourceLocation} -> Block with ${Roles.Actor.SECTION_ROLE.key} role requires an ${Roles.Actor.ID.key}" + .also{logger.error{it}} } @@ -252,12 +255,13 @@ class SdpiInformationCollector( val mrTitleElements = reExtractTitleElements.find(strLabel) val strTitle = mrTitleElements?.groups?.get(2)?.value ?: strLabel checkNotNull(strTitle) { - logger.error("No label for actor $strId") + "${block.sourceLocation} -> No label for actor $strId" + .also{logger.error{it}} } check(!actors.contains(strId)) // check for duplicate. { - "Duplicate actor #${strId} ($strTitle)".also { + "${block.sourceLocation} -> Duplicate actor #${strId} ($strTitle)".also { logger.error { it } } } @@ -281,7 +285,7 @@ class SdpiInformationCollector( for (strGroup in astrGroupings) { val match = ActorGrouping.GROUPING_REGEX.matchEntire(strGroup) checkNotNull(match) { - "Invalid actor grouping for $strContext".also { + "${block.sourceLocation} -> Invalid actor grouping for $strContext".also { logger.error{it} } } @@ -299,7 +303,8 @@ class SdpiInformationCollector( val strAnchor = block.id val strId = block.attributes[Roles.Actor.OPTION_ID.key]?.toString() checkNotNull(strId) { - logger.error("Block with ${Roles.Actor.OPTION.key} role requires an ${Roles.Actor.OPTION_ID.key}") + "${block.sourceLocation} -> Block with ${Roles.Actor.OPTION.key} role requires an ${Roles.Actor.OPTION_ID.key}" + .also{logger.error{it}} } val strTitle = getTitleFrom(block) @@ -315,16 +320,19 @@ class SdpiInformationCollector( private fun processActorAlias(block: StructuralNode) { val strId = block.attributes[Roles.Actor.ID.key]?.toString() checkNotNull(strId) { - logger.error("Block with ${Roles.Actor.ALIAS.key} role requires an ${Roles.Actor.ID.key}") + "${block.sourceLocation} -> Block with ${Roles.Actor.ALIAS.key} role requires an ${Roles.Actor.ID.key}" + .also{logger.error{it}} } val strAlias = block.id checkNotNull(strAlias) { - logger.error("block with ${Roles.Actor.ALIAS.key} role requires an id") + "${block.sourceLocation} -> block with ${Roles.Actor.ALIAS.key} role requires an id" + .also{logger.error{it}} } check(!actorAliases.containsKey(strAlias)) { - logger.error("Alias $strAlias already exists") + "${block.sourceLocation} -> Alias $strAlias already exists" + .also{logger.error{it}} } logger.info("Found actor alias: $strAlias ==> $strId") @@ -340,7 +348,8 @@ class SdpiInformationCollector( for (strActorId in actorIds) { val actor = findActor(strActorId) checkNotNull(actor) { - logger.error("Requirement ${req.value.localId} contains unknown actor $strActorId") + "Requirement ${req.value.localId} contains unknown actor $strActorId" + .also { logger.error{it}} } actor.requirements.add(nRequirementId) } @@ -350,7 +359,8 @@ class SdpiInformationCollector( private fun processUseCaseSupport(block: StructuralNode, profile: SdpiProfile) { val strUseCaseId = block.attributes[Roles.UseCaseSupport.USE_CASE_ID.key]?.toString() checkNotNull(strUseCaseId) { - logger.error("Use case in profile ${profile.profileId} requires ${Roles.UseCaseSupport.USE_CASE_ID.key} attribute") + "${block.sourceLocation} -> Use case in profile ${profile.profileId} requires ${Roles.UseCaseSupport.USE_CASE_ID.key} attribute" + .also{logger.error{it}} } val strAnchor = block.id val parentOids = profile.oids.map{ "$it.12" } @@ -374,7 +384,8 @@ class SdpiInformationCollector( if (strOptionId != null) { val profileOption = currentProfile.findOption(strOptionId) checkNotNull(profileOption) { - logger.error("Profile ${currentProfile.profileId} does not have an option $strOptionId for transactions") + "Profile ${currentProfile.profileId} does not have an option $strOptionId for transactions" + .also{logger.error{it}} } go.value.forEach { profileOption.add(it.transactionReference) } } @@ -393,7 +404,8 @@ class SdpiInformationCollector( if (strOptionId != null) { val profileOption = currentProfile.findOption(strOptionId) checkNotNull(profileOption) { - logger.error("Profile ${currentProfile.profileId} does not have an option $strOptionId for content modules") + "Profile ${currentProfile.profileId} does not have an option $strOptionId for content modules" + .also{logger.error{it}} } go.value.forEach { profileOption.add(it.ref) } } @@ -404,7 +416,8 @@ class SdpiInformationCollector( private fun processProfileOption(block: StructuralNode, currentProfile: SdpiProfile): SdpiProfileOption { val strId = block.attributes["profile-option-id"]?.toString() checkNotNull(strId) { - logger.error("Block with role 'profile-option' requires a 'profile-option-id") + "${block.sourceLocation} -> Block with role 'profile-option' requires a 'profile-option-id" + .also{logger.error{it}} } val existingOption = currentProfile.options.firstOrNull { it.id == strId } @@ -427,17 +440,19 @@ class SdpiInformationCollector( private fun processContentModule(block: StructuralNode) { val strContentModuleId = block.attributes[Roles.ContentModule.ID.key]?.toString() checkNotNull(strContentModuleId) { - logger.error("Content module block (id=${block.id} missing '${Roles.ContentModule.ID.key}'") + "${block.sourceLocation} -> Content module block (id=${block.id} missing '${Roles.ContentModule.ID.key}'" + .also{logger.error{it}} } check(!contentModules.containsKey(strContentModuleId)) { - logger.error("Duplicate content module id found: $strContentModuleId") + "${block.sourceLocation} -> Duplicate content module id found: $strContentModuleId" + .also{logger.error{it}} } val strLabel = parseContentModuleTitle(block.title) val strAnchor = block.id - val oids = getOids(block, "Content module $strContentModuleId", WellKnownOid.DEV_CONTENT_MODULE) + val oids = getOids(block, "${block.sourceLocation} -> Content module $strContentModuleId", WellKnownOid.DEV_CONTENT_MODULE) contentModules[strContentModuleId] = SdpiContentModule(strContentModuleId, oids, strLabel, strAnchor) } @@ -447,7 +462,8 @@ class SdpiInformationCollector( val match = reTitle.find(strDocText) val strTitle = match?.groups?.get(2)?.value checkNotNull(strTitle) { - logger.error("Content module title '$strDocText' is not formatted correctly") + "Content module title '$strDocText' is not formatted correctly" + .also{logger.error{it}} } return strTitle } @@ -459,7 +475,7 @@ class SdpiInformationCollector( check(!requirements.contains(nRequirementNumber)) // check for duplicate. { val strRequirement = block.attributes["requirement-number"].toString() - "Duplicate requirement #${strRequirement}: ${block.sourceLocation.path}:${block.sourceLocation.lineNumber}".also { + "${block.sourceLocation} -> Duplicate requirement #${strRequirement}".also { logger.error { it } } } @@ -541,7 +557,7 @@ class SdpiInformationCollector( OwningContext.USE_CASE -> block.attributes[UseCaseAttributes.ID.key]?.toString() } checkNotNull(strId) { - logger.error("Owner missing id") + logger.error("${findSourceLocation(block)} -> Owner missing id") } //println("Found owner of requirement: $ownerType = $strId") return RequirementContext(ownerType, strId) @@ -585,7 +601,7 @@ class SdpiInformationCollector( } else -> { - logger.error("Un-styled content in requirement #${nRequirementNumber}.") + logger.error("${block.sourceLocation} -> Un-styled content in requirement #${nRequirementNumber}.") throw IllegalStateException() //unStyledContent.addAll(getContent_Obj(child)) } @@ -668,7 +684,7 @@ class SdpiInformationCollector( val useCaseHeader: ContentNode = getUseCaseNode(nRequirementNumber, block.parent) val useCaseId = useCaseHeader.attributes[RequirementAttributes.UseCase.ID.key] checkNotNull(useCaseId) { - "Can't find use case id for requirement #${nRequirementNumber}".also { + "${block.sourceLocation} -> Can't find use case id for requirement #${nRequirementNumber}".also { logger.error { it } } } @@ -688,7 +704,7 @@ class SdpiInformationCollector( ): SdpiRequirement2 { val strStandardId = block.attributes[RequirementAttributes.RefIcs.ID.key]?.toString() checkNotNull(strStandardId) { - "Missing standard id for requirement #${nRequirementNumber}".also { logger.error(it) } + "${block.sourceLocation} -> Missing standard id for requirement #${nRequirementNumber}".also { logger.error(it) } } val section = block.attributes[RequirementAttributes.RefIcs.SECTION.key] @@ -704,7 +720,7 @@ class SdpiInformationCollector( val bibEntry = bibliography.findEntry(strStandardId) checkNotNull(bibEntry) { - "${getLocation(block)} bibliography entry for $strStandardId is missing".also { logger.error { it } } + "${block.sourceLocation} -> bibliography entry for $strStandardId is missing".also { logger.error { it } } } val strRefSource = bibEntry.source @@ -726,24 +742,24 @@ class SdpiInformationCollector( ): SdpiRequirement2 { val sesType = block.attributes[RequirementAttributes.RiskMitigation.SES_TYPE.key] checkNotNull(sesType) { - "Missing ses type for requirement #${nRequirementNumber}".also { logger.error(it) } + "${block.sourceLocation} -> Missing ses type for requirement #${nRequirementNumber}".also { logger.error(it) } } val strSesType = sesType.toString() val parsedSesType = RiskMitigationType.entries.firstOrNull { it.keyword == strSesType } checkNotNull(parsedSesType) { - "Invalid ses type ($strSesType) for requirement #${nRequirementNumber}".also { logger.error(it) } + "${block.sourceLocation} -> Invalid ses type ($strSesType) for requirement #${nRequirementNumber}".also { logger.error(it) } } val testability = block.attributes[RequirementAttributes.RiskMitigation.TESTABILITY.key] checkNotNull(testability) { - "Missing test type for requirement #${nRequirementNumber}".also { logger.error(it) } + "${block.sourceLocation} -> Missing test type for requirement #${nRequirementNumber}".also { logger.error(it) } } val strTest = testability.toString() val parsedTestability = RiskMitigationTestability.entries.firstOrNull { it.keyword == strTest } checkNotNull(parsedTestability) { - "Invalid test type ($strTest) for requirement #${nRequirementNumber}".also { logger.error(it) } + "${block.sourceLocation} -> Invalid test type ($strTest) for requirement #${nRequirementNumber}".also { logger.error(it) } } return SdpiRequirement2.RiskMitigation( @@ -761,7 +777,7 @@ class SdpiInformationCollector( specification: RequirementSpecification ): SdpiRequirement2 { check(false) { - "Currently unsupported".also { logger.error(it) } + "${block.sourceLocation} -> Currently unsupported".also { logger.error(it) } } return SdpiRequirement2.TechFeature( @@ -918,19 +934,13 @@ class SdpiInformationCollector( } checkNotNull(strType) { - ("Missing ${RequirementAttributes.Common.TYPE.key} attribute for SDPi requirement #$requirementNumber [${ - getLocation( - block - ) - }]").also { - logger.error { it } - } + "${block.sourceLocation} -> Missing ${RequirementAttributes.Common.TYPE.key} attribute for SDPi requirement #$requirementNumber}" + .also { logger.error { it } } } val reqType = RequirementType.entries.firstOrNull { it.keyword == strType } checkNotNull(reqType) { - ("Invalid requirement type '${strType}' for SDPi requirement #$requirementNumber [${getLocation(block)}]").also { - logger.error { it } - } + "${block.sourceLocation} -> Invalid requirement type '${strType}' for SDPi requirement #$requirementNumber" + .also { logger.error { it } } } return reqType @@ -949,7 +959,7 @@ class SdpiInformationCollector( } checkNotNull(node) { - "Can't find use case in parents for requirement #${requirementNumber}".also { + "${findSourceLocation(parent)} -> Can't find use case in parents for requirement #${requirementNumber}".also { logger.error { it } } } @@ -968,7 +978,7 @@ class SdpiInformationCollector( val specBlocks: MutableList = mutableListOf() gatherUseCaseBlocks(block, specBlocks) - val useCaseOids = getOids(block, "Use case $strUseCaseId", WellKnownOid.DEV_USE_CASE_GLOBAL) + val useCaseOids = getOids(block, "${block.sourceLocation} -> Use case $strUseCaseId", WellKnownOid.DEV_USE_CASE_GLOBAL) val backgroundContent: MutableList = mutableListOf() val scenarios: MutableList = mutableListOf() @@ -983,15 +993,15 @@ class SdpiInformationCollector( val oTitle = useCaseBlock.attributes["sdpi_scenario"] checkNotNull(oTitle) { - "${getLocation(useCaseBlock)} missing required scenario title".also { logger.error { it } } + "${useCaseBlock.sourceLocation} missing required scenario title".also { logger.error { it } } } - val scenarioOids = getOids(useCaseBlock, "Use case $strUseCaseId scenario ${oTitle.toString()}", useCaseOids) + val scenarioOids = getOids(useCaseBlock, "${block.sourceLocation} -> Use case $strUseCaseId scenario ${oTitle.toString()}", useCaseOids) val iStepBlock = iBlock + 1 check(iStepBlock < specBlocks.count() && specBlocks[iStepBlock].hasRole(Roles.UseCase.STEPS.key)) { - "${getLocation(useCaseBlock)} missing steps for scenario $oTitle".also { logger.error { it } } + "${useCaseBlock.sourceLocation} missing steps for scenario $oTitle".also { logger.error { it } } } val stepBlock = specBlocks[iStepBlock] val scenarioSteps = getSteps(stepBlock) @@ -1016,7 +1026,8 @@ class SdpiInformationCollector( val match = reTitle.find(strDocTitle) val strTitle = match?.groups?.get(1)?.value checkNotNull(strTitle) { - logger.error("Use case title '$strDocTitle' is not formatted correctly") + "${block.sourceLocation} -> Use case title '$strDocTitle' is not formatted correctly" + .also{logger.error{it}} } return strTitle @@ -1045,31 +1056,31 @@ class SdpiInformationCollector( for (child in block.blocks) { check(child is org.asciidoctor.ast.Block) { - "${getLocation(child)} steps must be paragraphs".also { logger.error { it } } + "${child.sourceLocation} steps must be paragraphs".also { logger.error { it } } } for (strLine in child.lines) { val mType = reType.find(strLine) checkNotNull(mType) { - "${getLocation(child)} step invalid format".also { logger.error { it } } + "${child.sourceLocation} step invalid format".also { logger.error { it } } } val oType = mType.groups["type"]?.value checkNotNull(oType) { - "${getLocation(child)} step missing type".also { logger.error { it } } + "${child.sourceLocation} step missing type".also { logger.error { it } } } val oDescription = mType.groups["description"]?.value checkNotNull(oDescription) { - "${getLocation(child)} step missing description".also { logger.error { it } } + "${child.sourceLocation} step missing description".also { logger.error { it } } } val stepType = resolveStepType(oType.toString()) checkNotNull(stepType) { - "${getLocation(child)} invalid step type".also { logger.error { it } } + "${child.sourceLocation} invalid step type".also { logger.error { it } } } steps.add(GherkinStep(stepType, oDescription.toString())) @@ -1090,7 +1101,7 @@ class SdpiInformationCollector( val mrTitleElements = reExtractTitleElements.find(strLabel) checkNotNull(mrTitleElements) { - "Can't get title and transaction id from $strLabel".also { + "${block.sourceLocation} -> Can't get title and transaction id from $strLabel".also { logger.error { it } } } @@ -1098,7 +1109,8 @@ class SdpiInformationCollector( val strTransactionId = block.attributes[Roles.Transaction.TRANSACTION_ID.key]?.toString() checkNotNull(strTransactionId) { - logger.error("Transaction id on block $strTitle is required") + "${block.sourceLocation} -> Transaction id on block $strTitle is required" + .also{logger.error{it}} } val strDefaultLeaf = getDefaultTransactionOid(strTransactionId) @@ -1107,7 +1119,7 @@ class SdpiInformationCollector( check(!transactions.contains(strTransactionId)) // check for duplicate. { - "Duplicate transaction #${strTransactionId} ($strLabel)".also { + "${block.sourceLocation} -> Duplicate transaction #${strTransactionId} ($strLabel)".also { logger.error { it } } } @@ -1187,7 +1199,7 @@ class SdpiInformationCollector( ): List { val strLeafArcs = block.attributes[BlockAttribute.LEAF_ARC.key]?.toString() ?: strDefaultLeaf checkNotNull(strLeafArcs) { - logger.error("$strContext requires an ${BlockAttribute.LEAF_ARC.key}") + "${block.sourceLocation} -> $strContext requires an ${BlockAttribute.LEAF_ARC.key}".also{logger.error(it)} } val blockOids = mutableListOf() @@ -1208,7 +1220,7 @@ class SdpiInformationCollector( ): List { val strLeafArcs = block.attributes[BlockAttribute.LEAF_ARC.key]?.toString() checkNotNull(strLeafArcs) { - logger.error("$strContext requires an ${BlockAttribute.LEAF_ARC.key}") + "${block.sourceLocation} -> $strContext requires an ${BlockAttribute.LEAF_ARC.key}".also{logger.error(it)} } val blockOids = mutableListOf() diff --git a/.ci/asciidoc-converter/src/main/kotlin/org/sdpi/asciidoc/extension/SupportUseCaseIncludeProcessor.kt b/.ci/asciidoc-converter/src/main/kotlin/org/sdpi/asciidoc/extension/SupportUseCaseIncludeProcessor.kt index 8224340e..4879d0d7 100644 --- a/.ci/asciidoc-converter/src/main/kotlin/org/sdpi/asciidoc/extension/SupportUseCaseIncludeProcessor.kt +++ b/.ci/asciidoc-converter/src/main/kotlin/org/sdpi/asciidoc/extension/SupportUseCaseIncludeProcessor.kt @@ -32,23 +32,28 @@ class SupportUseCaseIncludeProcessor : BlockMacroProcessor(BLOCK_MACRO_NAME_SUPP override fun process(parent: StructuralNode, strActorId: String, attributes: MutableMap): Any? { val (strProfileId, strProfileOptionId, strUseCaseId) = findParentContext(parent) checkNotNull(strProfileId) { - logger.error("$BLOCK_MACRO_NAME_SUPPORT_USE_CASE requires a ancestor block with the 'profile' role") + "${parent.sourceLocation} -> $BLOCK_MACRO_NAME_SUPPORT_USE_CASE requires a ancestor block with the 'profile' role" + .also{logger.error{it}} } checkNotNull(strUseCaseId) { - logger.error("$BLOCK_MACRO_NAME_SUPPORT_USE_CASE requires a ancestor block with the '${Roles.UseCaseSupport.SECTION_ROLE.key}' role") + "${parent.sourceLocation} -> $BLOCK_MACRO_NAME_SUPPORT_USE_CASE requires a ancestor block with the '${Roles.UseCaseSupport.SECTION_ROLE.key}' role" + .also{logger.error{it}} } check(strActorId.isNotEmpty()) { - logger.error("$BLOCK_MACRO_NAME_SUPPORT_USE_CASE requires an actor target") + "${parent.sourceLocation} -> $BLOCK_MACRO_NAME_SUPPORT_USE_CASE requires an actor target" + .also{logger.error{it}} } val strObligation = attributes[Roles.UseCaseSupport.OBLIGATION.key]?.toString() checkNotNull(strObligation) { - logger.error("$BLOCK_MACRO_NAME_SUPPORT_USE_CASE requires an ${Roles.UseCaseSupport.OBLIGATION.key} attribute") + "${parent.sourceLocation} -> $BLOCK_MACRO_NAME_SUPPORT_USE_CASE requires an ${Roles.UseCaseSupport.OBLIGATION.key} attribute" + .also{logger.error{it}} } val obligation = parseObligation(strObligation) checkNotNull(obligation) { - logger.error("$BLOCK_MACRO_NAME_SUPPORT_USE_CASE requires valid ${UseCaseAttributes.OBLIGATION.key} attribute") + "${parent.sourceLocation} -> $BLOCK_MACRO_NAME_SUPPORT_USE_CASE requires valid ${UseCaseAttributes.OBLIGATION.key} attribute" + .also{logger.error{it}} } diff --git a/.ci/asciidoc-converter/src/main/kotlin/org/sdpi/asciidoc/extension/TransactionActorsProcessor.kt b/.ci/asciidoc-converter/src/main/kotlin/org/sdpi/asciidoc/extension/TransactionActorsProcessor.kt index 808cf9f8..48bf500e 100644 --- a/.ci/asciidoc-converter/src/main/kotlin/org/sdpi/asciidoc/extension/TransactionActorsProcessor.kt +++ b/.ci/asciidoc-converter/src/main/kotlin/org/sdpi/asciidoc/extension/TransactionActorsProcessor.kt @@ -37,10 +37,10 @@ class TransactionActorsProcessor : BlockProcessor(BLOCK_NAME_TRANSACTION_ACTORS) override fun process(parent: StructuralNode, reader: Reader, attributes: MutableMap): Any? { val strTransactionId = findIdFromParent(parent, Roles.Transaction.TRANSACTION.key, Roles.Transaction.TRANSACTION_ID.key) checkNotNull(strTransactionId) { - logger.error("Missing ${Roles.Transaction.TRANSACTION_ID.key} on section with role of ${Roles.Transaction.TRANSACTION.key}") + logger.error("${parent.sourceLocation} -> Missing ${Roles.Transaction.TRANSACTION_ID.key} on section with role of ${Roles.Transaction.TRANSACTION.key}") } if (transactionActors.containsKey(strTransactionId)) { - logger.error("Actors for transaction $strTransactionId have already been defined.") + logger.error("${parent.sourceLocation} -> Actors for transaction $strTransactionId have already been defined.") } logger.info("Processing actors for transaction $strTransactionId. ") diff --git a/.ci/asciidoc-converter/src/main/kotlin/org/sdpi/asciidoc/extension/TransactionIncludeProcessor.kt b/.ci/asciidoc-converter/src/main/kotlin/org/sdpi/asciidoc/extension/TransactionIncludeProcessor.kt index 2c8548d0..ebf46b03 100644 --- a/.ci/asciidoc-converter/src/main/kotlin/org/sdpi/asciidoc/extension/TransactionIncludeProcessor.kt +++ b/.ci/asciidoc-converter/src/main/kotlin/org/sdpi/asciidoc/extension/TransactionIncludeProcessor.kt @@ -30,7 +30,8 @@ class TransactionIncludeProcessor : BlockMacroProcessor(BLOCK_MACRO_NAME_INCLUDE override fun process(parent: StructuralNode, strTransactionId: String, attributes: MutableMap): Any? { val (strProfileId, strProfileOptionId, strActorOptionId) = findContextId(parent) checkNotNull(strProfileId) { - logger.error("$BLOCK_MACRO_NAME_INCLUDE_TRANSACTION requires a ancestor block within the 'profile' role") + "${parent.sourceLocation} -> $BLOCK_MACRO_NAME_INCLUDE_TRANSACTION requires a ancestor block within the 'profile' role" + .also{logger.error{it}} } val strPlaceholderName = attributes[TransactionIncludeAttributes.PLACEHOLDER_NAME.key]?.toString() diff --git a/.ci/asciidoc-converter/src/main/kotlin/org/sdpi/asciidoc/extension/UseCaseIncludeProcessor.kt b/.ci/asciidoc-converter/src/main/kotlin/org/sdpi/asciidoc/extension/UseCaseIncludeProcessor.kt index 9b28295b..614ab6fc 100644 --- a/.ci/asciidoc-converter/src/main/kotlin/org/sdpi/asciidoc/extension/UseCaseIncludeProcessor.kt +++ b/.ci/asciidoc-converter/src/main/kotlin/org/sdpi/asciidoc/extension/UseCaseIncludeProcessor.kt @@ -29,7 +29,8 @@ class UseCaseIncludeProcessor : BlockMacroProcessor(BLOCK_MACRO_NAME_INCLUDE_USE override fun process(parent: StructuralNode, strUseCaseId: String, attributes: MutableMap): Any? { val (strProfileId, strProfileOptionId) = findProfileId(parent) checkNotNull(strProfileId) { - logger.error("$BLOCK_MACRO_NAME_INCLUDE_USE_CASE requires a ancestor block within the 'profile' role") + "${parent.sourceLocation} -> $BLOCK_MACRO_NAME_INCLUDE_USE_CASE requires a ancestor block within the 'profile' role" + .also{logger.error{it}} } val strActor = attributes[UseCaseAttributes.ACTOR.key]?.toString() ?: findIdFromParent( parent, @@ -37,16 +38,19 @@ class UseCaseIncludeProcessor : BlockMacroProcessor(BLOCK_MACRO_NAME_INCLUDE_USE UseCaseAttributes.ACTOR.key ) checkNotNull(strActor) { - logger.error("$BLOCK_MACRO_NAME_INCLUDE_USE_CASE requires an ${UseCaseAttributes.ACTOR.key} attribute or parent container") + "${parent.sourceLocation} -> $BLOCK_MACRO_NAME_INCLUDE_USE_CASE requires an ${UseCaseAttributes.ACTOR.key} attribute or parent container" + .also{logger.error{it}} } val strObligation = attributes[UseCaseAttributes.OBLIGATION.key]?.toString() checkNotNull(strObligation) { - logger.error("$BLOCK_MACRO_NAME_INCLUDE_USE_CASE requires an ${UseCaseAttributes.OBLIGATION.key} attribute") + "${parent.sourceLocation} -> $BLOCK_MACRO_NAME_INCLUDE_USE_CASE requires an ${UseCaseAttributes.OBLIGATION.key} attribute" + .also{logger.error{it}} } val obligation = parseObligation(strObligation) checkNotNull(obligation) { - logger.error("$BLOCK_MACRO_NAME_INCLUDE_USE_CASE requires valid ${UseCaseAttributes.OBLIGATION.key} attribute") + "${parent.sourceLocation} -> $BLOCK_MACRO_NAME_INCLUDE_USE_CASE requires valid ${UseCaseAttributes.OBLIGATION.key} attribute" + .also{logger.error{it}} } From c21e31c2499fb9ed221abd3d821533f1d4a634b3 Mon Sep 17 00:00:00 2001 From: Paul Date: Thu, 6 Aug 2026 20:49:56 +1200 Subject: [PATCH 2/4] Added source location to requirement matching etc. --- .../org/sdpi/asciidoc/extension/SdpiInformationCollector.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.ci/asciidoc-converter/src/main/kotlin/org/sdpi/asciidoc/extension/SdpiInformationCollector.kt b/.ci/asciidoc-converter/src/main/kotlin/org/sdpi/asciidoc/extension/SdpiInformationCollector.kt index 34b77aa9..47892293 100644 --- a/.ci/asciidoc-converter/src/main/kotlin/org/sdpi/asciidoc/extension/SdpiInformationCollector.kt +++ b/.ci/asciidoc-converter/src/main/kotlin/org/sdpi/asciidoc/extension/SdpiInformationCollector.kt @@ -144,7 +144,7 @@ class SdpiInformationCollector( } check(!profiles.containsKey(strProfileId)) { - logger.error("Duplicate profile id found: $strProfileId") + logger.error("${block.sourceLocation} -> Duplicate profile id found: $strProfileId") } val oids = getOids(block, "Profile $strProfileId", WellKnownOid.DEV_PROFILE) @@ -249,7 +249,7 @@ class SdpiInformationCollector( val strLabel = block.reftext ?: block.title - logger.info("Found actor $strId => $strLabel") + logger.info("${block.sourceLocation} -> Found actor $strId => $strLabel") val reExtractTitleElements = Regex("""^\d+([.:]\d+)*\s+(.*)""") val mrTitleElements = reExtractTitleElements.find(strLabel) @@ -335,7 +335,7 @@ class SdpiInformationCollector( .also{logger.error{it}} } - logger.info("Found actor alias: $strAlias ==> $strId") + logger.info("${block.sourceLocation} -> Found actor alias: $strAlias ==> $strId") actorAliases[strAlias] = strId } From bbdec494f66e95d4c9cc8cd6f8f6f0158108fd0d Mon Sep 17 00:00:00 2001 From: Paul Date: Thu, 6 Aug 2026 20:56:47 +1200 Subject: [PATCH 3/4] Added some documentation. --- articles/sdpi-article-ihe-tf-asciidoc-cookbook.adoc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/articles/sdpi-article-ihe-tf-asciidoc-cookbook.adoc b/articles/sdpi-article-ihe-tf-asciidoc-cookbook.adoc index 79e9ef65..9a8b90d1 100644 --- a/articles/sdpi-article-ihe-tf-asciidoc-cookbook.adoc +++ b/articles/sdpi-article-ihe-tf-asciidoc-cookbook.adoc @@ -1173,7 +1173,10 @@ The AsciiDoc source is automatically processed using GitHub actions. For testing, documents can be generated using Windows command line tools (probably for other operating systems too, but I don't know about that; please expand on this when you figure out how). To create output from the AsciiDoc source open a command prompt in the `.ci\asciidoc-converter` folder and use: * `build_document.bat` to create an html document (`sdpi-standard.html`), -* `build_document_pdf.bat` to create an Acrobat PDF document (`sdpi-standard.pdf`). +* `build_document_pdf.bat` to create an Acrobat PDF document (`sdpi-standard.pdf`), +* `build_standard_debug.bat` to create an html document (`sdpi-standard.html`) with reasonably accurate source location in the trace + output at the expense of document processing features (notably: section renumber, reference sanatizer/ anchor replacement is + disabled). This is intendes _solely_ for tracking down build issues in local builds. The output created is not complete. The output files are written to `sdpi-standard` folder. Extracts (requirements and use-cases in JSON format) are written to the `sdpi-standard\referenced-artifacts` folder. Extracts are generated along with the document output. From a3673f1f5fca1b1a57cb41e4561b9a1777947dd0 Mon Sep 17 00:00:00 2001 From: Paul Date: Thu, 6 Aug 2026 21:23:44 +1200 Subject: [PATCH 4/4] Generate debug build in separate folder Added change log entry. --- .ci/asciidoc-converter/build_standard_debug.bat | 4 ++-- CHANGELOG.md | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.ci/asciidoc-converter/build_standard_debug.bat b/.ci/asciidoc-converter/build_standard_debug.bat index ef05aee8..66a34f89 100644 --- a/.ci/asciidoc-converter/build_standard_debug.bat +++ b/.ci/asciidoc-converter/build_standard_debug.bat @@ -1,3 +1,3 @@ mkdir ..\..\sdpi-documents -mkdir ..\..\sdpi-documents\sdpi-standard -gradlew.bat run --args="--input-file ../../asciidoc/sdpi-standard.adoc --output-folder ../../sdpi-documents/sdpi-standard --backend html --debug" +mkdir ..\..\sdpi-documents\sdpi-standard-debug +gradlew.bat run --args="--input-file ../../asciidoc/sdpi-standard.adoc --output-folder ../../sdpi-documents/sdpi-standard-debug --backend html --debug" diff --git a/CHANGELOG.md b/CHANGELOG.md index f5b66b5b..cc060cdd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,8 @@ Each section shall contain a list of action items of the following format: `