'**************************************************
' 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 <a href="http://tempuri.org">text</a> the following ReplacementFormat should be used:</para>
''' <example>
''' <a href="{default|value}">{value|default}</a>
''' </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