Entradas para la categoría 'Artículos'

Herramienta para crear fuentes

Jueves 27 de Septiembre de 2007 a las 0:23

Esta aplicación permite exportar las fuentes instaladas en el sistema como una imagen a un fichero del tipo bmp, gif, png o jpg y un fichero que describe la ubicación y dimensiones de cada carácter dentro de la imagen.

Se permite indicar los caracteres a exportar no teniendo porque exportar todos. Dispone de un menú para seleccionar los caracteres más comunes como las minúsculas, las mayúsculas, los dígitos del 0 al 9, etc. lo que evita al usuario tener que teclear dichos caracteres.

También se permite configurar el tipo de fuente, su tamaño, color, si esta en negrita, cursiva, el formato de la imagen (png, bmp, gif, jpg), las dimensiones de la imagen, el color que será considerado como transparente y el espacio que se dejará entre caracteres. La imagen puede crearse haciendo uso del anti-aliasing, lo cual tiene la ventaja de que los bordes de las caracteres quedan más suavizados.

Todos los datos establecidos en la la herramienta se conservan para la siguiente ejecución, con lo que no es necesario volver a introducir dichos datos.

Se ofrece la posibilidad de previsualizar la imagen antes de proceder a su exportación.

(Pulsad la imagen para verla a tamaño real)
Creador de fuentes.
Creador de fuentes.

Una de las características más interesantes de la herramienta es la enorme flexibilidad para exportar los datos a diferentes formatos y la facilidad para incorporar nuevos formatos personalizados mediante un sistema de plugins.

Independientemente del formato de exportación, los valores que se guardarán en el fichero serán el ancho y el alto de la imagen en pixeles, el espacio entre caracteres y por cada carácter la posición expresada en pixels tanto en X como en Y y el ancho y alto del carácter.

Inicialmente la herramienta cuenta con cinco formatos para exportar las fuentes:
- Fichero CSV: Valores separados por comas (en este paso por ;)
- Fichero XML: XML donde cada carácter es un nodo, que contiene cuatro nodos hijos con los diferentes valores
- Fichero XML Atributos: XML donde cada carácter es un nodo que tiene cuatro atributos con los diferentes valores
- Fichero Binario: Los datos se guardan en un fichero binario
- Fichero Texto: Los datos se guardan como texto plano

La forma de crear plugins para extender la funcionalidad de la herramienta incorporándola nuevos formatos de exportación consiste en crear una dll que contenga la clase o clases con los nuevos formatos. Cada clase que proporcione un nuevo formato debe implementar la interfaz IPersistir que tiene la siguiente definición:

 

VB.NET:
  1. Public Interface IPersistir
  2.  
  3.     ''' <summary>
  4.     ''' Nombre del plugin
  5.     ''' </summary>
  6.     ''' <value></value>
  7.     ''' <returns></returns>
  8.     ''' <remarks></remarks>
  9.     ReadOnly Property Nombre() As String
  10.  
  11.     ''' <summary>
  12.     ''' Escribe en un fichero la información de la fuente
  13.     ''' </summary>
  14.     ''' <param name="ficheroSalidaSinExtension">Ruta del fichero (sin incluir la extension) en el que
  15.     ''' se escribirá la información de los caracteres</param>
  16.     ''' <param name="anchoImagen">Ancho en pixeles de la imagen</param>
  17.     ''' <param name="altoImagen">Alto en pixeles de la imagen</param>
  18.     ''' <param name="separacionEntreCaracteres">Número de pixels de separación entre carácter y carácter</param>
  19.     ''' <param name="caracteres">Carácteres a escribir junto con su información</param>
  20.     ''' <remarks></remarks>   
  21.     Sub Escribir(ByVal ficheroSalidaSinExtension As String, ByVal anchoImagen As Integer, ByVal altoImagen As Integer, ByVal separacionEntreCaracteres As Integer, ByVal caracteres As Dictionary(Of Char, Rectangle))
  22.  
  23. End Interface

 

El nombre del plugin será el que posteriormente se mostrará dentro del menú "Guardar Como" de la herramienta

Como ejemplo se muestra a continuación la implementación del plugin que permite guardar los datos como XML con atributos:

 

