'**************************************************
' FILE : BBCodeParser.vb
' AUTHOR : Paulo Santos
' CREATION : 4/29/2009 11:03:53 AM
' COPYRIGHT : Copyright © 2009
' PJ on Development
' All Rights Reserved.
'
' Description:
' TODO: Add file description
'
' Change log:
' 0.1 4/29/2009 11:03:53 AM
' Paulo Santos
' Created.
'***************************************************
Imports System.Text.RegularExpressions
Imports System.Diagnostics.CodeAnalysis
'''
''' The parser of
'''
Public NotInheritable Class BBCodeParser(Of TContext As Class)
Private __Factory As BBCodeElementFactory(Of TContext)
Private __Configuration As BBCodeConfiguration(Of TContext)
Private Shared ReadOnly __ConfigSerializer As New System.Xml.Serialization.XmlSerializer(GetType(BBCodeConfiguration(Of TContext)))
Private Shared ReadOnly __Tokenizer As Tokenization.Tokenizer = PrepareTokenizer()
''' Initializes an instance of the class.
''' This is the default constructor for this class.
Public Sub New()
End Sub
'''
''' Gets the dictionary of elements to be replaced by the .
'''
Public ReadOnly Property Dictionary() As BBCodeElementDictionary
Get
If (__Configuration Is Nothing) Then
__Configuration = New BBCodeConfiguration(Of TContext)()
End If
Return __Configuration.Dictionary
End Get
End Property
'''
''' Gets the dictionary of types created by the parser.
'''
Public ReadOnly Property ElementTypes() As BBCodeElementTypeDictionary(Of TContext)
Get
If (__Configuration Is Nothing) Then
__Configuration = New BBCodeConfiguration(Of TContext)()
End If
Return __Configuration.ElementTypes
End Get
End Property
#Region " LoadConfiguration Methods "
'''
''' Loads the configuration from the specified filename.
'''
''' The name of the file to read the dictionary from.
Public Sub LoadConfiguration(ByVal fileName As String)
If (String.IsNullOrEmpty(fileName)) Then
Throw New ArgumentNullException("fileName")
End If
Using fileStream As New IO.FileStream(fileName, IO.FileMode.Open)
LoadConfiguration(fileStream)
End Using
End Sub
'''
''' Loads the configuration from the specified .
'''
''' A to read the dictionary from.
Public Sub LoadConfiguration(ByVal stream As IO.Stream)
LoadConfiguration(New IO.StreamReader(stream, Text.Encoding.UTF8, True))
End Sub
'''
''' Loads the configuration from the specified .
'''
''' The to read the dictionary from.
Public Sub LoadConfiguration(ByVal reader As IO.TextReader)
Dim dic = __ConfigSerializer.Deserialize(reader)
If (dic IsNot Nothing) Then
__Configuration = dic
End If
End Sub
#End Region
#Region " SaveConfiguration Methods "
'''
''' Saves the conficuration to the specified file.
'''
''' The name of the file to save the dictionary.
Public Sub SaveConfiguration(ByVal fileName As String)
If (String.IsNullOrEmpty(fileName)) Then
Throw New ArgumentNullException("fileName")
End If
Using fileStream As New IO.FileStream(fileName, IO.FileMode.Create)
SaveConfiguration(fileStream)
End Using
End Sub
'''
''' Saves the conficuration to the specified .
'''
''' The to save the dictionary.
Public Sub SaveConfiguration(ByVal stream As IO.Stream)
SaveConfiguration(New IO.StreamWriter(stream, Text.Encoding.UTF8))
End Sub
'''
''' Saves the conficuration to the specified .
'''
''' The to save the dictionary.
Public Sub SaveConfiguration(ByVal writer As IO.TextWriter)
__ConfigSerializer.Serialize(writer, __Configuration)
End Sub
#End Region
#Region " Parse Methods "
'''
''' Parses the specified text, returning a collection of .
'''
''' The text to be parsed.
''' A containing the parsed text.
Public Function Parse(ByVal text As String) As BBCodeDocument(Of TContext)
Using reader As New IO.StringReader(text)
Return Parse(reader)
End Using
End Function
'''
''' Parses the specified stream, returning a collection of .
'''
''' The to be parsed.
''' A containing the parsed .
Public Function Parse(ByVal stream As IO.Stream) As BBCodeDocument(Of TContext)
Return Parse(stream, Text.Encoding.UTF8)
End Function
'''
''' Parses the specified stream, returning a collection of .
'''
''' The to be parsed.
''' The encoding of the stream.
''' A containing the parsed .
Public Function Parse(ByVal stream As IO.Stream, ByVal encoding As Text.Encoding) As BBCodeDocument(Of TContext)
Return Parse(New IO.StreamReader(stream, encoding))
End Function
'''
''' Parses the specified , returning a collection of .
'''
''' The to be parsed.
''' A containing the parsed .
Public Function Parse(ByVal reader As IO.TextReader) As BBCodeDocument(Of TContext)
Dim doc = New BBCodeDocument(Of TContext)(Me)
Dim rootElement As New BBCodeElement(Of TContext)(Me)
Dim currentElement As BBCodeElement(Of TContext) = rootElement
Dim tk As Tokenization.Token
Dim sb As New Text.StringBuilder()
Dim sbText As New Text.StringBuilder()
Do While (reader.Peek() <> -1)
Dim line As String = reader.ReadLine() & vbCrLf
sbText.AppendLine(line)
Do
'*
'* Get the next token
'*
tk = Tokenizer.GetToken(line)
If (tk Is Nothing) Then
Exit Do
End If
Dim tag = New BBCodeTag(tk.Value)
ParseElement(rootElement, currentElement, tk, sb, tag)
Loop
Loop
'*
'* Add the text node
'*
If (sb.Length > 0) Then
currentElement.Nodes.Add(New BBCodeText(Of TContext)(sb.ToString()))
End If
'*
'* Add the nodes to the document
'*
doc.Nodes.AddRange(rootElement.Nodes)
'*
'* Sets the source text
'*
doc.SetText(sbText.ToString())
Return doc
End Function
#End Region
'''
''' Gets the .
'''
Private Shared ReadOnly Property Tokenizer() As Tokenization.Tokenizer
Get
Return __Tokenizer
End Get
End Property
'''
''' Gets the .
'''
Private ReadOnly Property Factory() As BBCodeElementFactory(Of TContext)
Get
If (__Factory Is Nothing) Then
__Factory = New BBCodeElementFactory(Of TContext)(Me)
End If
Return __Factory
End Get
End Property
Private Sub ParseElement(ByVal rootElement As BBCodeElement(Of TContext), ByRef currentElement As BBCodeElement(Of TContext), ByVal token As Tokenization.Token, ByVal sb As Text.StringBuilder, ByVal tag As BBCodeTag)
'*
'* Check the token Type
'*
Select Case token.RuleType
Case 0, -1
'*
'* Empty tag or char
'*
sb.Append(token.Value)
Case 1
'*
'* Closing tag
'*
ParseClosingTag(rootElement, currentElement, token, sb, tag)
Case 2, 3, 4
'*
'* Value Tag, Parametrized Tag, Generic Tag
'*
ParseTag(currentElement, sb, tag)
End Select
End Sub
Private Sub ParseTag(ByRef currentElement As BBCodeElement(Of TContext), ByVal sb As Text.StringBuilder, ByVal tag As BBCodeTag)
'*
'* Add the text previous to the current element
'*
If (sb.Length > 0) Then
currentElement.Nodes.Add(New BBCodeText(Of TContext)(sb.ToString()))
sb.Remove(0, sb.Length)
End If
'*
'* Add the new element to the list of nodes
'*
Dim el = Factory.CreateElement(tag.Name, tag.Paramters)
currentElement.Nodes.Add(el)
'*
'* Change the current element, if it requires an closing tag
'*
If (el.RequireClosingTag) Then
currentElement = el
End If
End Sub
Private Shared Sub ParseClosingTag(ByVal rootElement As BBCodeElement(Of TContext), ByRef currentElement As BBCodeElement(Of TContext), ByVal token As Tokenization.Token, ByVal sb As Text.StringBuilder, ByVal tag As BBCodeTag)
'*
'* Check if the closing tag is closing a previously open tag
'*
If currentElement.RequireClosingTag AndAlso (String.CompareOrdinal(currentElement.Name, tag.Name) = 0) Then
'*
'* Add the inner text
'*
If (sb.Length > 0) Then
currentElement.Nodes.Add(New BBCodeText(Of TContext)(sb.ToString()))
sb.Remove(0, sb.Length)
End If
'*
'* Move up a level
'*
currentElement = If(currentElement.Parent, rootElement)
Else
'*
'* Adds to the text
'*
sb.Append(token.Value)
End If
End Sub
Private Shared Function PrepareTokenizer() As Tokenization.Tokenizer
Dim tk As New Tokenization.Tokenizer
'*
'* Prepares the BBCode Grammar
'*
With tk
'*
'* Define the grammar macros
'*
AddTokenizerBaseMacros(tk)
'*
'* Define the grammar rules
'*
.AddRule("EmptyTag", 0, "\[{w}\]")
.AddRule("ClosingTag", 1, "\[/{name}\]")
.AddRule("ValueTag", 2, "\[{param}\]")
.AddRule("ParamsTag", 3, "\[{name}{params}\]")
.AddRule("Tag", 4, "\[[^ \t\r\n\f\]]+?\]")
.AddRule("Char", -1, ".")
End With
Return tk
End Function
End Class