Message accumulation¶
SAIF.Application offers two related but independent message-building mechanisms: AppManagerBase.AppendMessageLines(...), which grows a single manager-scoped Message string one line at a time while skipping duplicates, and the static MessageFormatting helpers, which turn an array of message strings into one concatenated string using a caller-chosen (or fixed) separator, also skipping duplicates but as a batch operation.
| Field | Value |
|---|---|
| Operation ID | OP-APP-04 |
| Trigger | A manager calls AppendMessageLines(...) on itself, or any caller invokes a MessageFormatting shared method with a message array |
| Inputs | A single message string, an array of message strings, or an IEnumerable(Of String) of messages; an optional concatenation string for GetMessagesUserDefined |
| Outputs | Updated AppManagerBase.Message string (instance path); a single formatted message string (static MessageFormatting path) |
| Primary components | AppManagerBase.AppendMessageLines (three overloads), AppManagerBase.Message, AppManagerBase.NewLineCharacter, MessageFormatting.GetMessagesForWindows, MessageFormatting.GetMessagesForWeb, MessageFormatting.GetMessagesUserDefined, MessageFormatting.BuildMessageString, MessageFormatting.AppendMessageString, MessageFormatting.RemoveReturns |
| Entry point | Application\SAIF.Application\AppManagerBase.vb; Application\SAIF.Application\MessageFormatting.vb |
Steps¶
Instance path: AppManagerBase.AppendMessageLines¶
- Normalize the input shape. The three protected overloads all converge on the single-string overload:
AppendMessageLines(inMessages As IEnumerable(Of String))callsAppendMessageLines(inMessages.ToArray).AppendMessageLines(inMessage() As String)loops the array and callsAppendMessageLines(AMessage)once per element.AppendMessageLines(inMessage As String)is the actual append logic (step 2).- Append with duplicate suppression. For a single message string, the method appends it to
Messageonly when both conditions hold: the string is non-empty (inMessage.Length > 0) and it is not already a substring of the currentMessage(Not Message.Contains(inMessage)) (BR-002). - If
Messagealready has content: the configurableNewLineCharacter(default<BR>) is appended first as a separator before the new text. - If
Messageis empty: the new text becomes the entireMessagewith no leading separator. - If the message is empty or already contained: the call is a no-op — nothing is appended and
Messageis unchanged. - Because the duplicate check is
Message.Contains(inMessage)rather than an exact per-line match, a new message that is a substring of already-accumulated text is also suppressed, even if it was never appended as its own line.*
Static path: MessageFormatting¶
- Choose a formatting entry point. Callers pick one of three static methods based on the desired separator:
GetMessagesForWindows(vbCrLf),GetMessagesForWeb(<BR>), orGetMessagesUserDefined(caller-suppliedinConcatString). - Strip embedded line breaks from each message. Each method iterates the input
inMessagesarray and callsRemoveReturns(Msg)on every element, which replaces embeddedvbCrLfor<BR>occurrences with an empty string so a single logical message cannot itself introduce extra line breaks into the joined result. - De-duplicate at the batch level. Each cleaned message is added to a local
Resultslist only if it is not already present (Not Results.Contains(temp)) (BR-002) — this is an exact-match check on the cleaned text, unlike the substring check used byAppManagerBase.AppendMessageLines. - Join the de-duplicated list.
BuildMessageString(Results, separator)concatenates every entry inResults, inserting the separator string after every entry except the last (compared by reference/value equality againstinMessages.Last, which can misbehave if the list contains a repeated final value — see failure notes). - Append to an existing message (alternate helper).
AppendMessageString(ByRef inMessage, inMessages, inConcatString)performs the same join as step 6 but appends the result onto an existinginMessagestring, inserting a hard-coded<br>separator first wheninMessagealready has non-blank content — independent of anyNewLineCharactersetting on a callingAppManagerBase.
Process flow¶
The following diagram visualizes both the instance append path and the static batch-formatting path.
flowchart TD
Start([Message text arrives]) --> Which{Instance or static path?}
Which -->|AppManagerBase| Norm[Normalize to single-string calls]
Norm --> Check{Non-empty and<br>not already in Message?}
Check -->|No| Skip([No-op: Message unchanged])
Check -->|Yes| Sep{Message already<br>has content?}
Sep -->|Yes| AddSep[Append NewLineCharacter]
Sep -->|No| AddDirect[No separator needed]
AddSep --> Append[Append message text]
AddDirect --> Append
Which -->|MessageFormatting| Clean[RemoveReturns on each message]
Clean --> Dedup[Add to Results if not<br>already present, exact match]
Dedup --> Join[BuildMessageString joins<br>Results with chosen separator]
Join --> Done([Return formatted string])
Failure and edge-case behavior¶
AppManagerBase.AppendMessageLinessuppresses on a substring match (Message.Contains(inMessage)), while the staticMessageFormattingmethods suppress on an exact match (Results.Contains(temp)) — the two "duplicate suppression" behaviors covered by BR-002 are not identical, and callers mixing both mechanisms on the same logical message list may see inconsistent results.*BuildMessageStringandAppendMessageStringcompareinMessages.Last <> Messageto decide whether to append the separator; if the same message string appears more than once at the end of a list that was not already de-duplicated by a caller, the separator logic can omit separators earlier than expected.*RemoveReturnsonly replaces the first kind of return sequence it detects (vbCrLfelse<BR>) — a message containing bothvbCrLfand<BR>only has thevbCrLfoccurrences removed, since the method returns immediately after that branch.*NewLineCharacteronAppManagerBasedefaults to<BR>(DefaultNewLineCharacter) and is settable per-instance, but the staticMessageFormatting.AppendMessageStringalways uses a hard-coded<br>regardless of anyAppManagerBase.NewLineCharactersetting, so the two paths can produce visually different separators in the same application.*
Note: Statements marked with
*were inferred from code structure, naming conventions, or contextual evidence where direct confirmation was not available in source artifacts.
Related documentation¶
Sources¶
Application\SAIF.Application\AppManagerBase.vbApplication\SAIF.Application\MessageFormatting.vb