VB.NET:
  1. Imports ExportadorFuentes
  2.  
  3. Public Class PersistirXMLAtributos
  4.     Implements IPersistir
  5.  
  6.     Public ReadOnly Property Nombre() As String Implements IPersistir.Nombre
  7.         Get
  8.             Return "XML Atributos"
  9.         End Get
  10.     End Property
  11.  
  12.     Public Sub Escribir(ByVal ficheroSalidaSinExtension As String, ByVal anchoImagen As Integer, ByVal altoImagen As Integer, ByVal separacionEntreCaracteres As Integer, ByVal caracteres As System.Collections.Generic.Dictionary(Of Char, System.Drawing.Rectangle)) Implements IPersistir.Escribir
  13.         Dim w As Xml.XmlTextWriter = Nothing
  14.         Dim c As Char
  15.  
  16.         Try
  17.             w = New Xml.XmlTextWriter(ficheroSalidaSinExtension & ".xml", System.Text.Encoding.UTF8)
  18.             w.Formatting = Xml.Formatting.Indented
  19.  
  20.             w.WriteStartElement("Fuente")
  21.             w.WriteAttributeString("AnchoImagen", anchoImagen.ToString)
  22.             w.WriteAttributeString("AltoImagen", altoImagen.ToString)
  23.             w.WriteAttributeString("SeparacionEntreCaracteres", separacionEntreCaracteres.ToString)
  24.             w.WriteEndElement()
  25.  
  26.             For Each c In caracteres.Keys
  27.                 w.WriteStartElement(c.ToString)
  28.                 w.WriteAttributeString("X", caracteres(c).Left.ToString)
  29.                 w.WriteAttributeString("Y", caracteres(c).Top.ToString)
  30.                 w.WriteAttributeString("Ancho", caracteres(c).Width.ToString)
  31.                 w.WriteAttributeString("Alto", caracteres(c).Height.ToString)
  32.                 w.WriteEndElement()
  33.             Next
  34.  
  35.         Catch ex As Exception
  36.             MsgBox(ex.Message)
  37.         Finally
  38.             If Not w Is Nothing Then
  39.                 w.Close()
  40.             End If
  41.         End Try
  42.     End Sub
  43.  
  44. End Class

 

La dll con el o los plugins debe copiarse en el mismo directorio en que se encuentre el ejecutable de la aplicación.

La herramienta ha sido desarrollada en Visual Basic.NET (VB.NET) y están disponibles para su descarga en la sección de descargas tanto el código fuente como los ejecutables.

La solución cuenta con dos proyectos: el creador o exportador de fuentes y uno para los plugins. El proyecto del exportador o creador de fuentes consta principalmente de dos elementos, el interfaz IPersistir y el formulario que es el que realiza la mayoría de las acciones. El proyecto de los plugins cuenta con cinco clases que implementan otros tantos tipos de formas de exportar los datos.

Heredar de un Singleton

Miércoles 19 de Septiembre de 2007 a las 23:56

En un post anterior ya hablé del patrón de diseño Singleton.

Una de las limitaciones de este patrón es la dificultad de crear una clase singleton base y crear varias clases derivadas o hijas que hereden de ella adquiriendo el comportamiento de la clase Padre, lo cual incluye además del comportamiento como singleton cualquier otra funcionalidad común que se desee añadir.

