'************************************************** ' FILE : BBCodeElement.vb ' AUTHOR : Paulo Santos ' CREATION : 4/29/2009 11:33:36 AM ' COPYRIGHT : Copyright © 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 ''' ''' Represents an BBCode element. ''' Public Class BBCodeElement(Of TContext As Class) Inherits BBCodeNode(Of TContext) Private __Name As String Private __Nodes As BBCodeNodeCollection(Of TContext) 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("\{(?[_a-zA-Z0-9].*?)\}") ''' Initializes an instance of the class. ''' This is the default constructor for this class. Friend Sub New() End Sub ''' Initializes an instance of the class. ''' The name of the element. Protected Sub New(ByVal name As String) __Name = name.ToUpperInvariant() End Sub ''' Initializes an instance of the class. ''' The parser used to create this element. Friend Sub New(ByVal parser As BBCodeParser(Of TContext)) MyBase.New(parser) End Sub ''' ''' Gets the name of the element. ''' Public ReadOnly Property Name() As String Get Return __Name End Get End Property ''' ''' Gets the replacement format for this element. ''' ''' The replacement format. ''' ''' In order to use the any parameter in the replacement format use the following syntax: {paramName}. ''' ''' The parameter names are case insensitive. ''' ''' There are two reserved parameter keywords for formatting: DEFAULT and VALUE. ''' DEFAULT : The text following the first equal sign after the name of the BBCode element. ''' VALUE: The HTML generated by the BBCode between the start and end element tag. ''' [url=http://tempuri.org]text[/url] ''' In the example above the parameter DEFAULT would have the value "http://tempuri.org", while VALUE would be "text". ''' ''' To replace [url=http://tempuri.org]text[/url] the with <a href="http://tempuri.org">text</a> the following ReplacementFormat should be used: ''' ''' <a href="{default|value}">{value|default}</a> ''' ''' 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. ''' 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 ''' ''' Gets a value indicating if the element requires a closing tag. ''' ''' True if the element requires a closing tag; otherwise False. 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 ''' ''' Gets the list of attributes. ''' Public ReadOnly Property Attributes() As BBCodeAttributeDictionary Get If (__Attributes Is Nothing) Then __Attributes = New BBCodeAttributeDictionary End If Return __Attributes End Get End Property ''' ''' Gets the list of sub nodes. ''' Public ReadOnly Property Nodes() As BBCodeNodeCollection(Of TContext) Get If (__Nodes Is Nothing) Then __Nodes = New BBCodeNodeCollection(Of TContext)(Me) End If Return __Nodes End Get End Property ''' Transforms this instance of into its desired text representation. ''' An object that implements the interface. ''' The text formatted by the . Public Overrides Function Format(ByVal context As TContext, 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, context, formatter)) Next If Not IsValueFormatted Then sb.Append(GetAttribute("value", context, formatter)) End If Return sb.ToString() End Function ''' Gets or sets the inner ''' The BBCode between the start and end tags. Public NotOverridable Overrides Property InnerBBCode() As String Get Dim sb As New System.Text.StringBuilder() For Each n As BBCodeNode(Of TContext) 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 ''' Gets or sets the outer ''' The BBCode of this instance of the . 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 ''' Gets or sets the plain text of the node. ''' The plain text between the start and end tags. 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(Of TContext)(value)) End If End Set End Property ''' ''' Sets the element name of this instance. ''' ''' The new element name of this instance. Friend Sub SetName(ByVal name As String) __Name = name End Sub ''' ''' Gets a value indicating wheter or not the have been formatted. ''' ''' True if the values have been formatted; otherwise False. 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 context As TContext, 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(context, 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