An alternative to UBB.threads
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

262 lines
10 KiB

'**************************************************
' FILE : BBCodeElement.vb
' AUTHOR : Paulo Santos
' CREATION : 4/29/2009 11:33:36 AM
' COPYRIGHT : Copyright <EFBFBD> 2009
' PJ on Development
' All Rights Reserved.
'
' Description:
' TODO: Add file description
'
' Change log:
' 0.1 4/29/2009 11:33:36 AM
' Paulo Santos
' Created.
'***************************************************
Imports System.Globalization
Imports System.Text.RegularExpressions
''' <summary>
''' Represents an BBCode element.
''' </summary>
Public Class BBCodeElement
Inherits BBCodeNode
Private __Name As String
Private __Nodes As BBCodeNodeCollection
Private __Attributes As BBCodeAttributeDictionary
Private __ReplacementFormat As String = String.Empty
Private __RequireClosingTag As TriState = TriState.UseDefault
Private __IsValueFormatted As Boolean
Private Shared ReadOnly __RxAttribute As New Regex("\{(?<name>[_a-zA-Z0-9].*?)\}")
''' <summary>Initializes an instance of the <see cref="BBCodeElement" /> class.
''' This is the default constructor for this class.</summary>
Friend Sub New()
End Sub
''' <summary>Initializes an instance of the <see cref="BBCodeElement" /> class.</summary>
''' <param name="name">The name of the element.</param>
Protected Sub New(ByVal name As String)
__Name = name.ToUpperInvariant()
End Sub
''' <summary>Initializes an instance of the <see cref="BBCodeElement" /> class.</summary>
''' <param name="parser">The parser used to create this element.</param>
Friend Sub New(ByVal parser As BBCodeParser)
MyBase.New(parser)
End Sub
''' <summary>
''' Gets the name of the element.
''' </summary>
Public ReadOnly Property Name() As String
Get
Return __Name
End Get
End Property
''' <summary>
''' Gets the replacement format for this element.
''' </summary>
''' <value>The replacement format.</value>
''' <remarks>
''' <para>In order to use the any parameter in the replacement format use the following syntax: {paramName}.</para>
''' <para/>
''' <para>The parameter names are case insensitive.</para>
''' <para/>
''' <para>There are two reserved parameter keywords for formatting: DEFAULT and VALUE.</para>
''' <para><c>DEFAULT</c> : The text following the first equal sign after the name of the BBCode element.</para>
''' <para><c>VALUE</c>: The HTML generated by the BBCode between the start and end element tag.</para>
''' <para><example>[url=http://tempuri.org]text[/url]</example></para>
''' <para>In the example above the parameter DEFAULT would have the value "http://tempuri.org", while VALUE would be "text".</para>
''' <para/>
''' <para>To replace [url=http://tempuri.org]text[/url] the with &lt;a href="http://tempuri.org"&gt;text&lt;/a&gt; the following ReplacementFormat should be used:</para>
''' <example>
''' &lt;a href="{default|value}"&gt;{value|default}&lt;/a&gt;
''' </example>
''' <para>The example above, will set the href attribute with either the default value or the text inside the [url] element. The pipe (|) implies in finding the first non-empty attribute.</para>
''' </remarks>
Public ReadOnly Property ReplacementFormat() As String
Get
If (String.IsNullOrEmpty(__ReplacementFormat)) Then
If (Me.Parser.Dictionary.ContainsKey(Me.Name)) Then
__ReplacementFormat = Me.Parser.Dictionary(Me.Name).ReplacementFormat
End If
End If
Return __ReplacementFormat
End Get
End Property
''' <summary>
''' Gets a value indicating if the element requires a closing tag.
''' </summary>
''' <value><c>True</c> if the element requires a closing tag; otherwise <c>False</c>.</value>
Public ReadOnly Property RequireClosingTag() As Boolean
Get
If (__RequireClosingTag = TriState.UseDefault) Then
If (Not String.IsNullOrEmpty(Me.Name) AndAlso Me.Parser.Dictionary.ContainsKey(Me.Name)) Then
__RequireClosingTag = If(Me.Parser.Dictionary(Me.Name).RequireClosingTag, TriState.True, TriState.False)
ElseIf (Not String.IsNullOrEmpty(Me.Name) AndAlso Me.Parser.ElementTypes.ContainsKey(Me.Name)) Then
__RequireClosingTag = If(Me.Parser.ElementTypes(Me.Name).RequireClosingTag, TriState.True, TriState.False)
End If
End If
Return (__RequireClosingTag = TriState.True)
End Get
End Property
''' <summary>
''' Gets the list of attributes.
''' </summary>
Public ReadOnly Property Attributes() As BBCodeAttributeDictionary
Get
If (__Attributes Is Nothing) Then
__Attributes = New BBCodeAttributeDictionary
End If
Return __Attributes
End Get
End Property
''' <summary>
''' Gets the list of sub nodes.
''' </summary>
Public ReadOnly Property Nodes() As BBCodeNodeCollection
Get
If (__Nodes Is Nothing) Then
__Nodes = New BBCodeNodeCollection(Me)
End If
Return __Nodes
End Get
End Property
''' <summary>Transforms this instance of <see cref="BBCodeElement" /> into its desired text representation.</summary>
''' <param name="formatter">An object that implements the <see cref="ITextFormatter" /> interface.</param>
''' <returns>The text formatted by the <see cref="ITextFormatter" />.</returns>
Public Overrides Function Format(ByVal formatter As ITextFormatter) As String
IsValueFormatted = False
Dim sb As New Text.StringBuilder(Me.ReplacementFormat)
Dim attribs = __RxAttribute.Matches(Me.ReplacementFormat)
For Each attrib As Match In attribs
sb.Replace(attrib.Value, GetAttribute(attrib.Groups("name").Value, formatter))
Next
If Not IsValueFormatted Then
sb.Append(GetAttribute("value", formatter))
End If
Return sb.ToString()
End Function
''' <summary>Gets or sets the inner </summary>
''' <value>The BBCode between the start and end tags.</value>
Public NotOverridable Overrides Property InnerBBCode() As String
Get
Dim sb As New System.Text.StringBuilder()
For Each n As BBCodeNode In Nodes
sb.Append(n.OuterBBCode)
Next
Return sb.ToString()
End Get
Set(ByVal value As String)
If Not RequireClosingTag Then
Throw New InvalidOperationException("The InnerBBCode property cannot be set for elements that does not require closing tags.")
End If
Me.Nodes.Clear()
Me.Nodes.AddRange(Me.Parser.Parse(value).Nodes)
End Set
End Property
''' <summary>Gets or sets the outer </summary>
''' <value>The BBCode of this instance of the <see cref="BBCodeNode" /> .</value>
Public NotOverridable Overrides ReadOnly Property OuterBBCode() As String
Get
Dim sb As New Text.StringBuilder()
sb.Append("[")
sb.Append(Me.Name)
If ((Me.Attributes.Count = 1) AndAlso Me.Attributes.ContainsKey("default")) Then
sb.AppendFormat(CultureInfo.InvariantCulture, "={0}", Quote(Me.Attributes("default")))
Else
For Each key In Me.Attributes.Keys
sb.AppendFormat(CultureInfo.InvariantCulture, " {0}={1}", key, Quote(Me.Attributes(key)))
Next
End If
If (Me.RequireClosingTag) Then
sb.Append("]")
sb.Append(Me.InnerBBCode)
sb.Append("[/")
sb.Append(Me.Name)
End If
sb.Append("]")
Return sb.ToString()
End Get
End Property
''' <summary>Gets or sets the plain text of the node.</summary>
''' <value>The plain text between the start and end tags.</value>
Public NotOverridable Overrides Property InnerText() As String
Get
Dim sb As New Text.StringBuilder()
For Each n In Me.Nodes
sb.Append(n.InnerText)
Next
Return sb.ToString()
End Get
Set(ByVal value As String)
If (Not RequireClosingTag) Then
Throw New InvalidOperationException("The InnerText property cannot be set for elements that does not require closing tags.")
End If
'*
'* Removes all nodex from the element
'*
Me.Nodes.Clear()
If (Not String.IsNullOrEmpty(value)) Then
'*
'* Only append a BBCodeText element if the value is not empty
'*
Me.Nodes.Add(New BBCodeText(value))
End If
End Set
End Property
''' <summary>
''' Sets the element name of this instance.
''' </summary>
''' <param name="name">The new element name of this instance.</param>
Friend Sub SetName(ByVal name As String)
__Name = name
End Sub
''' <summary>
''' Gets a value indicating wheter or not the <see cref="BBCodeElement.Nodes"/> have been formatted.
''' </summary>
''' <returns><c>True</c> if the values have been formatted; otherwise <c>False</c>.</returns>
Private Property IsValueFormatted() As Boolean
Get
Return __IsValueFormatted
End Get
Set(ByVal value As Boolean)
__IsValueFormatted = value
End Set
End Property
Private Function GetAttribute(ByVal name As String, ByVal formatter As ITextFormatter) As String
Dim attribs = name.ToUpperInvariant().Split("|".ToCharArray(), StringSplitOptions.RemoveEmptyEntries)
For Each attrib In attribs
If (String.CompareOrdinal(attrib, "VALUE") = 0) Then
IsValueFormatted = True
Dim sb As New Text.StringBuilder()
For Each node In Me.Nodes
sb.Append(node.Format(formatter))
Next
Return sb.ToString()
ElseIf (Me.Attributes.ContainsKey(attrib)) Then
Return (Me.Attributes.Item(attrib))
End If
Next
Return String.Empty
End Function
End Class