La dificultad parte del hecho de que si declaramos la variable Instancia como static (en C#) o shared (en VB.NET), dicha variable será compartida por todas las clases derivadas.

El problema de heredar de una clase singleton es que al pertenecer la instancia privada (declarada como shared en VB.NET y como static en C#) a la clase padre será compartida por todas las clases derivadas. Por lo que cuando en cada una de las clases derivadas hagamos el GetInstance obtendremos siempre la misma instancia de la misma clase derivada (que será la que realice el primer GetInstance que es donde se instancia dicha clase.

En este artículo se aporta una posible solución a este problema. Para ello realmente no se utiliza herencia sino Generics, aunque el resultado es similar y se da solución al problema planteado.

Las clases "derivadas" (que como ya hemos comentado realmente no heredan de la clase genérica) el único código adicional al que tuvieran que deben incorporar es declarar un constructor privado para evitar que sean instanciadas con lo que se vulneraría uno de los requisitos básicos del patrón singleton que es que solo puede existir una instancia de la clase.

 

VB.NET:
  1. Public Class SingletonGenerico(Of Tipo)
  2.  
  3.     Private Sub New()
  4.     End Sub
  5.  
  6.     Private Shared _instancia As Tipo
  7.  
  8.     Public Shared ReadOnly Property Instancia() As Tipo
  9.         Get
  10.             If _instancia Is Nothing Then
  11.                 _instancia = CType(Activator.CreateInstance(GetType(Tipo), True), Tipo)             
  12.             End If
  13.  
  14.             Return _instancia
  15.         End Get
  16.     End Property
  17.  
  18. End Class

 

La utilización de una clase "MiClase" que tuviera una propiedad llamada "PropiedadDeMiClase" y que hiciera uso de la clase genérica seria la siguiente:

 

VB.NET:
  1. ConfiguracionGenerica(Of MiClase).Instancia.PropiedadDeMiClase = "¡Funciona!"

 

Punto contenido dentro de un póligono

Lunes 3 de Septiembre de 2007 a las 23:59

Una necesidad muy común en el desarrollo de videojuegos es la de determinar si un punto esta contenido dentro de un polígono irregular. Por ejemplo en el caso de una aventura gráfica es necesario para averiguar si el usuario ha pulsado sobre una determinada región que podría ser un objeto, otro personaje, etc.

Dentro de la librería matemática que he creado se encuentra la clase polígono que implementa dicha funcionalidad. Así como una serie de clases que también son utilizadas en el algoritmo como la clases Vector o la clase Angulo

El algoritmo para determinar si el punto esta incluido dentro del área del polígono es bastante sencillo, pero requiere algunos cálculos.

Inicialmente se une el punto que se quiere averiguar si esta dentro o fuera del polígono con cada uno de los vértices del polígono, con lo cual se obtendrán tantos segmentos como vértices tenga el polígono.
Después hay que ir sumando los ángulos que forman esos segmentos. La suma hay que realizarla en orden, es decir primero sumar el ángulo que forma el segmento del punto al vertice1 con el segmento del punto al vertice2 con el ángulo que forma el segmento del punto al vertice2 con el segmento del punto al vertice3. Y así sucesivamente en orden hasta terminar sumando el ángulo que forma el segmento del último vértice con el del primer vértice.
Si el ángulo total resultante de la suma de ángulos es 0 grados entonces el punto estará fuera del polígono, si es 2PI entonces estará dentro.
Al realizar los cálculos probablemente se acumulen errores de redondeo así que a lo mejor el resultado no es exactamente 0 o 2 PI, por lo que no se debe comparar con 0 o 2PI si no ver si esta cerca de esos valores.

El código del método que determina si el punto pertenece al polígono es el siguiente:

 

VB.NET:
  1. ''' <summary>
  2.     ''' Devuelve si el punto pertenece al poligono
  3.     ''' </summary>
  4.     ''' <param name="pPunto"></param>
  5.     ''' <returns></returns>
  6.     ''' <remarks></remarks>
  7.     Public Function Incluye(ByVal pPunto As Point) As Boolean
  8.         Dim a As New Angulo(0)
  9.         Dim v1 As Vector
  10.         Dim v2 As Vector
  11.         Dim i As Integer
  12.  
  13.         If Vertices.Count <3 Then
  14.             Throw New ApplicationException("El polígono debe tener al menos tres vértices y tiene " & Vertices.Count & " vertices.")
  15.         End If
  16.  
  17.         'Trazamos un vector desde el punto a cada uno de los
  18.         'vertices
  19.         'Calculamos el angulo que forma cada vector con el
  20.         'vector del vertice adyacente (el vector del ultimo
  21.         'vertice formará un angulo con el vector primer vertice)
  22.         'Sumamos todos los angulo
  23.         For i = 0 To Vertices.Count - 1
  24.             v1 = New Vector(pPunto, Vertices(i))
  25.             v2 = New Vector(pPunto, Vertices((i + 1) Mod Vertices.Count))
  26.             a = a + Vector.Angulo(v1, v2)
  27.         Next
  28.  
  29.         'Si el angulo resultante es 360º entones el punto estará
  30.         'dentro del poligono
  31.         'Si el angulo es 0 grados estará fuera.
  32.         'Como se han podido acumular pequeños errores al hacer
  33.         'los calculos establecemos que si el valor absoluto
  34.         'de los grados es menor de 180 esta fuera (realmente
  35.         'será cercano a 0º) y si no dentro (estará cercano a 360)
  36.  
  37.         Return Math.Abs(a.Grados)> 180
  38.  
  39.     End Function

 

El polígono viene dado por una lista de puntos que representan los vértices.

Para determinar el ángulo entre dos vectores utilizamos la siguiente función:

 

VB.NET:
  1. ''' <summary>
  2.     ''' Devuelve el angulo que forman los dos vectores expresado en radianes
  3.     ''' </summary>
  4.     ''' <param name="v1"></param>
  5.     ''' <param name="v2"></param>
  6.     ''' <returns>El angulo expresado en radianes</returns>
  7.     ''' <remarks></remarks>
  8.     Public Shared Function Angulo(ByVal v1 As Vector, ByVal v2 As Vector) As Angulo
  9.         If Vector.ModuloDelProductoVectorialConSigno(v1, v2)> 0 Then
  10.             Return New Angulo(Math.Acos((Vector.ProductoEscalar(v1, v2) / (v1.Modulo * v2.Modulo))))
  11.         Else
  12.             'Si el modulo del producto vectorial es negativo
  13.             'el angulo será negativo
  14.             Return New Angulo(-1 * Math.Acos((Vector.ProductoEscalar(v1, v2) / (v1.Modulo * v2.Modulo))))
  15.         End If
  16.  
  17.     End Function

 

El cálculo del producto escalar de dos vectores y del módulo del producto vectorial es muy sencillo:

 

VB.NET:
  1. ''' <summary>
  2.     ''' Devuelve el producto escalar de dos vectores
  3.     ''' </summary>
  4.     ''' <param name="v1"></param>
  5.     ''' <param name="v2"></param>
  6.     ''' <returns></returns>
  7.     ''' <remarks></remarks>
  8.     Public Shared Function ProductoEscalar(ByVal v1 As Vector, ByVal v2 As Vector) As Integer
  9.         Return ((v1.X * v2.X) + (v1.Y * v2.Y))
  10.     End Function
  11.  
  12.     ''' <summary>
  13.     ''' Devuelve el módulo del vector resultante de hacer el
  14.     ''' productor vectorial de dos vectores
  15.     ''' </summary>
  16.     ''' <param name="v1"></param>
  17.     ''' <param name="v2"></param>
  18.     ''' <returns></returns>
  19.     ''' <remarks></remarks>
  20.     Public Shared Function ModuloDelProductoVectorialConSigno(ByVal v1 As Vector, ByVal v2 As Vector) As Integer
  21.         Return ((v1.X * v2.Y) - (v1.Y * v2.X))
  22.     End Function

 

Singleton más legible y accesible en VB.NET

Jueves 26 de Julio de 2007 a las 23:59

Uno de los patrones de diseño más conocidos y utilizados (si no el que más) es el patrón singleton, que permite garantizar la existencia de una única instancia de una clase evitando la creación de objetos de ese tipo declarando el constructor como privado o protegido. Otra de las ventajas de este patrón es que permite centralizar en un único punto todos los accesos a la instancia.

Una posible implementación en Visual Basic .NET sería la siguiente:

 

VB.NET:
  1. Public Class Singleton
  2.  
  3.     Private Sub New()
  4.     End Sub
  5.  
  6.     Private Shared _instancia As Singleton
  7.  
  8.     Public Shared ReadOnly Property Instancia() As Singleton
  9.         Get
  10.             If _instancia Is Nothing Then
  11.                 _instancia = New Singleton
  12.             End If
  13.  
  14.             Return _instancia
  15.         End Get
  16.     End Property
  17. End Class

 

La forma de acceder a una propiedad de esta clase para asignarle un valor sería:

Singleton.Instancia.MiPropiedad = "miValor"

Esta forma de acceder a una propiedad es un poco artificial y no resulta tan legible o natural como:

Singleton.MiPropiedad = "miValor"

Para conseguir acceder de estar forma a una propiedad de nuestra instancia deberemos crearnos un modulo:

 

VB.NET:
  1. <Global.Microsoft.VisualBasic.HideModuleNameAttribute()> _
  2. Public Module Auxiliar
  3.     Public ReadOnly Property SingletonInst() As Singleton
  4.         Get
  5.             Return Singleton.Instancia
  6.         End Get
  7.     End Property
  8. End Module

 

Ahora para acceder a la propiedad simplemente tendremos que escribir:

SingletonInst.MiPropiedad = "miValor"

Esta solución únicamente es valida en Visual Basic.NET debido a que en C# no existen los módulos.

Detectar los cambios de la IP

Martes 24 de Julio de 2007 a las 23:51

A la hora de contar las visitas que tiene un sitio web es interesante no computar tus propias visitas que en algunos momentos pueden llegar a ser numerosas y que falsean las estadísticas reales. Para evitar esto los sistemas de estadísticas como el statcounter o el Google Analytics cuentan con la posibilidad de no contabilizar las visitas provenientes de una determinada IP.

El problema es que es bastante común tener direcciones IP dinámicas que cambian de vez en cuando, lo que obliga a tener que estar comprobando frecuentemente si tu IP actual coincide con la que estás filtrando en los servicios de estádisticas y si no es así actualizar las IP de los filtros.

En el caso de que se olvide hacer esta comprobación y cambies de IP las estadísticas habrán estado contando todas tus visitas.

Para evitar está situación he creado una pequeña aplicación en Visual Basic.NET que apenas cuenta con media docena de líneas de código y que configurada para que se arranque al iniciar windows realizará la comparación de la IP actual y la anterior IP y en el caso de que sea diferente mostrará un aviso.

En el área de descargas está disponible el ejecutable.

El código de esta pequeña aplicación creada en VB.NET es el siguiente:

 

VB.NET:
  1. Option Strict On
  2. Option Explicit On
  3.  
  4. Module Programa
  5.  
  6.     Sub Main()
  7.         Dim IPs As System.Net.IPHostEntry = System.Net.Dns.GetHostEntry(System.Net.Dns.GetHostName())
  8.  
  9.         If IPs.AddressList(0).ToString <> My.Settings.IP Then
  10.             MsgBox("Ha cambiado la IP de la máquina", MsgBoxStyle.Information, "Atención")
  11.             My.Settings.IP = IPs.AddressList(0).ToString
  12.             My.Settings.Save()
  13.         End If
  14.     End Sub
  15.  
  16. End Module

Dotando a los controles de .NET de un borde personalizado

Miércoles 18 de Julio de 2007 a las 23:57

Sorprendentemente los controles de .NET no admiten apenas estilos de borde. Por ejemplo el control listbox solo permite dibujar el borde de tres formas: hundido, con una línea negra o sin borde.

El intellisense que desarrollé hace tiempo para el editor de código se basaba en un listbox y no tenía una apariencia adecuada ni similar a los del intellisense de los IDES habituales debido al borde que tenía que era el borde hundido que viene por defecto en dicho control.

La primera forma de implementar los bordes que me vino a la cabeza fue crear un control que heredara de listbox, es la forma más lógica de implementarlo debido a lo que se busca es un listbox con cierta funcionalidad adicional. Sin embargo el control listbox no implementa el evento paint y aunque lo hereda de la clase Control nunca se levanta. De igual forma si se sobrescribe el método onpaint tampoco se ejecuta nunca.

La siguiente opción que se me ocurrió fue crear un control de usuario compuesto de un listbox y un picturebox, un panel o similar que contuviera el listbox y que sobresaliera dos pixels por cada uno de los lados del listbox. En esos pixeles sería donde dibujaría los bordes valiéndome del evento paint. Esta opción tiene un importante inconveniente y es que al no heredar de listbox sino de la clase UserControl no implementa todos los métodos, propiedades y eventos de listbox. En este punto habría dos opciones, la fácil que consistiría en hacer publico el listbox. Esta opción rompe con uno de los pilares de la orientación a objetos que es la encapsulación y el código que utilizara el control tendría que hacer utilizar por ejemplo listBoxConBorde.ListboxInterno.SelectedIdex en vez de listBoxConBorde.SelectedIdex que sería lo deseable.
La otra opción sería recubrir las propiedades, métodos y eventos del listbox en el control de usuario, creando en el control de usuario tantas propiedades, métodos y eventos como tenga el listbox. Estos miembros del control de usuario simplemente llamarían a los miembros homónimos del listbox. Esta opción me parece más adecuada que la anterior pero mucho mas tediosa de implementar.

Finalmente opté por una solución que me ha parecido mucho más acertada, sencilla y genérica. Consiste en crear un control llamado "Borde" que dibuje el borde de cualquier control. Hereda de la clase "Control" y consta de dos propiedades:

  • El tipo de borde (que admite los siguientes valores Elevado, ElevadoDobleLinea, Hundido, ElevadoYHundido y HundidoYElevado)
  • El control asociado al que se le dibujará el borde.

Además del código para realizar el dibujo del borde también se encarga de adecuar su tamaño y posición a la del control asociado.

Extender el control para añadir nuevos tipos de bordes con diferentes efectos sería una labor bastante sencilla de realizar.

La nueva apariencia del intellisense estableciendo el borde a ElevadoDobleLinea es la siguiente:


Intellisense con el nuevo borde.
Intellisense con el nuevo borde.


Intellisense."
Intellisense anterior.

 

En la sección de descargas se puede encontrar el código completo en VB.NET del control.

Accediendo a las tablas del sistema de Oracle

Domingo 12 de Noviembre de 2006 a las 23:58

En este artículo vamos a ver como acceder a las tablas del sistema de Oracle, para poder obtener información sobre las bases de datos, por ejemplo las tablas de una base de datos, los campos de los que consta, los procedimientos almacenados, las claves primarias, etc.

Esta información nos puede ser bastante útil por ejemplo para documentar la base de datos de forma automática, para crear los procedimientos almacenados automáticamente en función de los campos de las tablas o cualquier otra utilidad que se nos ocurra.

En el trabajo me he creado un generador de procedimientos almacenados que crea los PA de inserción, modificación, borrado y consulta de cualquier tabla de la base de datos, el cual nos está ahorrando mucho esfuerzo y horas de desarrollo a todo el equipo. La base de datos sobre la que estamos desarrollando la cambian continuamente, por lo que hay que rehacer los procedimientos almacenados cada dos por tres y gracias a está herramienta es automática la actualización de los PA.

A continuación paso a especificar algunas de las consultas a las tablas del sistema que pueden resultar de mayor utilidad:

La consulta que nos devuelve todas las tablas es la siguiente:


SELECT TABLE_NAME FROM USER_TABLES

Para obtener información sobre los campos o columnas de la tabla "NOMBRE_DE_LA_TABLA", incluyendo el nombre del campo, el tipo de datos (incluyendo precisión y escala si procede), si admite nulos y los comentarios que tenga asociados utilizaremos la siguiente consulta:


SELECT COLS.*, COM.COMMENTS
	FROM SYS.ALL_TAB_COLUMNS COLS
	LEFT JOIN SYS.ALL_COL_COMMENTS COM ON COLS.TABLE_NAME = COM.TABLE_NAME AND COLS.COLUMN_NAME = COM.COLUMN_NAME
	WHERE COLS.OWNER = USER AND COM.OWNER = USER AND COLS.TABLE_NAME = 'NOMBRE_DE_LA_TABLA' 

Si queremos obtener los campos que forman la clave principal o primary key (PK) de la tabla "NOMBRE_DE_LA_TABLA" usaremos:


SELECT CONS.TABLE_NAME, COLS.COLUMN_NAME, COMMENTS
	FROM SYS.ALL_CONSTRAINTS CONS, SYS.ALL_IND_COLUMNS COLS
	LEFT JOIN SYS.ALL_COL_COMMENTS COM ON COLS.TABLE_NAME = COM.TABLE_NAME AND COLS.COLUMN_NAME = COM.COLUMN_NAME
	WHERE COLS.INDEX_OWNER = USER AND CONS.OWNER = USER AND COM.OWNER = USER AND CONS.CONSTRAINT_TYPE = 'P' AND CONS.CONSTRAINT_NAME = COLS.INDEX_NAME AND CONS.TABLE_NAME ='NOMBRE_DE_LA_TABLA'

Para obtener la información de todos los triggers de la base de datos, incluyendo su nombre, código fuente del trigger, el evento que lo dispara, la tabla a la que esta asociado, etc:


SELECT * FROM SYS.ALL_TRIGGERS WHERE OWNER = USER 

La siguiente consulta nos devolverá todas las claves foráneas (FK's) de la tabla "NOMBRE_DE_LA_TABLA"


SELECT * FROM  SYS.ALL_CONSTRAINTS
	WHERE OWNER = USER AND TABLE_NAME ='NOMBRE_DE_LA_TABLA' AND CONSTRAINT_TYPE = 'R'

Por último la siguiente solicitud a la base de datos nos devolverá el código fuente de todos los paquetes de la base de datos, incluyendo tanto la cabecera como el cuerpo. Se devuelve un registro por cada línea del paquete.


SELECT * FROM SYS.ALL_SOURCE
	WHERE OWNER = USER AND INSTR(SYS.ALL_SOURCE.TYPE,'PACKAGE') > 0
	ORDER BY NAME, TYPE, LINE

Es importante destacar que en las consultas anteriores al filtrar por una determinada tabla su nombre debe ir en mayúsculas.

El filtro de OWNER = USER lo establecemos para obtener únicamente la información de los objetos de los que somos propietarios.

Creando un analizador semántico

Viernes 29 de Septiembre de 2006 a las 0:52

Entre las funciones de un analizador semántico están las siguientes:

  • Detectar si las variables, constantes y funciones han sido declaradas antes de ser utilizadas.
  • Verificar que las variables, constantes y funciones sean accesibles (visibilidad) desde el ámbito en que son utilizadas.
  • Comprobar que los diferentes identificadores solo hayan sido declarados una vez.
  • Comprobaciones de tipos al evaluar las expresiones. Por ejemplo que no se multiplique un número por una cadena o que la expresión a evaluar en un IF sea del tipo booleano.
  • Verificar que no se intente modificar el valor de una constante.
  • Generar la tabla de símbolos.

El lenguaje de script que estoy desarrollando de momento solo tendrá un ámbito, por lo que no será necesaria la comprobación del ámbito.

Para realizar el análisis semántico utilizaremos una gramática de atributos. Dichos atributos se asociaran tanto a los símbolos terminales como a los no terminales y se propagarán por el árbol sintáctico desde abajo hacia arriba, dando lugar al árbol semántico.
Lo primero que haremos será definir las reglas semánticas, para ello utilizaremos de nuevo la notición BNF a la que añadiremos en pseudocódigo las reglas semánticas.

A continuación se detallan aquellas producciones que constan de reglas semánticas:


10 <DeclaracionVariable> ::= Variable <Tipo> Identificador
	InsertarSimbolo (Variable, Identificador, <Tipo>.Tipo)
11 <Tipo> ::= Numerica
	<Tipo>.Tipo  = Numerica
12 <Tipo> ::= Cadena
	<Tipo>.Tipo  = Cadena
13 <Tipo> ::= Logica
	<Tipo>.Tipo  = Logica
14 <DeclaracionConstante> ::= Constante <Resto_Constante>
	InsertarSimbolo (Constante, Identificador, < Resto_Constante >.Tipo)
18 <Condicional> ::= Si <Expresion> Entonces <Sentencias>  <Resto_Condicional>
	Si <Expresion>.Tipo <> Logica
		Error
21 <Bucle> ::= Mientras <Expresion> Hacer <Sentencias> Fin Mientras
	Si <Expresion>.Tipo <> Logica
		Error
22 <Asignacion_O_LlamadaAMetodo> ::= Identificador <Resto_Asignacion_O_LlamadaAMetodo> 

	Si No Existe (Identificador)
		Error
	Si hijo(1).produccion = 23
		Si es una variable
			Si tipo(identificado) <> hijo(1).tipo
				error
		sino
			Error
	Si hijo(1).produccion = 24
		Recorrer parametros y comprobar que el numero y el tipo de parámetros coincide
23 <Resto_Asignacion_O_LlamadaAMetodo> ::= = <Expresion>
	<Resto_Asignacion_O_LlamadaAMetodo>.Tipo = <Expresion>.Tipo
24 <Resto_Asignacion_O_LlamadaAMetodo> ::= <Parametros>
30 <Expresion> ::= <ExpresionSimple> <Resto_Expresion>
	Si <Resto_Expresion> SeAnula
		<Expresion>.Tipo = <ExpresionSimple>.Tipo
	Sino
		Si <ExpresionSimple>  = <Resto_Expresion>.Tipo
			<Expresion>.Tipo = Logica
		Sino
			Error
31 <Resto_Expresion> ::= <OperadorRelacional> <ExpresionSimple>
	<Resto_Expresion>.Tipo = <ExpresionSimple>.Tipo
32 <Resto_Expresion> ::= ε
39 <ExpresionSimple> ::= <Termino> <Resto_ExpresionSimple>
	Si <Resto_ExpresionSimple>  se anula
		<ExpresionSimple>.Tipo = <Termino>.Tipo
	Sino
		Si <Termino>.Tipo = <Resto_ExpresionSimple>.Tipo
			<ExpresionSimple>.Tipo = <Termino>.Tipo
		Si no
			Error
40 <Resto_ExpresionSimple> ::= <OperadorAditivo> <ExpresionSimple>
	Si <OperadorAditivo> = +
		Si <ExpresionSimple>.Tipo  = Numerica Or <ExpresionSimple>.Tipo = Cadena
			<Resto_ExpresionSimple>.Tipo = <ExpresionSimple>.Tipo
		Sino
			Error
	Si <OperadorAditivo> = -
		Si <ExpresionSimple>.Tipo  = Numerica
			<Resto_ExpresionSimple>.Tipo = <ExpresionSimple>.Tipo
		Sino
			Error
	Si <OperadorAditivo> = O
		Si <ExpresionSimple>.Tipo  = Logica
			<Resto_ExpresionSimple>.Tipo = Logica
		Sino
			Error
41 <Resto_ExpresionSimple> ::= ε
45 <Termino> ::= <Operando> <Resto_Termino>
	Si <Resto_Termino> se anula
		<Termino>.Tipo = <Operando>.Tipo
	Sino
		Si <Operando>.Tipo = <Resto_Termino>.Tipo
			<Termino>.Tipo = <Operando>.Tipo
		Si no
			Error
46 <Resto_Termino> ::= <OperadorMultiplicacion> <Termino>
	Si <OperadorMultiplicacion> = *  Or <OperadorMultiplicacion> = /
		Si <Termino>.Tipo  = Numerica
			<Resto_Termino>.Tipo = Numerica
		Sino
			Error
	Si <OperadorMultiplicacion> = Y
		Si <Termino>.Tipo  = Logica
			<Resto_Termino>.Tipo = Logica
		Sino
			Error
47 <Resto_Termino> ::= ε
51 <Operando> ::= Identificador
	Si Existe(Identificado)
		<Operando>.Tipo = Tipo (Identificador)
	Sino
		Error
52 <Operando> ::= Cte_Numerica
	<Operando>.Tipo = Numerico
53 <Operando> ::= Cte_Cadena
	<Operando>.Tipo = Cadena
54 <Operando> ::= Cte_Logica
	<Operando>.Tipo = Logica
55 <Operando> ::= ( <Expresion> )
	<Operando>.Tipo = Tipo(<Expresion>)

Recorreremos recursivamente el árbol sintáctico en postorden (para cada nodo procesamos recursivamente todos los descendientes primero y luego el nodo). Iremos añadiendo atributos con información al árbol a medida que procesemos nodos. Como el recorrido se realiza en postorden cuando procesemos un nodo ya habremos procesado previamente todos sus descendientes por lo que dispondremos de la información de que nos provean sus atributos.

Añadiremos a la tabla de símbolos todas las constantes y variables que se vayan reconociendo, junto con la información del tipo y el valor (en el caso des la constantes)

La tabla de símbolos será una lista de elementos del tipo símbolo:

VB.NET:
  1. ''' <summary>
  2. ''' Es un simbolo de la tabla de simbolos. Se utiliza en el analizador léxico
  3. ''' </summary>
  4. ''' <remarks></remarks>
  5. Public Class Simbolo
  6.  
  7.     Public Enum ClaseSimbolo
  8.         Variable
  9.         Constante
  10.         Funcion
  11.     End Enum
  12.  
  13.     Private _nombre As String
  14.  
  15.     ''' <summary>
  16.     ''' El nombre del simbolo. Por ejemplo el nombre de una variable tal y como se declara
  17.     ''' </summary>
  18.     ''' <value></value>
  19.     ''' <returns></returns>
  20.     ''' <remarks></remarks>
  21.     Public Property Nombre() As String
  22.         Get
  23.             Return _nombre
  24.         End Get
  25.         Set(ByVal value As String)
  26.             _nombre = value
  27.         End Set
  28.     End Property
  29.  
  30.     Private _clase As ClaseSimbolo
  31.  
  32.     ''' <summary>
  33.     ''' Indica que clase de simbolo es: variable, constante o función
  34.     ''' </summary>
  35.     ''' <value></value>
  36.     ''' <returns></returns>
  37.     ''' <remarks></remarks>
  38.     Public Property Clase() As ClaseSimbolo
  39.         Get
  40.             Return _clase
  41.         End Get
  42.         Set(ByVal value As ClaseSimbolo)
  43.             _clase = value
  44.         End Set
  45.     End Property
  46.  
  47.     Private _tipo As String
  48.  
  49.     ''' <summary>
  50.     ''' Representa el tipo del simbolo: numerica, cadena o logica
  51.     ''' </summary>
  52.     ''' <value></value>
  53.     ''' <returns></returns>
  54.     ''' <remarks></remarks>
  55.     Public Property Tipo() As String
  56.         Get
  57.             Return _tipo
  58.         End Get