1. ''' <summary>
  2. ''' Performs a cursory check on the validity of SWIFT field data based on SWIFT field formats.
  3. ''' This attempts to convert the proprietary notation used by SWIFT to standard regex to
  4. ''' achieve this goal.
  5. ''' SWIFT field specs can be broken into multiple lines, each line can contain 1
  6. ''' or more subfield definitions.
  7. ''' Square braces indicate a subfield is optional.
  8. ''' Each subfield notates by a character set, fixed characters and the number
  9. ''' (fixed or maximum) for each set. For instance:
  10. ''' [/14!N] would imply an optional field consisting of a slash then exactly
  11. ''' 14 digits.
  12. ''' 4*35X would imply up to 4 lines each containing 1 to 35 characters out
  13. ''' of character set X.
  14. ''' </summary>
  15. ''' <param name="sSwiftFormatSpec">Field spec in SWIFT standard notation. Pipe delimitered for lines.</param>
  16. ''' <param name="sData">Contents of the field to be validated.</param>
  17. ''' <returns>True if data matches spec</returns>
  18. ''' <remarks>This is NOT a full data validation. The rules about SWIFT fields are complex
  19. ''' and vary frequently as notated in the guides for specific instances. Specific checks,
  20. ''' if required should be used in addition to this validation.
  21. ''' </remarks>
  22. Public Function FormatCheck(ByVal sSwiftFormatSpec As String, ByVal sData As String) As Boolean
  23. sData = sData.ToUpper
  24. sSwiftFormatSpec = sSwiftFormatSpec.ToUpper
  25. 'Split incoming SWIFT format specs into an array on the pipe symbol (pre-existing format)
  26. Dim sFormatLines As String() = Split(sSwiftFormatSpec, "|")
  27.  
  28. Dim sRegexLine As String = ""
  29. Dim bAllOptional As Boolean = True 'Keeps track of whether an entire line is optional
  30. Dim bOptional As Boolean = False 'Keeps track of whether field is optional
  31. Dim cCharacterSet As Char 'Character set for field
  32. Dim sRegex As String = "\A"
  33. Dim bNewLineAdded As Boolean = False
  34.  
  35. For Each sLine As String In sFormatLines
  36. bNewLineAdded = False
  37. bAllOptional = True
  38. sRegexLine = ""
  39.  
  40. 'Split each line based into fields based on location of the character set code (A,C,D,E,H,N,X or Z).
  41. 'These will be the last character in the set, UNLESS the field is optional in which case it will
  42. 'end with character set code followed by ]. There has to be a cleaner way than replacing then
  43. 'splitting. Feel free to find it.
  44. For Each sField As String In Split(Regex.Replace(sLine, "([ACDEHNXZ]]?)([^\]])", "$1|$2"), "|")
  45.  
  46. 'Identify the character set
  47. cCharacterSet = Regex.Match(sField, "[ACDEHNXZ]").Value
  48.  
  49. 'Identify optional fields and flag/clean to make following steps easier.
  50. If sField.StartsWith("[") Then
  51. bOptional = True
  52. sField = Replace(sField, "[", "")
  53. sField = Replace(sField, "]", "")
  54. Else
  55. bAllOptional = False
  56. bOptional = False
  57. End If
  58.  
  59. 'First locate fixed field length indicated by exclamation mark and convert.
  60. 'e.g. 16!N becomes N{16,16}
  61. sField = Regex.Replace(sField, "(\d{1,2})(!)(" & cCharacterSet & ")", "$3{$1,$1}")
  62.  
  63. 'Next locate non fixed field length and convert.
  64. 'e.g. 35X becomes X{1,35}
  65. sField = Regex.Replace(sField, "(\d{1,2})(" & cCharacterSet & ")", "$2{1,$1}")
  66.  
  67. 'If field is optional then make regex optional.
  68. 'e.g. [35X] now becomes (X(1,35))?
  69. If bOptional = True Then sField = "(" & sField & ")?"
  70.  
  71. 'Lookup the regex conversion of acceptable characters for this SWIFT character
  72. 'set and replace accordingly.
  73. 'e.g. 4!N is now [0-9]{4,4}
  74. sField = Replace(sField, cCharacterSet, "[" & _dictListsRegex(cCharacterSet) & "]")
  75.  
  76. 'If the field contains a line multiplier repeat the regex as required with
  77. 'CRLF.
  78. 'So if regex for 35X is Y then 4*35X becomes (Y\r\n){1,4}
  79. If Regex.IsMatch(sField, "(\d{1,2})(\*)(.*)") Then
  80. sField = Regex.Replace(sField, "(\d{1,2})(\*)(.*)", "(^$3\r\n){1,$1}")
  81. bNewLineAdded = True
  82. End If
  83. sRegexLine += sField
  84. Next
  85.  
  86. 'Append CRLF if not already there. This is another hack to make the regex easier.
  87. If Not bNewLineAdded Then sRegexLine += "\r\n"
  88.  
  89. 'If the entire line consists of only optional fields make the entire line optional in regex
  90. If bAllOptional Then sRegexLine = "(" & sRegexLine & ")?"
  91. sRegex += sRegexLine
  92. Next
  93. sRegex += ("\Z")
  94. sData += vbCrLf
  95.  
  96. 'Finally all fields must have some content, even if all parts are optional. If field is blank
  97. 'then return false. Otherwise return if a valid match has been found.
  98. Return Not (Trim(Replace(Regex.Match(sData, sRegex, RegexOptions.Multiline).Value, vbCrLf, "")) = "")
  99. End Function