'------------------------------------------------------------------------------
' Agrupar pares (22/Sep/07)
' Le cambio el nombre de la clase a ProcesarGrupos (23/Sep/07)
'
' Clase para manejar los grupos de textos en las palabras clave de Eliza
' Reducida en funcionalidad para la clase EvaluarExpresiones
'
' Vuelvo a cambiarle el nombre a AgruparPares (02/Oct/07)
' La usaré como clase base para ProcesarGrupos en ElizaNET.
'
' Lo incluyo en una DLL con EvaluarExpresiones (04/Oct/07)
' Cambio el ámbito a Friend
'
' ©Guillermo 'guille' Som, 2007
'
' El espacio de nombre de esta librería es:
' elGuille.Developer
'------------------------------------------------------------------------------
Option Strict On
Imports Microsoft.VisualBasic
Imports System
Imports System.Collections
Imports System.Collections.Generic
Imports System.Text
'--------------------------------------------------------------------------
' Para procesar los grupos de paréntesis
'--------------------------------------------------------------------------
''' <summary>
''' Delegado para el evento de la clase AgruparPares
''' </summary>
''' <param name="mensaje">
''' El mensaje de notificación
''' </param>
''' <remarks></remarks>
Friend Delegate Sub AgruparParesEventHandler(ByVal mensaje As String)
''' <summary>
''' Agrupar las cadenas que estén entre los pares indicados.
''' Por ejemplo, para agrupar lo que está entre paréntesis,
''' de esa forma si hay anidamiento se puede procesar bien.
''' </summary>
''' <remarks>
''' La idea original la usé en el conversor de VB a C#
''' después lo puse como case independiente (ProcesarGrupos)
''' para el programa ElizaNET, finalmente reduje la clase
''' para usarla en EvaluarExpresiones.
''' </remarks>
Friend Class AgruparPares
Public Event ErrorAgruparPares As AgruparParesEventHandler
Protected Overridable Sub OnErrorAgruparPares(ByVal mensaje As String)
RaiseEvent ErrorAgruparPares(mensaje)
End Sub
Protected m_Sustituciones As New List(Of String)
Protected m_Texto As String
Protected m_TextoSustituido As String
' En esta clase solo se usa este símbolo para agrupar pares
' (si se quiere hacer más genérica, hay que permitir otros símbolos,
' en ese caso hay que tener en cuenta el valor de la cadena sSeparadores)
Protected Const m_Simbolo As Char = "·"c
' Para que permita hasta 9999 niveles (03/Oct/07)
Protected Const formatoNivel As String = "0000"
Protected Const m_MarcadorInicio As String = "ñ" & m_Simbolo
Protected Const m_MarcadorFin As String = m_Simbolo & "ñ"
''' <summary>
''' Devuelve True si la sustitución indicada tiene marcadores
''' </summary>
''' <param name="index"></param>
''' <returns></returns>
''' <remarks>
''' 01/Oct/07
''' </remarks>
Public Function ContieneMarcador(ByVal index As Integer) As Boolean
Return m_Sustituciones(index).Contains(m_MarcadorInicio)
End Function
''' <summary>
''' Los caracteres usados para indicar un marcador
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks>
''' 01/Oct/07
''' </remarks>
Public ReadOnly Property MarcadorInicio() As String
Get
Return m_MarcadorInicio
End Get
End Property
Public ReadOnly Property LenFormato() As Integer
Get
Return formatoNivel.Length
End Get
End Property
''' <summary>
''' Longitud del texto usado como marcador
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
Public ReadOnly Property LenMarcador() As Integer
Get
Return formatoNivel.Length + 4
End Get
End Property
''' <summary>
''' El marcador correspondiente al índice indicado
''' </summary>
''' <param name="index">
''' Índice el marcador,
''' será un valor entre cero y Sustituciones.Count - 1
''' (ambos incluidos)
''' </param>
''' <returns></returns>
''' <remarks>
''' Se usa de forma interna para hacer las sustituciones
''' pero lo dejo público por si se quiere usar desde fuera
''' </remarks>
Public Function Marcador(ByVal index As Integer) As String
Return m_MarcadorInicio & index.ToString(formatoNivel) & m_MarcadorFin
End Function
''' <summary>
''' El texto de cada sustitución
''' Este será el texto a poner en cada marcador
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
Public ReadOnly Property Sustituciones() As List(Of String)
Get
Return m_Sustituciones
End Get
End Property
''' <summary>
''' Obtiene las sustituciones
''' Si invertido = False es como llamar a la propiedad Sustituciones
''' </summary>
''' <param name="invertido">
''' True si se quiere invertir el orden de las sustituciones.
''' False para obtener lo mismo que llamando a la propiedad Sustituciones,
''' en realidad solo tiene utilidad si el valor es True
''' </param>
''' <returns>
''' Una lista con los grupos correspondientes a las sustituciones
''' </returns>
''' <remarks></remarks>
Public Function GetSustituciones(ByVal invertido As Boolean) As List(Of String)
If invertido Then
Dim col As New List(Of String)
col.AddRange(m_Sustituciones)
col.Reverse()
Return col
End If
Return m_Sustituciones
End Function
''' <summary>
''' Cadena con el texto original
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
Public ReadOnly Property Texto() As String
Get
Return m_Texto
End Get
End Property
''' <summary>
''' El texto con las sustituciones hechas
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
Public ReadOnly Property TextoSustituido() As String
Get
Return m_TextoSustituido
End Get
End Property
''' <summary>
''' Constructor privado para usar desde GetAgruparPares
''' </summary>
''' <remarks></remarks>
Protected Sub New()
End Sub
'-------------------------
' Los métodos compartidos
'-------------------------
''' <summary>
''' El texto que se ha indicado para analizar
''' Es el mismo que Texto, pero en este caso, se usa para
''' los mensajes de error que se produzcan al analizar sub grupos
''' que estén mal anidados o que no se permitan
''' </summary>
''' <remarks></remarks>
Protected Shared TextoInicial As String
''' <summary>
''' Obtiene un objeto con los grupos del texto indicado
''' </summary>
''' <param name="texto">
''' Texto a analizar en los que se incluyen grupos
''' </param>
''' <param name="parIni">
''' Caracteres para indicar el inicio de un grupo
''' (será la llave de apertura seguida de un asterisco {* )
''' </param>
''' <param name="parFin">
''' Caracteres para indicar el final de un grupo
''' (será la llave de cierre })
''' </param>
''' <returns>
''' Devuelve una instancia de esta clase
''' </returns>
''' <remarks>
''' La única forma de crear un objeto de esta clase
''' es por medio de este método
''' </remarks>
Public Shared Function CrearInstancia(ByVal texto As String, _
ByVal parIni As String, _
ByVal parFin As String _
) As AgruparPares
TextoInicial = texto
Return getAgruparPares(texto, parIni, parFin)
End Function
''' <summary>
''' Obtiene un objeto con los grupos del texto indicado
''' Este método es para uso interno, ya que desde esta clase
''' se crean nuevas clases, pero no deben cambiar el valor de TextoInicial
''' </summary>
''' <param name="texto">
''' Texto a analizar en los que se incluyen grupos
''' </param>
''' <param name="parIni">
''' Caracteres para indicar el inicio de un grupo
''' (será la llave de apertura seguida de un asterisco {* )
''' </param>
''' <param name="parFin">
''' Caracteres para indicar el final de un grupo
''' (será la llave de cierre })
''' </param>
''' <returns>
''' Devuelve una instancia de esta clase
''' </returns>
''' <remarks>
''' Este método se usa internamente en la clase
''' </remarks>
Protected Shared Function getAgruparPares(ByVal texto As String, _
ByVal parIni As String, _
ByVal parFin As String _
) As AgruparPares
Dim pGr As New AgruparPares()
pGr.m_Texto = texto
' Si no tiene ninguno de los caracteres (01/Oct/07)
If texto.IndexOfAny((parIni & parFin).ToCharArray) = -1 Then
texto = parIni & texto & parFin
End If
Dim i, j As Integer
Dim s1 As String
Dim sb As New StringBuilder(texto)
i = 0
j = 0
Do
' Buscar el último
i = sb.ToString.LastIndexOf(parIni)
If i > -1 Then
' buscar el siguiente
j = sb.ToString.IndexOf(parFin, i + 1)
' si no está el de cierre, se supone el final...
' aunque se debería producir un error
If j = -1 Then
Throw New ArgumentException( _
"Los caracteres de apertura y cierre no están emparejados," & _
" falta uno de cierre.")
Return Nothing
End If
pGr.m_Sustituciones.Add(sb.ToString.Substring(i, j - i + parFin.Length))
s1 = pGr.Marcador(pGr.Sustituciones.Count - 1)
' quitar lo hallado
sb.Remove(i, j - i + parFin.Length)
' insertar la variable
sb.Insert(i, s1)
Else
Exit Do
End If
Loop
pGr.m_TextoSustituido = sb.ToString
Return pGr
End Function
End Class