I want to optimize this code in the following areas: internal error checking (not just try...catch, but if...then as well) and for speed. See below for code to be optimized. I'll post a reply that has some test code.

VB Code:
  1. Public Function ParseText( _
  2.            ByVal TextToParse As String, _
  3.            ByVal ParseStartDelim As Char, _
  4.            ByVal ParseEndDelim As Char, _
  5.            ByVal ClassCollection As Collection, _
  6.            Optional ByVal ClassFieldSeparator As Char = ".", _
  7.            Optional ByVal DebugMode As Boolean = False, _
  8.            Optional ByVal DebugEndBlockPrefix As Char = "/", _
  9.            Optional ByVal StartTag As String = "[template start]", _
  10.            Optional ByVal EndTag As String = "[template end]") _
  11.     As String
  12.         'For use with web pages that need data access
  13.         '
  14.         'Parses text based on start and end delim char. Uses CallByName to determine
  15.         'what to replace parsed values with. Debug mode helps make debuging final
  16.         'output easier.
  17.         'Assuming that ClassCollection has an item that is a class with a field named
  18.         ''name' that was set to 'Jack Schitt' and said item's Key in the collection
  19.         'was 'Profile'. Also assume 'ParseStartDelim', 'ParseEndDelim',
  20.         ''ClassFieldSeperator' and are '[', ']', and '.', respectivly, then the
  21.         'samples can be shown as:
  22.         '
  23.         '"test test [profile.name] test"
  24.         '
  25.         'With DebugMode=False, this function would return
  26.         '"test test Jack Schitt test"
  27.         '
  28.         'With DebugMode=True, this function would return
  29.         '"test test [profile.name]Jack Schitt[/profile.name] test"
  30.         '
  31.         'Classes and/or members that don't exist raise an exception that is trapped
  32.         'internally, causeing the tag to be replaced with "". (This is useful for
  33.         'comments.) In debug mode, it will be replaced by the message of the exception.
  34.         '
  35.         'Last but not least, there are the 'StartTag' and 'EndTag' params. Basically,
  36.         'only text between those tags is returned. Text outside of those tags is
  37.         'removed prior to parsing.
  38.  
  39.         Dim Pos As Integer 'current position in string
  40.         Dim SubStringHolder As String 'Holds a substring between a particular [ and ]
  41.         '                              [ is shorthand for ParseStartDelim
  42.         '                              (and viceversa)
  43.         Dim ClassName As String
  44.         Dim FieldName As String
  45.         Dim [Class] As Object
  46.         Dim ReturnValue As Object
  47.  
  48.         'check for start tag
  49.         If TextToParse.IndexOf(StartTag) <> -1 Then
  50.             'remove everything before [template start]
  51.             TextToParse = TextToParse.Remove(0, TextToParse.IndexOf(StartTag) + StartTag.Length)
  52.         End If
  53.  
  54.         'check for end tag
  55.         If TextToParse.IndexOf(EndTag) <> -1 Then
  56.             'remove everything after end tag
  57.             TextToParse = TextToParse.Remove(TextToParse.IndexOf(EndTag), TextToParse.Length - TextToParse.IndexOf(EndTag))
  58.         End If
  59.  
  60.         While Pos < (TextToParse.Length)
  61.             'if [character at Pos] = ParseStartDelim
  62.             '...The complicated, but efficient way...
  63.             If (TextToParse.Substring(Pos, 1).Chars(0).Equals(ParseStartDelim)) Then
  64.                 'In Tag
  65.  
  66.                 Try
  67.                     'we need the substring from the current [ to the next ]
  68.                     'get everything from Pos on
  69.                     SubStringHolder = TextToParse.Substring(Pos + 1)
  70.                     'trim it to the next ]
  71.  
  72.                     If SubStringHolder.IndexOf(ParseEndDelim) = -1 Then
  73.                         Throw New ParseException("Reached end of file without closing delimiter.", True)
  74.                     End If
  75.  
  76.                     SubStringHolder = SubStringHolder.Substring(0, SubStringHolder.IndexOf(ParseEndDelim))
  77.  
  78.                     'remove the existing text
  79.                     TextToParse = TextToParse.Remove(Pos, SubStringHolder.Length + 2)
  80.  
  81.                     If SubStringHolder.IndexOf(ClassFieldSeparator) = -1 Then
  82.                         'contains no '.'
  83.                         ClassName = SubStringHolder
  84.                         Throw New ParseException("No class name specified", False)
  85.                     End If
  86.  
  87.                     ClassName = SubStringHolder.Substring(0, SubStringHolder.IndexOf(ClassFieldSeparator))
  88.  
  89.                     FieldName = SubStringHolder.Substring(SubStringHolder.IndexOf(ClassFieldSeparator) + 1)
  90.  
  91.                     If ClassName = "" Then
  92.                         '[.foo]
  93.                         Throw New ParseException("No class name specified", False)
  94.                     End If
  95.  
  96.                     If FieldName = "" Then
  97.                         '[foo.]
  98.                         Throw New ParseException("No field name specified", False)
  99.                     End If
  100.  
  101.                     [Class] = ClassCollection(ClassName)
  102.                     ReturnValue = CallByName([Class], FieldName, CallType.Get)
  103.  
  104.                     'this next block is required incase field is a sub or
  105.                     'otherwise doesn't return a value
  106.                     If ReturnValue Is Nothing Then
  107.                         If DebugMode Then
  108.                             SubStringHolder = "Field was called but returned 'Nothing'"
  109.                         Else
  110.                             SubStringHolder = ""
  111.                         End If
  112.                     Else
  113.                         SubStringHolder = ReturnValue.ToString
  114.                     End If
  115.                 Catch nope As MissingMemberException
  116.                     'CallByName throws this.
  117.                     '[foo.bar] 'bar' is not found
  118.                     If DebugMode Then
  119.                         SubStringHolder = "Field name not found"
  120.                     Else
  121.                         SubStringHolder = ""
  122.                     End If
  123.                 Catch nope As ArgumentException
  124.                     '[Class] = ClassCollection(ClassName) throws this
  125.                     '[foo.bar] 'foo' not found in collection
  126.                     If DebugMode Then
  127.                         SubStringHolder = "Class name not found"
  128.                     Else
  129.                         SubStringHolder = ""
  130.                     End If
  131.                 Catch nope As ParseException
  132.                     'Several If...Then blocks above throw this
  133.                     If DebugMode Then
  134.                         SubStringHolder = nope.Message
  135.                     Else
  136.                         SubStringHolder = ""
  137.                     End If
  138.  
  139.                     If nope.IsFatal Then
  140.                         Return TextToParse & " " & SubStringHolder
  141.                         Exit Function
  142.                     End If
  143.  
  144.                 Finally
  145.                     If DebugMode Then
  146.                         'if debug mode, wrap the string with tags
  147.  
  148.                         '[foo.bar]{value}
  149.                         SubStringHolder = ParseStartDelim & _
  150.                         ClassName & ClassFieldSeparator & FieldName & _
  151.                         ParseEndDelim & SubStringHolder
  152.  
  153.                         '+=[/foo.bar]
  154.                         SubStringHolder += ParseStartDelim & DebugEndBlockPrefix & _
  155.                         ClassName & ClassFieldSeparator & FieldName & _
  156.                         ParseEndDelim
  157.                     End If
  158.                 End Try
  159.  
  160.                 'insert the value into the main text where the tag originally
  161.                 'was. if DebugMode, the tags were wrapped above.
  162.                 TextToParse = TextToParse.Insert(Pos, SubStringHolder)
  163.  
  164.                 'increment the position by the length of the new value. -1 for
  165.                 'the statement after this one
  166.                 Pos += SubStringHolder.Length - 1
  167.             End If
  168.             Pos += 1
  169.             If Pos < 0 Then Exit While
  170.         End While
  171.  
  172.         Return TextToParse
  173.     End Function
  174.  
  175.     Public Class ParseException
  176.         'this exception used internally by the parser
  177.         '
  178.         ''message' is the text sent to the output when the parser's
  179.         ''debugmode' is true
  180.         '
  181.         ''IsFatal' tells the parser to stop parseing. If true, ''Message' is
  182.         'appended to the end of the output.
  183.  
  184.         Inherits Exception
  185.  
  186.         Sub New(ByVal message As String, ByVal IsFatal As Boolean)
  187.             MyBase.New(message)
  188.             _IsFatal = IsFatal
  189.         End Sub
  190.  
  191.         Private _IsFatal As Boolean 'private copy
  192.  
  193.         Public ReadOnly Property IsFatal() As Boolean
  194.             Get
  195.                 Return _IsFatal
  196.             End Get
  197.         End Property
  198.     End Class