Upload
enrique-catala-banuls
View
106
Download
5
Embed Size (px)
Citation preview
HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES
ESPECÍFICOS DE DOMINIO
AUTORES:
ENRIQUE CATALA BAÑULS
VICENTE SORIANO CLAVER
TUTOR:
JUAN CARLOS TRUJILLO MONDEJAR
DEPARTAMENTO:
DPTO. DE LENGUAJES Y SISTEMAS INFORMÁTICOS
CURSO:
2005-2006
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
2
ÍNDICE
ÍNDICE ................................................................................................................ 2
DSL Tools ........................................................................................................... 4
¿Qué son y para qué sirven? .......................................................................... 4 Software Factories y MDA ............................................................................... 4 Proceso de desarrollo ..................................................................................... 5 Modelo de dominio (DomainModel) ................................................................ 6 Diseñador gráfico (Designer) .......................................................................... 8 Plantillas de generación de código ................................................................ 10 Compilación .................................................................................................. 11 Ejecución ....................................................................................................... 11 Ejemplo sencillo ............................................................................................ 12
I. Gestión del proyecto ...................................................................................... 17
Planificación .................................................................................................. 17 Proceso de desarrollo ................................................................................... 21
Diagrama de Gantt .................................................................................... 22 Repositorio .................................................................................................... 23
Gráfico de revisiones ................................................................................. 24 Sincronización con el repositorio ............................................................... 26 Estadísticas de desarrollo .......................................................................... 27 Versiones estables por hito ....................................................................... 29
II. Lenguaje específico de dominio para modelado multidimensional ............... 30
Modelo de dominio ........................................................................................ 30 Esquema conceptual ................................................................................. 30 Hacia un modelo compilable ...................................................................... 33
Atributos embebidos .............................................................................. 33 Relaciones en el modelo ........................................................................ 35 Errores de compilación .......................................................................... 38
Esquema definitivo .................................................................................... 42 Conectores ............................................................................................. 42 La clase DegenerateFact ...................................................................... 44 Conector del DegenerateFact ................................................................ 48 Esquema final del Modelo de Dominio ................................................... 50
Interfaz de usuario ........................................................................................ 52 Clases y formas ......................................................................................... 53 Conectores entre clases ............................................................................ 55 Problemas encontrados ............................................................................. 58
Cambio de iconos en la Toolbox ............................................................ 58 Etiquetas en los conectores (Text Decorators) ...................................... 59 Iconos en las Compartment Shapes ...................................................... 60 Iconos transparentes .............................................................................. 63
Restricciones y validaciones ......................................................................... 63 Archivos de recursos ................................................................................. 64
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
3
Soft Constraints ......................................................................................... 66 Añadir cardinalidades en las relaciones ................................................. 68 Restricciones de la clase Base .............................................................. 71 Restricciones de los conectores del DegenerateFact ............................ 74 Atributos derivados ................................................................................ 75
Hard Constraints ........................................................................................ 77 AggregationConnector ........................................................................... 79 AssociationConnector ............................................................................ 81 BaseAssociatesBaseConnector ............................................................. 84 BaseInheritsBaseConnector .................................................................. 86 DegenerateFactConnector ..................................................................... 88 Library .................................................................................................... 90
Mensajes de error ...................................................................................... 93 Lista de errores original (modelo de la Universidad de Alicante) ........... 93 Lista de errores del proyecto .................................................................. 96
Menú de comandos ..................................................................................... 102 Cambios en el CommandSet ................................................................... 103 Ficheros embebidos como recursos ........................................................ 105 Cuadros de diálogo.................................................................................. 110
Modelo Estrella .................................................................................... 111 Modelo SnowFlake............................................................................... 114 Modelo Oracle WarehouseBuilder ....................................................... 119
III. Generación de código. ............................................................................... 121
Modelo de generación de código ................................................................ 121 Introducción ............................................................................................. 121 Descripción del proceso de procesamiento de los diagramas OOMM .... 123 Tipos de datos implicados en la generación de código ........................... 135
Enumeración MotorBBDD .................................................................... 135 Clase TClaveAjena .............................................................................. 136 Clase MetodosBase ............................................................................. 138 Clase Validacion .................................................................................. 141 Clase Tcolumna ................................................................................... 152 Clase TTabla ........................................................................................ 153 Clase TTablaMultidimensional ............................................................. 174
Especificación del motor de BBDD destino de la generación de código . 184 Extensión de nuevos motores de BBDD destino ..................................... 185
Modelo de validación del generador de código ........................................... 189 Generación de código ................................................................................. 190
Generación de código SQL Estrella......................................................... 195 Generación de código SQL Snowflake .................................................... 204 Generación de código Oracle Warehouse Builder ................................... 216
Detector de palabras reservadas ................................................................ 223 Abstracción de tipos de datos ..................................................................... 225
BIBLIOGRAFÍA ............................................................................................... 228
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
4
DSL Tools
¿Qué son y para qué sirven?
Un Lenguaje Específico de Dominio (Domain-Specific Language, o DSL) es un
lenguaje diseñado para realizar una determinada tarea o para resolver un problema
específico, en contraste con los lenguajes de propósito general (C#, Java…). Al usar
lenguajes específicos de dominio, se pueden construir herramientas de modelado
personalizadas y, básicamente, diseñar un nuevo lenguaje de modelado e implementarlo de
una forma muy sencilla. Por ejemplo, un lenguaje específico puede usarse para describir
una interfaz de usuario, un proceso de negocio, una base de datos o un flujo de
información, y después, a partir de estas descripciones, ser usado para generar código.
Las Domain-Specific Language Tools (a partir de ahora, DSL Tools) o
herramientas para construir DSLs, pueden ser usadas para construir herramientas de diseño
personalizadas, adaptadas a cualquier problema. Por ejemplo, se puede crear una
herramienta de modelado para un proceso de negocio usando las DSL Tools, para describir
así determinados conceptos de cómo funcionan los modelos de negocio de tu organización.
Si estás construyendo una herramienta para representar diagramas de estados, puedes
describir qué es un estado, las propiedades que tiene un estado, qué tipos de estados
existen, cómo están definidas las transiciones entre estados, etc. Un diagrama de estado
usado para describir el estado de los contratos en una compañía de seguros y otro para
especificar la interacción del usuario entre las páginas de un sitio Web son
superficialmente similares, pero los conceptos subyacentes que representan son totalmente
distintos. Creando una herramienta de diseño personalizada, se puede especificar
exactamente la definición de los conceptos del diagrama de estado que se necesitan para
dicha herramienta.
Software Factories y MDA
En los últimos años, han surgido dos principales metodologías que siguen el
paradigma de desarrollo software dirigido por modelos: la Model Driven Architecture
(MDA) y las Software Factories. La MDA es una propuesta del Object Management
Group (OMG) y es una de las aproximaciones más divulgadas en la comunidad científica
por su estado de madurez. Por otro lado, una aproximación más reciente y que está
teniendo un gran impacto es la denominada Software Factories, propuesta por Microsoft.
El término MDA se refiere a un enfoque sobre el desarrollo dirigido por modelos
basado en el uso de tecnologías de modelado del OMG, haciendo especial hincapié en el
Lenguaje de Modelado Unificado (UML - Unified Modeling Language) y en el Servicio
para Meta-Objetos (MOF – MetaObjects Facility). La esencia de MDA consiste en hacer
una distinción entre los modelos de plataforma independiente (PIMs – Platform
Independent Models) y los modelos de plataforma específica (PSMs - Platform Specific
Models). Para desarrollar una aplicación con MDA es necesario primero construir un PIM
de la aplicación, luego transformarlo a un PSM usando un mapeado estandarizado para así,
finalmente, obtener de este último el código de la aplicación.
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
5
Las fábricas de Software utilizan conocimientos de dominio específicos,
arquitecturas de solución, herramientas y otros activos reutilizables para ayudar a sus
usuarios a producir tipos específicos de soluciones de software. Una fábrica de Software se
basa en tres puntos claves:
Un sistema para la fábrica de software.
Una plantilla para la fábrica de software
Un entorno de desarrollo extensible.
La fábrica de software configura el entorno de desarrollo extensible, como por
ejemplo Eclipse, Borland Jbuilder o Microsoft Visual Studio Team System (VSTS),
utilizando un paquete de instalables llamado plantilla de la fábrica de software o paquete
de instrucciones. Si se configura de esta forma, el entorno de desarrollo se vuelve una
utilidad especializada que acelera el desarrollo de un tipo específico de solución de
software, como una interfaz del usuario o una capa de acceso a una base de datos, o tal vez
toda una aplicación en un dominio empresarial como por ejemplo el cuidado de la salud o
la seguridad nacional.
La plantilla de la fábrica de software se organiza por medio de un modelo llamado
sistema de la fábrica de software. El sistema define uno o más puntos de vista que son
relevantes para las partes interesadas en la producción de la solución de software deseada.
Cada punto de vista define artefactos del ciclo de vida producidos o consumidos por los
interesados, las actividades que ellos realizan con estos artefactos y los bienes reutilizables
disponibles para soportarlos al realizar estas actividades. La metodología de la fábrica de
software integra el Desarrollo Orientado por un Modelo (MDD – Model Driven
Development), el Desarrollo Basado en el Componente (CBD – Component-Based
Development) y las prácticas de desarrollo ágil, incluyendo el uso de patrones y lenguaje
de patrones con modelos, marcos y Herramientas (Ver “Recursos”).
Para nivelar los modelos de forma efectiva para las varias formas de
automatización, las fábricas de software hacen gran uso de los lenguajes específicos de
dominio. La tecnología DSL es mucho más nueva que varias de las otras tecnologías
utilizadas en las fábricas de software, y depende de familias de lenguajes extensibles. Sin
embargo, los marcos y herramientas de creación del DSL han estado en desarrollo por
algún tiempo en los grupos académicos, y han comenzado a aparecer recientemente en
forma comercial (como las DSL Tools).
Proceso de desarrollo
Para llevar a cabo un proyecto guiado por las DSL Tools hay que tener en cuenta un
proceso de desarrollo necesario para cada nuevo proyecto. Dicho proceso no es secuencial,
hay una serie de pasos que se repiten y a los que se vuelve atrás a fin de ir avanzando en el
desarrollo del modelo. Dichos pasos se pueden esquematizar de la siguiente forma:
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
6
Lo primero es crear un nuevo proyecto DSL, seleccionando File, New, Project…, y
en la parte de Other Project Types, Extensibility buscar el Domain Specific Language
Designer.
El cuadro central se refiere a la creación del lenguaje de dominio. Se compone de
dos partes, la definición del modelo de dominio y la del diseñador gráfico. Ambas partes se
realizan de forma paralela, se debe estar sincronizándolas conforme avanzamos para crear
así un lenguaje válido.
Cada vez que creamos un doblete Modelo de dominio – Diseñador Gráfico
consistente, debemos después transformar los templates, depurar y/o ejecutar el código,
modelar el nuevo lenguaje creado en la ventana de depuración del Visual Studio y, por
último, escribir las plantillas de generación de código para ese lenguaje.
Una vez acabado se puede volver a definir el DSL, depurar, modelar y generar
código, así hasta que se logre el lenguaje específico de dominio que se anda buscando.
Modelo de dominio (DomainModel)
Un proyecto dirigido por las DSL Tools se divide en dos partes fundamentales,
aunque muy relacionadas. La primera de ellas consiste en crear un modelo del lenguaje o
metamodelo. De forma esquemática, se pretende mostrar el funcionamiento que va a tener
nuestro lenguaje específico de dominio. Para ello se utilizan conceptos ya conocidos, como
relaciones, herencia, clases, propiedades… El fichero que guarda este modelo es el
DomainModel.dsldm, contenido en el proyecto DomainModel.
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
7
La figura básica del modelo es la clase. Con las clases podemos representar
cualquier elemento significativo de nuestro lenguaje (un diagrama, un cuadro de diálogo de
un asistente, un atributo de otra clase…). Dichas clases pueden estar compuestas de
propiedades. Estas propiedades pueden ser de los tipos de datos más comunes: String,
Boolean, Int32, Int64, Double… También se pueden definir tipos de datos enumerados y
tipos de datos simples. Para visualizar las propiedades de una clase, debemos desplegar el
símbolo + que se encuentra más a la derecha de la clase. Tanto clases como propiedades se
dibujan arrastrando los elementos Class y Value Property de la Toolbox.
La clase que aparece con una X significa que es la raíz del modelo, y es de donde
deben derivar el resto de clases. Si una clase no está relacionada con la clase raíz no podrá
mostrarse posteriormente en la pantalla del diseñador.
Tenemos tres tipos de relaciones entre clases:
- Embedding: relaciones embebidas. Significa que una clase contiene a otra, o
que un concepto está formado por otro. Normalmente se suele utilizar para
establecer atributos dentro de las clases (ya que es necesario que sea una
Embedding relationship para que luego se pueda representar gráficamente de
esa manera). Representada por una línea continua.
- Reference: referencias entre clases. Simplemente es una relación entre dos
conceptos (A se relaciona con B). Representada por una línea discontinua.
- Inheritance: relaciones de herencia entre clases. Aquí existe una clase padre, y
una clase hija que hereda las propiedades del padre (y que contendrá además las
suyas propias). Se representa por una flecha que apunta al padre.
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
8
Todas ellas menos la relación de herencia se pueden representar a parte en el
esquema seleccionando la clase con el botón derecho y dándole a la opción “Show As
Class”. Las clases que representan relaciones se muestran en color rojo oscuro. Así le
podemos asignar también propiedades a las relaciones e incluso relacionarlas con otras
clases.
El triángulo y el rectángulo encima de las relaciones indican los roles y las
cardinalidades en ambos sentidos. Para el triángulo el sentido en que se lee es el que
indica, de izquierda a derecha, y para el rectángulo el sentido contrario. El texto que
aparece encima de la relación pertenece al nombre del role del triángulo. Para saber el
nombre del role rectángulo y el de la relación hay que seleccionarlos y mirar en la ventana
de propiedades.
Poniendo como ejemplo la figura anterior y las distintas cardinalidades que le
podemos asignar al role ‘Pages’, representado como el triángulo en la imagen, vamos a
explicar el significado de cada cardinalidad y cómo obtenerla (valores max y min en la
ventana de propiedades):
- 1 (max=1, min=1) Una PageFlow debe tener exactamente una única Page.
- 0 (max=1, min=0) Una PageFlow puede tener como mucho un Page (esto es,
o 0 o 1).
- + (max=0, min=1) Una PageFlow debe tener 1 o más Pages.
- * (max=0, min=0) Una PageFlow puede tener 0, 1 o más Pages.
Este significado se puede intuir con las relaciones máximo-mínimo. Para el caso de
0 y 1 es tal y como se supone: max y min expresan sus correpondientes valores máximo y
mínimo. Para el caso de + y * es igual si pensamos que max=0 tiene el sentido de muchos.
Diseñador gráfico (Designer)
La segunda parte de la que se compone un proyecto con las DSL Tools es la del
diseñador gráfico. Es la parte que relaciona los elementos del Domain Model con sus
correspondientes en el entorno gráfico. El fichero principal que contiene todas las
definiciones es el Designer.dsldd, contenido en el proyecto Designer.
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
9
Dicho fichero contiene el código XML con todas las definiciones necesarias. Pero
resulta algo complicado editarlo, porque la documentación es escasa y el código
complicado. Afortunadamente una empresa (Modelisoft) creó una aplicación llamada
DslDm2Dd para sincronizar el DomainModel.dsldm con el Designer.dsldd de forma
gráfica y de manera muy intuitiva. Al instalarse la herramienta se puede arrancar
directamente desde el entorno de Visual Studio seleccionando Tools/DslDm->Dd.
A la izquierda se encuentran los elementos del Domain Model, y a la derecha sus
correspondientes en el Designer. Las rayas rojas indican los que están relacionados entre
sí. Si existe alguna incompatibilidad, aparecerá un mensaje de aviso en la ventana de abajo.
Normalmente las clases en el Domain Model se corresponden con las Shapes en el
Designer, y las RelationShip con los Connectors. En el ejemplo de la figura, ExampleClass
se corresponde con ExampleShape; y ExampleRelation con ExampleConnector.
Para editar las Shapes o los conectores basta con seleccionarlos y darle al botón
derecho y ‘Edit’. Si tenemos un elemento en el Domain Model que aun no está relacionado
con nada, para crear una nueva Shape o Connector habrá que seleccionarlo y arrastrarlo
hasta la raíz de la parte del Designer. Enseguida aparecerá el asistente que nos ayudará en
el proceso.
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
10
Si una clase está embebida por otra, poder mostrar en el Designer esa clase como
un atributo de la segunda. Para ello, al editar la clase principal hay que seleccionarla como
Compartment Shape, y en la parte donde te piden los Compartments of the shape, añadir
un nuevo compartment dándole al símbolo + y seleccionar la clase del desplegable
melCollection Expression.
Una vez acabado la edición, hay que guardar los cambios, cerrar la aplicación y
volver al entorno del Visual Studio.
Plantillas de generación de código
Los ficheros del DomainModel.dsldm y el Designer.dsldd una vez definidos son
utilizados para generar otro código. Ese es el código que será la base de nuestro lenguaje
de dominio. Para generarlo y saber si hemos hecho todo correctamente, debemos darle al
botón que se encuentra a la derecha de la ventana del Solution Explorer llamado
“Transform All Templates”. Si no aparece ningún error, el Domain Model y el Designer
estarán sincronizados correctamente.
También existe otro tipo de ficheros que generan código. Son aquellos que
podemos encontrar con extensión *.dslddt para el proyecto del Designer y *.dsldmt para
los del Domain Model. Son ficheros templates, y si nos fijamos todos ellos tienen un
archivo anidado, que corresponde al código que generan. Para generar dicho código se
puede seleccionar el archivo de generación con el botón derecho y pinchar en “Run
Custom Tool”. Esta Custom Tool está especificada en la ventana de propiedades del
archivo, normalmente con valor TextTemplatingFileGenerator. Estos ficheros también
generan su código cuando le damos al botón “Tranform All Templates”.
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
11
Del mismo modo se pueden definir templates que generen código para el lenguaje
específico de dominio final. Se pueden añadir en el proyecto destino, y utilizarlos de la
misma manera que los anteriores. Pueden llevar extensiones como *.t4 o
*.ReportTemplate.
Compilación
Una vez creado el lenguaje DSL y generados todos los templates correctamente,
pasamos a depurar o ejecutar el código. Para ello no hay más que darle a F5 o al botón en
forma de rectángulo verde en el entorno del Visual Studio. Al lado de dicho botón
podemos seleccionar si queremos ejecutarlo en modo depuración o en modo release.
Normalmente cuando se desarrollo un proyecto se ejecuta siempre en modo depuración. El
release se usará cuando se esté desarrollando el instalador, para la versión final del
proyecto.
El hecho de que hayamos generado todos los templates correctamente no significa
que el modelo sea correcto. Podemos encontrarnos errores debidos a otras circunstancias,
como por ejemplo que hayamos introducido algunos nombres con espacios en blanco que
posteriormente son transformados en variables. Habrá que estar atentos a los mensajes que
nos aparezcan en la ventana de errores para poder solucionarlos.
Ejecución
Cuando conseguimos ejecutar el proyecto DSL, surgirá una nueva instancia de
Visual Studio con un nuevo proyecto abierto, que corresponderá a nuestro lenguaje de
dominio creado. En la ventana de la solución veremos los archivos incluidos en él.
Algunos deben tener la extensión que decidimos que tendrían los archivos que
representarían el nuevo lenguaje al crear el proyecto DSL. Si abrimos algunos de estos
archivos, aparecerá la ventana del diseñador y un menú a la izquierda con las herramientas
que podemos incluir en el: formas, conectores, etc.
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
12
Para modelar simplemente habrá que arrastrar estos elementos de la Toolbox al
campo de trabajo. Dependiendo de las cardinalidades y las restricciones que hayamos
definido, se podrán conectar o no los elementos entre sí. Del mismo modo, podremos
encontrarnos con errores de modelado si así se ha especificado con anterioridad.
Al acabar de modelar, se podrá generar código a partir de nuestro modelo con
aquellos archivos incluidos en el proyecto destinados para ese fin dándole al botón de
Transform All Templates.
Ejemplo sencillo
Vamos a ver de forma rápida como crear un lenguaje específico de dominio para
modelar tablas relacionales y generar su código sql correspondiente.
Creamos un nuevo proyecto DSL Designer con nombre “LenguajeRelacional” a
partir del Minimal Language. Le ponemos como nombre del lenguaje también
“LenguajeRelacional”, el Namespace “UA.EjemploDSL.LenguajeRelacional” y la
extensión “rlc”.
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
13
Abrimos el fichero DomainModel.dsldm. La raíz del proyecto se llamará
ModeloRelacional, estará formado por Tablas que a su vez estarán formadas por Atributos
y por una Clave Primaria. Las tablas podrán relacionarse entre sí por claves ajenas.
Para crear el modelo de dominio empezamos haciendo un “Replace All” de
ExampleModel por ModeloRelacional, y otro de ExampleClass por Tabla. Reemplazamos
los nombres necesarios en clases y conectores, y añadimos las relaciones embebidas para
Atributo y Clave Primaria. Hay que asegurarse de que el rol triángulo de las relaciones
embebidas tenga su propiedad Accepts a “All”. También le quitamos a Tabla la propiedad
ExampleProperty y añadimos a Atributo la propiedad Tipo.
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
14
Le damos al botón de “Transform All Templates” y ejecutamos el DslDm2Dd para
sincronizar con el diseñador. Editamos el ExampleShape y el ExampleConnector a fin de
cambiarles el nombre y sincronizarlos con Tabla y RelacionClavesAjenas. TablaShape
será una CompartmentShape, que tendrá embebidas las clases Atributo y Clave Primaria.
Salvamos el fichero y volvemos al Visual Studio. Volvemos a darle al botón de
“Transform All Templates” y depuramos. Si todo ha ido bien, se abrirá una nueva ventana
de Visual Studio donde podremos modelar tablas relacionales.
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
15
Además, podemos incluir un fichero en el proyecto final que genere código sql para
cualquier modelo que dibujemos. Para ello le damos al botón derecho en el proyecto
LenguajeRelacionalDebugging y escogemos Add.. NewItem. Le llamamos
GeneraSQL.ReportTemplate y añadimos el siguiente código:
<#@ template inherits=
"Microsoft.VisualStudio.TextTemplating.VSHost.ModelingTextTransformation"#>
<#@ output extension=".sql" #>
<#@ ModeloRelacional processor="LenguajeRelacionalDirectiveProcessor"
requires="fileName='Test.rlc'" provides="ModeloRelacional=ModeloRelacional"
#>
<# foreach(Tabla tabla in this.ModeloRelacional.Elementos)
{
#>
Create Table <#=tabla.Name#>
(
<#
foreach(Atributo atributo in tabla.atributos)
{
#> <#=atributo.Name#> <#=atributo.Tipo#>
<#
}
foreach(ClavePrimaria cp in tabla.cp)
{
#>
PrimaryKey <#=cp.Name#>
<# } #>)
<# }
#>
Añadimos en la propiedad Custom Tool del fichero el texto
“TextTemplatingFileGenerator” y así veremos como se crea un fichero llamado
GeneraSQL.sql con el siguiente código:
Create Table Tabla1
(
Atributo1 String
Atributo2 Int
PrimaryKey CP1
)
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
16
Create Table Tabla2
(
Atributo3 Float
PrimaryKey CP2
)
Que se generará automáticamente a partir del modelo contenido en el fichero
“Test.rlc”.
Finalmente, y tan solo como nota aclaratoria, hemos de decir que en este apartado
no se ha pretendido plasmar el modelo relacional por completo. Simplemente lo hemos
utilizado como ejemplo para mostrar las posibilidades que nos ofrecen las DSL Tools.
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
17
I. Gestión del proyecto
Planificación
Para la planificación del proyecto hemos utilizado Microsoft Proyects puesto que al
ser dos personas no necesitábamos sistemas de planificación más avanzados y porque nos
ofrecía lo que necesitábamos, que era la planificación de requerimientos entre tareas, la
asignación de requisitos para llevarlas a cabo y la planificación de fechas. Esto añadido a la
posibilidad de realizar diagramas de gantt de forma automática nos hizo decidirnos.
Pese a que realizamos un primer estudio de la planificación de tareas que tenían que
ser llevadas a cabo, a medida que avanzábamos en los conocimientos sobre las DSL Tools
nos dimos cuenta que había que cambiar la planificación para adaptarla a la forma de
programación del modelo y la generación de código. De esta forma podremos ver mas
adelante como algunas de las tareas planeadas en un principio, como la fase de generación
de código, cambiaron radicalmente al estudiar la forma en la que teníamos que utilizar las
DSL Tools para generar código a partir de nuestros diagramas. De la misma forma,
pasamos de tener prevista la finalización del proyecto el día 16 de Mayo al día 26 de Mayo
como al final vimos que sería mas factible. Esta última fecha del día 26 de Mayo fue la
que finalmente cumplimos puesto que dimos por acabado el proyecto ese mismo día,
pese a que no teníamos finalizado el proyecto de instalación del pluggin, cosa que no
habíamos contemplado en la planificación por ser algo extra.
Vamos a ver un resumen de las 39 tareas en las que subdividimos el proyecto, para
hacernos una idea algo mas general de lo que tuvimos que llevar a cabo para su desarrollo,
para ello, adjuntamos la tabla de tareas realizadas del proyecto a día 28/06/2006, cuando ya
habíamos terminado la fase principal del proyecto, pero aún no teníamos implementado el
instalador de la aplicación:
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
18
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
19
En ella podemos ver de forma general, las 34 tareas que ya teníamos
implementadas, así como los tiempos de desarrollo que invertimos en cada una de ellas.
Ahora pasamos a mostrar el desglose de la planificación, atendiendo a los recursos
que utilizamos, que fuimos tanto vicente como enrique, puesto que fuimos los dos únicos
programadores del pluggin.
Como recursos, a parte de nosotros dos, hacemos referencia a tres archivos
llamados End-End.WizardUIPGuide.doc, Example.ValidationAndConstraints.doc y
Example.DSLCustomizations.doc puesto que fueron tenidos en cuenta en los comienzos del
desarrollo para estudiarnos el modelo, funcionamiento y programación de las DSL Tools.
No se ha tenido en cuenta como recurso por otra parte, la información relativa al
modelo multidimensional orientado a objeto que modela nuestra aplicación, puesto que eso
fue algo que tuvimos que aprendernos antes de llevar a cabo el proyecto y no hemos creído
conveniente incluir en los tiempos algo que se supone teníamos que saber para poder
realizar la herramienta CASE.
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
20
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
21
Proceso de desarrollo
Para llevar a cabo un proyecto guiado por las DSL Tools hay que tener en cuenta un
proceso de desarrollo necesario para cada nuevo proyecto. Dicho proceso no es secuencial,
hay una serie de pasos que se repiten y a los que se vuelve atrás a fin de ir avanzando en el
desarrollo del modelo. Dichos pasos se pueden esquematizar de la siguiente forma:
El proceso de desarrollo usando DSL Tools, como podemos ver en el diagrama
anterior, es un proceso incremental e iterativo. El proceso de desarrollo comienza con la
creación de un nuevo proyecto Domain Specific Language Designer, que no es mas que el
diseñador gráfico que representará nuestro modelo y luego a partir de el será cuando
comencemos a programar la generación de código, la cual podemos ver representada
mediante la transformación de templates a la generación de código.
Pese a que en la planificación del proyecto que nos hicimos, podemos ver un una
planificación en cascada, puesto que vamos haciendo una cosa cuando acabamos la otra, el
modelo del lenguaje de dominio se ha realizado usando una metodología iterativa puesto
que una vez teníamos una versión estable, volvíamos al principio para añadir funcionalidad
y mejorar el modelo.
No nos ha hecho falta realizar muchas iteraciones completas puesto que el modelo
estaba muy bien especificado y pudimos realizar cada paso prácticamente conforme lo
teníamos previsto, pero eso no quita que lo normal sea tener un proceso iterativo en la
definición del modelo de dominio.
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
22
Diagrama de Gantt
A continuación se muestra el diagrama de Gantt de todo el proceso de desarrollo del proyecto, incluyendo la fase 1 de preparativos en
la que estuvimos investigando el funcionamiento de las DSL Tools.
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
23
Repositorio
Decidimos montar un sistema de control de versiones para poder controlar el
proyecto correctamente y no tener sobresaltos ante perdidas de información y sobre todo
porque podemos tener el control del código siempre que queramos, pudiendo volver a
versiones previas del desarrollo si llegábamos a algún punto de “no retorno” al ir
programando.
Las 3 opciones que barajamos eran:
CVS
Subversión
Visual SourceSafe
La tercera fue inmediatamente descartada a pesar que es la que nos propone por
defecto la herramienta Visual Studio 2005, puesto que su modelo de programación “en
exclusiva” no permitía trabajar simultáneamente a los desarrolladores en los mismos
archivos, quedando bloqueados siempre que los editaba alguien. Esto era un problema
puesto que al trabajar físicamente dispersos, no podíamos estar pendientes el uno del otro
en cada momento.
CVS era una buena candidata al principio por tratarse de un sistema de control de
versiones maduro y estable pero le faltaba algo, y este algo era que tiene la pega de que
cada uno de los cambios que se realizaban se subían completamente al repositorio y esto
quiere decir que si tenemos 1000 versiones distintas del mismo archivo con pequeños
cambios, tenemos 1000 archivos idénticos pero con pequeños cambios entre ellos y que
ocupan prácticamente lo mismo. Teniendo en cuenta que trabajaríamos con ficheros de
documentación de varios megas, .dll´s que podrían ocupar cientos de kilobytes,…esto
supondría una ocupación de disco bastante amplia para el servidor donde lo teníamos
montado (un servidor propiedad de Enrique Catalá) que tenia disco limitado.
Nos decantamos finalmente por Subversión por disponer de todo lo que
necesitábamos además de tratarse del nuevo sistema de control de versiones que se está
imponiendo en la comunidad de Software Libre para desarrollos distribuidos y que como
principal ventaja era que las actualizaciones de archivos no eran completas, sino que se
actualizaba en el repositorio única y exclusivamente la porción modificada. Incluso los
archivos de .doc funcionaban de esta forma (no siendo el caso de los archivos binarios
como imágenes, zip,…) y esto nos aseguraba actualizaciones mas rápidas, menos tráfico de
red y menos consumo de espacio en disco del servidor.
Finalmente instalamos subversión sobre un servidor con Sistema Operativo Ubuntu
Hoary.
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
24
Gráfico de revisiones
A continuación se muestra el gráfico de revisiones que se ha ido formando a
medida que realizábamos el proyecto. Podemos ver como en cada uno de los hitos
conseguidos, realizábamos una nueva rama estable que almacenábamos en una carpeta de
backup dentro del repositorio.
Cada uno de los hitos está marcado en color gris. Cada vez que cumplíamos un hito
hacíamos un “fork” sobre el proyecto en desarrollo que almacenábamos ( para nunca más
tocar ) en una subcarpeta denominada “Backups\Versiones Estables” y que podemos ver
gráficamente mediante los recuadros de color verde.
Si nos fijamos, la ruta principal del proyecto es
/ProyectoDSL/ObjectOrientedMultidimensionalModel, mientras que la ruta de los backups
de los hitos es /ProyectoDSL/Backups/Version X/ObjectOrientedMultidimensionalModel
Hemos de resaltar que la Revisión 208 podemos ver que es eliminada en la revisión
214 porque lo que se almacenaba no consideramos conveniente tomarlo como un hito,
puesto que se trataba de la prueba de concepto de embeber los ficheros de generación de
código en la dll del proyecto, pero que no funcionaba aún por lo que decidimos eliminarla.
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
25
Podemos observar si vemos el gráfico, que hay una etiqueta de texto explicativa
que se corresponde a la Revisión 225 ( versión 0.4 y última ), donde podemos ver que el 10
de Mayo de 2006 dimos por finalizada la tarea 30 que se correspondía con el paso de
embeber los ficheros de generación de código en la .dll cumpliendo la planificación que
nos marcamos y que podemos ver en el diagrama de Gantt.
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
26
Sincronización con el repositorio
Vamos a mostrar la pantalla que hemos estado utilizando para controlar cada una de las revisiones que realizábamos. Esta pantalla
forma parte del programa TortoiseSVN (cliente de Subversión para Windows), que puede ser descargado de la Web
http://tortoisesvn.sourceforge.net/downloads totalmente gratuito y con licencia GPL.
La pantalla consta de 3 partes bien diferenciadas:
1. Podemos ver información sobre cada una de las revisiones, fecha, autor, mensajes que utilizábamos para describirla,..
2. En la segunda podemos ver el comentario que realizábamos en cada actualización
3. En la tercera los archivos modificados, añadidos o eliminados que han intervenido en la revisión.
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
27
Estadísticas de desarrollo
A continuación vamos a comentar algunos datos del proceso de desarrollo relativos
a los dos programadores que intervinimos en el.
En primer lugar vemos una pantalla que incluye las 9 semanas de desarrollo de
proyecto única y exclusivamente, sin incluir las de documentación ni el instalador.
*Grafico 1: No incluye las semanas de documentación, solo desarrollo
En la siguiente captura (Gráfico 2), podemos ver un gráfico que compara a los dos
desarrolladores en cuanto a confirmaciones por semana.
*Grafico 2: Semanas de desarrollo de proyecto, sin documentación del mismo
El primer pico de vicente se corresponde con los primeros pasos con el interfaz de
diseño y podemos ver como enrique esta algo inactivo puesto que el se encarga del proceso
de generación de código.
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
28
Una vez ya se obtuvo una primera versión de la interfaz de usuario, podemos ver
como enrique comienza el desarrollo de la generación de código exhaustivamente,
quedando vicente en un estado aletargado en el que se dedicaba a añadir y mejorar
gráficamente la interfaz (recordemos el proceso iterativo comentado antes) pero que no
podía continuar hasta que enrique no acabara con el modelado de la API de generación de
código.
Una vez enrique definió el estándar de generación de código, vicente ya pudo
ponerse con todo el proceso de generación de código mediante eventos de ratón, sacando
ventanas que preguntaban sobre lo que el usuario quería hacer y extendiendo las
posibilidades de la interfaz gráfica,…
El gráfico siguiente (Grafico 3) ya muestra las últimas semanas de proyecto en las
que vicente esta más activo puesto que se ha encargado del proyecto del instalador del
pluggin para Visual Studio.
*Grafico 3: Desarrollo completo incluyendo documentación del proyecto.
Acabaremos diciendo que las estadísticas son meramente informativas, no tiene que
ver con que un desarrollador haya realizado mas proyecto que otro, puesto que estamos
hablando de confirmaciones contra el repositorio y estas se suelen hacer al final del día y
ante cada desarrollo de código satisfactorio pero obviamente podemos estar en un proceso
de investigación, depuración, etc. y esto no se vería reflejado en estas estadísticas.
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
29
Versiones estables por hito
Existen 4 versiones estables en diversos hitos del proyecto que hemos marcado
como funcionales en la carpeta de backup y que se corresponden con las cuatro ramas que
podemos observar en el gráfico de revisiones antes mencionadas. Estos cuatro hitos son:
Version 0.1:
Primera versión compilable.
Permite dibujar Fact, Dimension y Base, pero no permite relaciones ni nada aún.
Version 0.2:
Version compilable.
99% de diseñador y comenzada la generación de código.
Se permite generar código simple de create tables, sin cuerpo aún.
Version 0.3:
Version compilable.
En esta versión ya se genera SQL Estrella al 100% y se ha comenzado a programar
la generación SQL snowflake.
Version 0.4:
Version 0.4 estable y funcional.
Soporta botón derecho para generar código y el 99% de los errores al diseñar te los
muestra.
100% de generación de código.
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
30
II. Lenguaje específico de dominio para modelado multidimensional
En esta parte explicamos como se ha trabajado en la creación de un lenguaje
específico de dominio con el fin de poder modelar almacenes de datos multidimensionales.
Trataremos de manera más concreta la interfaz de usuario y los pasos que hemos tenido
que dar para lograr el modelo final. En un primer momento tuvimos que definir el esquema
conceptual del modelo de dominio, tal y como ha sido expuesto en el capítulo referido a las
DSL Tools. Los primeros modelos no fueron satisfactorios, daremos las razones de ello y
cómo lo solucionamos hasta llegar al esquema definitivo.
El siguiente paso, aunque realmente se ha sido realizando de forma paralela a la
creación del modelo de dominio, fue la realización del modelo gráfico. Daremos una breve
descripción de los pasos para desarrollarlo, pero explicaremos en más profundidad el
resultado final, las clases, formas y conectores que lo componen.
A continuación era necesario establecer las restricciones y validaciones sobre
nuestro modelo, para así darle una semántica más fuerte. Hablaremos de los archivos de
recursos, que nos han servido para organizar los mensajes de error y otros recursos
necesarios, los dos tipos de restricciones que se han desarrollado: hard constraints y soft
constraints; y, por último, la lista de los errores establecidos, y las pequeñas diferencias
entre éstos y los errores del modelo inicial cedido por la Universidad de Alicante.
Y, finalmente, hemos tratado de explicar como se desarrolló la apariencia final que
tiene el software, la interacción con el usuario, los cambios que hemos debido de hacer en
el archivo de comandos CommandSet.dslddt y las nuevas clases que dan como resultado
los cuadros de diálogo que nos permiten elegir entre las distintas opciones de generación
de código del modelo creado por el mismo usuario (elección de la base de datos,
dimensiones a normalizar…).
Modelo de dominio
El primer paso para dar forma a nuestro modelo consistía en realizar un esquema
conceptual del mismo. Las DSL Tools nos ofrecen esta misma posibilidad con las
herramientas ya explicadas (clases, propiedades y relaciones), dándole además la función
añadida de poder obtener un diseñador gráfico a partir de ese modelo.
Iremos explicando como fue ese primer modelo, que plasmaba la idea conceptual
inicial que teníamos del sistema, y de qué manera tuvimos que ir modificándolo para que
pudiese adaptarse a las exigencias y restricciones de las DSL Tools, hasta poder obtener así
el modelo final al que se ha llegado.
Esquema conceptual
Como se ha comentado, se ha realizado una reproducción del modelo
multidimensional para almacenes de datos expuesto por la Universidad de Alicante en su
artículo (ver referencia en bibliografía). De este modelo sólo se ha desarrollado el nivel 3
debido a limitaciones de la herramienta, por lo tanto los elementos necesarios se reducen a
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
31
Facts, Dimensions, Bases, Degenerate Facts, Asociaciones Rolls-up To y Completeness, y
los atributos Degenerate Dimension, Fact Attribute, OID, Descriptor y Dimension
Attribute.
El boceto inicial para organizar todos estos conceptos era el siguiente:
Se intentó organizar los conceptos de la manera más sencilla posible para así luego
obtener un esquema de la misma manera sencillo. A la hora de intentar traducir este
esquema a un modelo de dominio de las DSL Tools, se tuvo que tener en cuenta algunos
aspectos:
Se necesita una clase raíz de la que deriven las demás clases para darle así
sentido al modelo. A esta clase se le ha llamado “Esquema”
Fact
Dimension
Base
Class
DegenerateFact
Association Rolls-up To
Completeness
DegenerateDimension
FactAttribute
OID
Descriptor
DimensionAttribute
Attributes
Element
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
32
Tanto las clases como los atributos se debían especificar en el modelo como
clases. La diferencia entre unos y otros se realizaría en la parte del
diseñador, en donde para aquellos que sean atributos deberían embeberse en
otras clases. Sin embargo, en el modelo, se podía hacer una superclase
“Class” y otra superclase “Attribute” para distinguirlos, y darle
características comunes de ser así necesario.
Las asociaciones se especifican como relaciones en el modelo de dominio.
De todas maneras, la Rolls-up To y Completeness, al ser ambas asociaciones
entre bases, se representarían como un atributo en dicha asociación, visible
por lo tanto seleccionando las propiedades de la relación en la ventana de
propiedades de Visual Studio.
La clase DegenerateFact, al ser una clase especial, se dejaría de momento a
parte.
De esta manera, el esquema conceptual inicial para el DomainModel de las DSL
Tools quedó así:
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
33
Esquema que, por supuesto, no compilaba todavía…
Hacia un modelo compilable
Dado que el sencillo esquema conceptual anterior resultaba aún incompleto,
decidimos dar un paso más hacia la primera compilación de nuestro proyecto. Entonces se
nos presentaron una serie de problemas que fueron necesarios resolver antes de poder
continuar. El primero de ellos era la organización de las clases y sus atributos, así como
conseguir relacionarlos entre ellos. En segundo lugar, distinguir los distintos tipos de
relaciones, y el aspecto que iban a tener en el modelo de dominio. Y, por último, fue
necesario solucionar algunos errores de compilación que no nos permitían seguir
avanzando en nuestra tarea.
Atributos embebidos
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
34
Para que un atributo pudiese representarse como tal de una determinada clase era
necesario que la clase que representa dicho atributo estuviese embebida en la clase que lo
contiene. Gráficamente, si tenemos una clase 1 que embebe una clase dos, la relación entre
ellas debe ser una Embedding relationship, como se muestra a continuación:
Lo que más tarde se traduciría gráficamente por:
Que era lo que se buscaba para poder representar los atributos de las clases. Para
que el modelo compilara correctamente había que tener en cuenta una serie de restricciones
(impuestas por las DSL Tools):
Debe haber entre ellas una relación embebida (como ya se ha explicado).
La propiedad Accepts del rol de la izquierda (el triángulo) debe estar
marcada como “All”, mientras que la otra (la del rectángulo) debe ser
“None”.
Para que una clase pueda embeber a otra, la clase embebida no puede estar
ya embebida por otra clase. Por ejemplo, si tenemos una clase A que
embebe a B, no puede existir una clase C que ya embeba a B. Gráficamente:
Esto significa que una clase puede embeber a todas las que quiera, pero sin
embargo una clase solo puede ser embebida por otra. Esto suponía un
problema para los atributos DegenerateDimension y FactAttribute, ya que
ambos estaban embebidos tanto por el Fact como por el DegenerateFact.
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
35
Como solución provisional se cambiaron las relaciones del DegenerateFact
por referencias. Más adelante explicaremos como solucionamos el
problema.
Y por último, en una relación embebida sólo hay cuatro posibles
combinaciones en las cardinalidades de los roles: (*,0), (*,1), (+,0), (+,1).
Cualquier otra combinación entre ellas dará error cuando intentemos
generar los templates. El rol del rectángulo no te da opción a ningún otro
valor que no sea 0 o 1, pero sin embargo el triángulo si que te permite
cualquier valor. En este último rol, por lo tanto, el valor de max siempre
deberá ser de ‘0’.
Relaciones en el modelo
Sabiendo esto, se añadieron al esquema todas las relaciones entre clases como
references, y las de clases con atributos como embedding (a excepción de las del
Degenerate Fact). Estas relaciones son:
Entre clases:
o Base con Base:
BaseAssociatesBase: asociaciones entre bases. A su vez tiene
5 atributos:
1. type: RollsupTo o Completeness.
2. SourceMultiplicity: cardinalidad en el origen.
3. TargetMultiplicity: cardinalidad en el destino.
4. SourceRole: role en el origen (+d o +r).
5. TargetRole: role en el destino (+d o +r).
BaseInheritsBase: herencia entre bases
o Dimension con Base:
DimBaseAssociation: asociaciones entre una dimensión y
una base.
o Fact con Dimension
Aggregation: agregaciones, siempre entre un hecho y una
dimensión. Estas agregaciones, a su vez, pueden estar
relacionadas con un DegenerateFact.
Entre clases y atributos:
o Base:
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
36
Descriptor
DimensionAttribute
OID
o Dimension (sin atributos)
o Fact:
DegenerateDimension
FactAttribute
o DegenerateFact:
DegenerateDimension
FactAttribute
De este modo, el esquema quedó como indica la siguiente figura:
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
37
NOTA: Las relaciones Aggregation y BaseAssociatesBase son mostradas como
clases debido a que una posee a su vez otra relación y la otra tiene atributos dependientes
de ella, respectivamente. Y según las DSL Tools, para poder especificar esto es necesario
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
38
extraer la relación como clase (aunque sólo de forma visual, semánticamente siguen siendo
relaciones). Esto se consigue seleccionando la relación y dándole al botón derecho elegir
“Show As Class”.
Errores de compilación
Sin embargo, todavía no conseguíamos compilar el modelo del todo, nos
encontramos con algunos errores de compilación que nos lo impedían. Pasaremos a
explicarlos levemente:
1. Problemas derivados de usar palabras reservadas. Si nos fijamos en el
siguiente esquema (1), hay dos roles que utilizan palabras reservadas. Si los
dejamos tal y como están, a la hora de compilar nos darán errores
extrañísimos, pero si nos fijamos en la zona del código donde se producen
veremos que hay algunas palabras reservadas que se utilizan de manera
incorrecta. En nuestro caso nos pasaba con las palabras “class” y “base”.
La solución consiste en darle otro nombre, tales como “classRole” y
“baseRole”, respectivamente.
2. Error de tipo de datos mal definido. En las asociaciones entre bases, para
definir el tipo de asociación que representa (Rolls-upTo o Completeness), se
ha añadido un atributo a dicha asociación, llamado type. El tipo de dato de
este atributo es un enumerado llamado AssociationType. Pues bien, para
cada tipo de dato que se defina como enumerado es obligatorio ponerle un
valor por defecto (etiqueta Default en las propiedades de la propiedad type),
y este valor debe ser un string que represente alguno de los tipos
enumerados definidos, no su valor numérico. Si se pone como valor por
defecto un valor numérico aparecerá un error en el código como el
siguiente:
Private
Proyecto.Fincarrera.ObjectOrientedMultidimensionalModel.DomainModel.AssociationType
typePropertyStorage =
Proyecto.Fincarrera.ObjectOrientedMultidimensionalModel.DomainModel.AssociationType.0;
Ese cero al final del código es sintácticamente incorrecto, debería aparecer
un string dentro de los definidos en AssociationType. Vemos más
claramente el error en la siguiente imagen:
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
39
Lo solucionamos poniendo “RollsupTo” en lugar del 0 en el atributo
Default.
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
40
(1) Esquema que muestra el mal uso de palabras reservadas
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
41
3. Problema del carácter no válido dentro de una enumeración. Este error
está también relacionado con el anterior. En la enumeración antes descrita,
hay que tener cuidado con los caracteres utilizados para definir cada
elemento, ya que todos esos strings serán más adelante convertidos en
código que deberá ser correcto sintácticamente. Nuestra enumeración inicial
era:
Donde se puede apreciar que el tipo Rolls-upTo tiene un guión, que a la
hora de traducirlo a código daría la siguiente definición de tipo enumerado:
public enum AssociationType
{
Rollsup-To=0,
Completeness=1,
}
Lo que es incorrecto, y hace que el compilador nos diga que esperaba un
“;”.
La solución es tan sencilla como quitar dicho guión.
Por lo tanto, después de todos estos detalles y errores de compilación, conseguimos
así el primer modelo compilable. El esquema del modelo de dominio es el mismo que en
(1), y el resultado gráfico que se obtiene es el siguiente:
Todavía no se habían probado los conectores, pero cada una de las clases queda
representada con el efecto gráfico que buscábamos.
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
42
Esquema definitivo
Tras conseguir el primer modelo compilable, quedaban aún por resolver diversas
cuestiones. Entre ellas se encontraban el conseguir realizar las asociaciones entre
elementos del modelo (conectores), el problema ya comentado de varios atributos que
deben estar embebidos en distintas clases y decidir qué hacer con la clase DegenerateFact.
Pasaremos a tratar cada uno de estos temas hasta así lograr obtener el diagrama final a
partir del cual se ha trabajado en la generación de código.
Conectores
Para conseguir mostrar las asociaciones en el esquema final, era necesario
asociarlos en el diseñador como conectores entre clases. Explicaremos como se realiza esto
más adelante, en el siguiente apartado sobre la creación del diseñador gráfico. La cuestión
que se nos plantea ahora era el resultado visual que nos daba.
Cuando tenemos una referencia entre una clase A y otra clase B de tipos distintos,
si queremos definir un conector para dicha referencia en el Designer.dsldd tenemos que
especificar un (y sólo un) sentido en el que el conector será dibujado, esto es, desde un rol
hasta otro. En nuestro caso muchos de los conectores actúan de esta manera, por lo que no
existe ninguna dificultad. El problema se plantea cuando queremos que nuestro conector
pueda dibujarse en ambos sentidos.
Si queremos que se pueda navegar desde la Shape A hasta la Shape B y viceversa,
una posible solución consiste en que en el modelo de dominio ambas clases deriven de una
superclase ficticia (o real, en el caso de que ya derivaran de otra clase), llamémosla clase
C, y crear un conector desde la clase C a sí misma. Gráficamente,
De esta manera ya podremos crear un conector desde la Shape A que represente a
la clase A hasta la Shape B que represente a la clase B, y viceversa. Sólo hay que poner las
Shapes permitidas en la etiqueta <permittedShapes> del Designer.dsldd (para más
información sobre el Designer, consultar apartado Creación del diseñador gráfico).
En nuestro caso particular, queríamos tener la posibilidad de relacionar la clase
Dimension y la clase Base en ambos sentidos, por lo que se tuvieron que hacer los cambios
explicados en el modelo de dominio. De esta manera pasamos de tener este esquema:
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
43
a tener este otro, donde la relación entre dimensión y base se especifica en la
relación de “Class” a “Class”:
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
44
Perdemos en claridad a la hora de leer en modelo, ya que no resulta tan intuitivo
que existe una relación entre Dimension y Base, pero ganamos en funcionalidad. El
problema es que las restricciones que nos obligan a relacionar una clase Dimension con
una Base hay que especificarlas a otros niveles.
La clase DegenerateFact
Hasta ahora habíamos dejado la clase DegenerateFact aparte, por tratarse de una
clase especial. Cuando hablábamos del esquema conceptual, antes de meternos en asuntos
concretos de las DSL Tools, veíamos como el concepto DegenerateFact era a la vez una
clase y una asociación. Esto es porque une dos conceptos, solo puede darse en una relación
entre una instancia de un Fact con una instancia de una Dimension, de otra manera no tiene
sentido.
Hasta el momento, el esquema que tenemos es el siguiente (se han contraído en el
esquema la clase Base y la asociación Aggregation para simplificar):
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
45
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
46
Como se puede apreciar, no se puede acceder a la clase DegenerateFact desde
ninguna ruta a partir de la clase raíz “Esquema”, por lo que no aparecerá en el diseñador
cuando depuremos el modelo. Como tenemos dos tipos principales de clases que dependen
de la raíz, Attributes y Classes, lo más lógico es englobarlo dentro de las Classes, ya que
guarda muchas más similitudes con ellas.
Por lo tanto, ahora la clase DegenerateFact heredaría de la clase Class al igual que
las otras clases. Pero todavía queda por solucionar el tema de sus atributos. Como hemos
explicado ya en el punto Atributos embebidos, una clase no puede estar embebida por otra
si ya existe una clase que la embebe. Recordemos que los atributos de DegenerateFact
(DegenerateDimension y FactAttribute) ya están embebidos por la clase Fact. Si
intentamos establecer una relación embebida que vaya de DegenerateFact a estos mismos
atributos nos dará un error de compilación, no dejándonos continuar. En un principio
habíamos tomado la decisión temporal de establecer las relaciones del DegenerateFact con
sus atributos como referencias, pero no puede quedarse así ya que para que gráficamente se
pueda representar como una clase dentro de otra (ver figura de abajo), se exige que haya
una relación embebida entre ellas.
Para poder representar las clases de esta manera, Clase1 debe embeber a Clase2
Así que debemos de asumir que los atributos de Fact y los de DegenerateFact son
distintos a la vista del modelo de dominio. Distintos pero con el mismo comportamiento.
Por lo tanto, la solución que hemos buscado es crear dos clases abstractas
DegenerateDimension y FactAttribute, de las cuales heredan otro par de clases para cada
una de ellas: DF_DegenerateDimension y F_DegenerateDimension para la primera, y
DF_FactAttribute y F_FactAttribute para la segunda, de manera que las que tienen
nomeclatura DF pertenezcan al DegenerateFact y las de prefijo F sean de la clase Fact. De
esta manera cada clase tiene sus propios atributos, por lo que será sintácticamente correcto,
y a la vez dichos atributos se comportarán de la misma manera, por heredar de las mismas
clases.
Resumiendo, haciendo estos cambios el modelo quedaría de la siguiente manera:
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
47
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
48
Las clases dibujadas con puntos suspensivos indican que son clases abstractas.
Como se puede ver, ahora si que existe una relación embebida tanto para los atributos de
Fact como para los de DegenerateFact, así que se podrán representar gráficamente tal y
como buscábamos.
Conector del DegenerateFact
Una vez teníamos todo lo necesario para poder representar el modelo nos surgió
una dificultad, de nuevo con la clase DegenerateFact, y esta vez con respecto a la
representación de su conector.
Hasta ahora el conector del DegenerateFact se había especificado en el modelo de
dominio como se muestra el esquema simplificado de abajo, pero todavía no se había
sincronizado con el diseñador, por lo tanto no teníamos ninguna representación física de tal
relación.
Pero resulta que en el DomainModel sí que se puede especificar la relación entre
una clase y la intersección de otras dos, seleccionando Show As Class en la relación, tal y
como se ha explicado antes. En nuestro caso, sucede así con la clase DegenerateFact y la
agregación entre las clases Fact y Dimension. Pero desgraciadamente no hay forma
ninguna de representar gráficamente dicha relación. Lo ideal habría sido un conector que
tuviera en un extremo la clase DegenerateFact y en el otro el conector que une a una
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
49
instancia de un Fact con una de una clase Dimension. Pero la herramienta en esta versión
no lo permitía, por lo que tuvimos que buscar opciones alternativas.
Teníamos dos posibilidades. Una era crear una clase puente (representada con una
figura geométrica, como por ejemplo un rombo) y un conector específico que pudiese unir
a las tres clases entre sí; o bien crear un conector que fuese exclusivamente desde la clase
DegenerateFact hasta una clase Fact y/o a otra clase Dimension (para cada relación, por lo
tanto, habría que usar dos conectores). Se escogió esta última opción por simplicidad. Por
lo tanto decidimos crear un conector llamado DegenerateFact Association, representado
por una línea de puntos, que se comportara de esta manera. El resultado gráfico era el
siguiente:
Y todo esto trasladado en lenguaje de modelo de dominio daba resultado a una
nueva relación que debía tener en cuenta a tres clases: Fact, Dimension y DegenerateFact.
Para poder establecer restricciones entre ellas, la relación no podía ser directa entre las
clases, sino a partir de la clase de la que todas ellas derivan. De esta manera, al igual que
hicimos con el conector bidireccional entre Dimension y Base (ver apartado Conectores),
había que establecer una relación entre la clase Clase consigo misma, para luego en el
diseñador poder especificar las tres clases implicadas y el sentido del conector.
En resumidas cuentas, el resultado después de cambiar este tipo de conector en el
modelo de dominio era el siguiente:
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
50
Como se ve en la imagen anterior, la relación en Aggregation ya no existe, y ha
sido sustituida por otra relación de Clase a Clase llamada FactDimHaveDegenFact (se
puede ver el nombre en la ventana de propiedades del Visual Studio). Ya que el conector
se debe relacionar tanto con un Fact como con una Dimension, las cardinalidades tenemos
que establecerlas como muchos a muchos, para poder permitirnos más de un conector
desde la clase de DegenerateFact. Las restricciones del número de conectores máximo por
clase deberemos imponerlas a otros niveles (ver apartado Restricciones y Validaciones).
Esquema final del Modelo de Dominio
Después de todos estos cambios para conseguir un equilibrio entre el esquema
conceptual del modelo de dominio y las posibilidades gráficas que se permiten obtener a
partir de él, obtuvimos el esquema de modelo de dominio con el que se ha estado
trabajando en el resto del proyecto. Dicho esquema totalmente desplegado es el siguiente
(se ha dividido en dos páginas para su mejor visualización):
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
51
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
52
Interfaz de usuario
La creación del diseñador gráfico y del aspecto de la interfaz no es una tarea
independiente de la de la creación del modelo de dominio. De hecho ya se han visto casos
en los que hay que tener en cuenta las posibilidades gráficas de la herramienta antes de
tomar una decisión sobre la forma del esquema conceptual. En algunos, incluso, se ha
perdido legibilidad en el esquema para poder ganar en recursos gráficos. Por lo tanto son
dos procesos que deben desarrollarse de forma paralela, y siempre que se hace un cambio
en el modelo de dominio se debe poder sincronizar con el diseñador.
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
53
Para el desarrollo de esta fase ha sido de gran ayuda la herramienta ofrecida por
Modelisoft, la “DslDm2Dd Tool”. Gracias a ella, nos hemos podido ahorrar la difícil
comprensión del código XML que sirve para definir el fichero Designer.dsldd, base del
diseñador gráfico en las DSL Tools. Con unos pocos cuadros de diálogo hemos podido
darle una apariencia bastante aceptable a nuestro diseñador, y a la vez comprender de
manera general como está esquematizado el fichero principal del Designer.
No pretendemos aquí hacer un tutorial sobre la herramienta, ni mucho menos del
funcionamiento del fichero Designer.dsldd, ya que nos llevaría hojas y hojas poder explicar
ambos con suficiente claridad. Sin embargo, hemos preferido en este apartado describir el
resultado gráfico final de nuestro proyecto, una vez conseguido el esquema de modelo de
dominio definitivo.
Por lo tanto describiremos en los dos primeros apartados todas las herramientas de
las que disponemos para dibujar un modelo multidimensional orientado a objetos (con
extensión *.oomm), así como su funcionamiento y limitaciones. Y sólo en el último
apartado hablaremos un poco sobre algunos problemas encontrados durante el camino,
únicamente a modo general, y la mayoría de ellos debido a bugs o errores que
posiblemente hayan sido ya subsanados en posteriores versiones de las DSL Tools.
Clases y formas
Para que una clase pueda ser representada en el diseñador como tal, hay que
asociarla a una Shape en el fichero del Designer.dsldd. Existen tres tipos distintos de
Shapes:
1. Geometric Shape: forma geométrica predefinida (rectágulo, rectágulo
redondeado, elipse, círculo o diamante).
2. Image Shape: imagen de un arcchivo.
3. Compartment Shape: forma geométrica (rectángulo o rectángulo
redondeado) que puede tener otras clases embebidas como atributos.
Nosotros debemos representar 4 clases distintas: Fact, DegenerateFact, Base y
Dimension. Las tres primeras se representan como Compartment Shapes, ya que todas ellas
tienen atributos, mientras que la clase Dimension se representa como una Image Shape.
Las resumimos en la siguiente tabla:
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
54
Nombre de la
clase
(DomainModel)
Nombre de la Shape
(Designer)
Icono en la
Toolbox Forma en el diseñador Atributos
Fact FactShape
DegenerateDimension
FactAttribute
Dimension DimensionShape
─
Base BaseShape
OID
Descriptor
DimensionAttribute
DegenerateFact DegenerateFactShape
DegenerateDimension
FactAttribute
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
55
Conectores entre clases
En el caso de las relaciones entre clases, para que puedan materializarse
gráficamente deben ser asociadas a un conector en el Designer.dsldd.
Todos los conectores que hemos creado se corresponden a una Reference
RelationShip en el DomainModel. Son cinco: Aggregation, Association, BasesAssociation,
Inheritance y la DegenerateFact Association.
En el caso del Aggregation y del BasesAssociation los conectores contienen
cardinalidades en sus extremos (consultar apartado Soft Constraints). Teóricamente el
conector de BasesAssociation debería tener una etiqueta que indicara el tipo de conector
(RollsUp-To o Completeness), pero esta versión de las DSL Tools no permitía las etiquetas
en el centro del conector (bug de esta release, consultar apartado Etiquetas en los
conectores).
El conector del DegenerateFact relaciona tres clases: Fact, Dimension y
DegenerateFact. Por lo tanto, harán falta dos conectores para que estén las tres
relacionadas entre ellas. Para utilizarlo siempre habrá que partir del DegenerateFact.
Un conector entre dos tipos distintos de clases será unidireccional si sólo se puede
establecer dicha relación desde una clase determinada a la otra. Será bidireccional si se
puede hacer en ambos sentidos.
Podemos ver la representación de los conectores en la siguiente tabla:
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
56
Nombre de la relación
(DomainModel)
Nombre del Connector
(Designer)
Icono en la
Toolbox Forma en el diseñador
Clases que
relaciona
Aggregation AggregationConnector
Fact con
Dimension
(unidireccional)
DimBaseAssociation AssociationConnector
Dimension con
Base
(bidireccional)
BaseAssociatesBase BaseAssociatesBaseConnector
Base con Base
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
57
BaseInheritsBase BaseInheritsBaseConnector
Base con Base
FactDimHaveDegenerateFact FactDimHaveDegenerateFactConnector
DegenerateFact
con Fact
(unidireccional)
y
DegenerateFact
con Dimension
(unidireccional)
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
58
Problemas encontrados
A la hora de intentar sincronizar el DomainModel con el Designer se han
encontrado una serie de dificultades, la mayoría de ellas relacionadas con los iconos.
Consideramos una buena guía de apoyo para aquellos que quieran seguir los pasos que
hicimos nosotros en nuestro momento. Por lo tanto pasaremos a explicar dichas
dificultades y limitaciones, y la manera de la cual se han solucionado (en los casos en los
que ha sido posible).
Cambio de iconos en la Toolbox
Algo tan sencillo como cambiar un icono ya definido para una clase en el Toolbox
puede acarrear muchos problemas. Al intentar modificar el icono con la aplicación
DslDm2Dd, generar plantillas y compilar, todo es aparentemente correcto; pero cuando se
ejecuta en modo depuración es probable que el antiguo icono siga estando en la Toolbox.
Suponemos que es un error de la herramienta de sincronización, que por alguna
razón no actualiza los cambios relacionados con los recursos del proyecto. La cuestión es
que después de muchas pruebas, para poder cambiar un icono hemos tenido que seguir los
siguientes pasos:
1. Ejecutar el DslDm2Dd, y borrar la clase en la parte del Designer Definition
que corresponde a la clase a la que se quiere cambiar el icono. Guardar el
Designer.dsldd.
2. Volver al Visual Studio, generar templates y depurar. El icono debe haber
desaparecido de la Toolbox.
3. Volver a ejecutar el DslDm2Dd y arrastrar de la parte del Domain Model el
concepto del cual hay que cambiar el icono a la raíz del Designer Definition
para crearlo de nuevo.
4. Cambiarle el nombre de la clase a otro distinto del antiguo. En nuestro
ejemplo, vamos a cambiarle el icono al conector
BaseAssociatesBaseConnector, así que le pondremos como nuevo nombre
BaseAssociatesBaseConnector2 (ver imagen).
5. Seguir los pasos del asistente hasta que te pida el icono, y seleccionar el
fichero correspondiente.
6. Salvar, generar plantillas y depurar. El nuevo icono aparecerá el la Toolbox.
Creando un elemento en el Designer con un nuevo nombre hace que se cree un
nuevo objeto y que, por lo tanto, se inicialicen todos sus valores a los indicados por
primera vez. Siguiendo estos pasos hemos cambiado el nombre al objeto del Designer
Definition a otro distinto del que teníamos antes. Si queremos volver al nombre antiguo tan
solo hay que hacer un Replace All del nombre que tenemos ahora al nuevo que queremos
(que era el que teníamos en un principio).
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
59
Etiquetas en los conectores (Text Decorators)
Cuando hablábamos del conector BaseAssociatesBaseConnector, comentamos que
debería de tener una etiqueta central donde apareciese el tipo de conector: RollsUp-To o
Completeness. Sin embargo, esto no es posible en esta versión de las DSL Tools.
En un principio, existen 6 posibles posiciones donde se puede colocar una etiqueta
para un conector: CenterBottom, CenterTop, SourceBottom, SourceTop, TargetBottom y
TargetTop. Todas ellas corresponden al atributo Position de la etiqueta <connectorText>
del Designer.dsldd. Utilizando la herramienta de Modelisoft se puede seleccionar mediante
el cuadro de diálogo de la figura de abajo.
Pero desgraciadamente existe un bug en esta CTP que no permite poner etiquetas
en las posiciones centrales, esto es, la CenterBottom y la CenterTop. Se puede especificar
en el fichero XML, pero no produce ningún resultado.
Por lo tanto, en el caso del conector BaseAssociatesBaseConnector, como también
debe tener etiquetas para las cardinalidades, no es posible mostrar visualmente encima del
mismo conector el tipo al que pertenece, ya que las cuatro únicas posibles etiquetas están
ya ocupadas. Para saber el valor de dicho atributo habrá que seleccionar el conector y mirar
en la ventana de propiedades del Visual Studio.
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
60
Iconos en las Compartment Shapes
Como se ha explicado con anterioridad, existen tres tipos de Shapes que podemos
darle a una clase (apartado Clases y formas): Geometric Shape, Image Shape y
Compartment Shape. El problema es que no podemos representar Compartment Shapes
con imágenes sacadas de un archivo (al igual que hacíamos en la Image Shapes, como en
el caso de Dimension, pero con atributos).
Esto no nos permite representar las clases Fact, DegenerateFact y Base como nos
hubiera gustado, con una imagen y con atributos embebidos, pero sin embargo existe una
solución intermedia: sí que se puede añadir un pequeño icono en alguna zona del
rectángulo que representa la clase. Así podremos identificarlas inequívocamente.
Si intentamos añadirle un icono con la herramienta DslDm2Dd, es muy probable
que nos salga alguna excepción algo extraña. Tal y como explican en la página de dicha
herramienta (ver apartado de enlaces), más concretamente en la parte del Walktrough
donde pone More Information, los iconos de las Shapes aún no están implementados. Pero
sin embargo podemos conseguir que aparezcan haciendo algunos pequeños cambios.
Como el resultado de las Shapes con iconos ya se puede apreciar en el apartado de
Clases y formas, lo que vamos a hacer es dar un sencillo ejemplo de cómo conseguirlo
partiendo de un proyecto de las DSL Tools con el lenguaje mínimo. Vamos a poner un
pequeño icono en la esquina superior izquierda a la shape que representa la clase
ExampleClass. Primero de nada ejecutamos el DslDm2Dd y editamos la ExampleShape.
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
61
Vamos siguiendo los pasos del asistente, hasta que llegamos a la parte de los Icon
Decorators, le damos al botón señalado con un + en la zona donde queremos poner nuestro
icono, y lo seleccionamos en el menú contextual siguiente.
Con la parte Always Visible marcada, le damos a aceptar y continuamos hasta que
cerramos el asistente. Salvamos el Designer.dsldd y volvemos a la ventana del Visual
Studio.
Cuando recargamos el fichero y generamos templates parece que todo ha salido
bien, pero si intentamos depurar el modelo nos saldrá algo parecido al siguiente mensaje de
error:
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
62
Pues bien, tal y como se explica en este mensaje lo único que nos hace falta es
agregar el icono en el fichero de recursos (Designer.Resource.resx). Detenemos la
depuración y nos vamos a este fichero, y una vez allí seleccionamos la pestaña de images.
Allí se encuentran ya los dos iconos que pertenecen a la Toolbox, sólo habrá que añadir
nuestra imagen dándole a Add Resource -> Add Existing File. Es importante que el nombre
de la imagen sea el mismo que el que nos decían en el diálogo de error.
Una vez añadido, ya podemos volver a depurar y veremos como aparece el icono
en cada nueva Shape que añadamos.
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
63
Iconos transparentes
El último de los errores que encontramos en esta versión de las DSL Tools fue en
relación con la apariencia final de los iconos, tanto en las Shapes como en la Toolbox.
Puede que si se intenta insertar un icono personalizado nos hayamos encontrado
que el icono aparece transparente. Como se ha podido comprobar, todo el tema de los
iconos está todavía en fase de pruebas. Bien, en un post del foro de las DSL Tools de
Microsoft se explica que si se deja la esquina superior derecha y la esquina inferior
izquierda en blanco, todos los iconos blancos aparecen transparentes. Hemos comprobado
que esto no solo sucede con el blanco, sino con cualquier color.
Es por ejemplo el caso del icono que representa a la clase Fact. Todo su contorno es
negro, por lo que al principio sucedía que toda la maya aparecía transparente en la
Toolbox.
La solución que le dimos fue cambiar el valor de dichos colores de las esquinas por
un gris muy ennegrecido (valores RGB(10,10,10), por ejemplo), de manera que no se
notara el cambio de color pero que a la vez fuesen distintos para evitar que se volviese
transparente. En el dibujo de abajo se puede apreciar el cambio.
Restricciones y validaciones
Una vez conseguido el esquema final del modelo de dominio y la apariencia que
iba a tener el diseñador, tocaba dedicarse a los detalles. El lenguaje utilizado para definir el
modelo de dominio no era suficiente para expresar toda la semántica del modelo que se
buscaba. Pero las DSL Tools incorporan también un sistema de validaciones y
restricciones, en el cual modificando el código generado automáticamente se puede llegar a
definir nuestro propio comportamiento para el modelo.
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
64
Una parte importante en este punto son los ficheros de recursos, por eso les
dedicaremos un breve apartado para explicar su funcionamiento. Y con respecto a las
restricciones, debemos decir que pueden añadirse de dos maneras distintas:
Usando el sistema de validaciones (soft-constraints). El código se añade en
el proyecto del DomainModel (e.g. en una carpeta “Validation”). Las
validaciones se realizarán cuando se disparen determinados eventos.
Usando el sistema gráfico en tiempo de ejecución (hard-constraints). El
código se añade en el proyecto del designer (e.g. en una carpeta “Custom”).
Las validaciones se realizan constantemente, en tiempo de ejecución.
El elegir una forma de restringir nuestro lenguaje u otra depende de lo que estemos
buscando. Las soft-constraints, por lo general, permiten trabajar de una forma más rápida
y, al final del todo, pasar a solucionar las restricciones. Esto es posible dado que con este
sistema de validaciones se puede seguir trabajando aunque haya partes de nuestro modelo
que estén incorrectas.
Por otro lado, las hard-constraints no permiten modelos incorrectos (con modelos
correctos o incorrectos nos referimos siempre a errores de forma temporal). Las
restricciones se solucionan conforme vamos dibujando el diagrama. Estas restricciones
deberían ser más sencillas para que se puedan computar de una forma más rápida y no
ralentice la ejecución del programa. De la misma manera, un número excesivo de hard-
constraints puede llegar a resultar frustrante para el diseñador, al tener que estar parando a
cada paso para resolver los problemas.
Archivos de recursos
Para los que no estén muy familiarizados con el entorno de Visual Studio, o bien
los que sí lo están, para organizar un poco la estructura del proyecto dsl, introduciremos
este apartado sobre los archivos de recursos.
Visual Studio contiene unos archivos especiales de extensión .resx llamados
archivos de recursos, utilizados para almacenar todos aquellos elementos necesarios por el
programa tales como strings, ficheros, iconos, imágenes, etc. de una forma ordenada. Cada
proyecto tiene al menos un archivo de este tipo. Se puede consultar dándole con el botón
derecho en el proyecto, en Properties, y luego abriendo la pestaña Resources.
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
65
En el caso de un proyecto de las DSL Tools nos encontramos con más de un
archivo de recursos. Nos referiremos a los proyectos principales, el Designer y el
DomainModel, para explicar cada una de la función que se le ha dado a dichos archivos:
Designer Project:
o En Properties/Resources.resx, este es el archivo principal de
recursos, el mismo que se obtiene al darle al botón derecho y
Properties. No se le ha dado ninguna utilidad.
o En Diagram/Designer.Resources.resx, se refiere a los recursos
relacionados con el diagrama, esto es, con el diseñador gráfico
resultante. En nuestro caso, al tratarse dicho diagrama de nuestro
campo de trabajo, ha sido el más utilizado, sobre todo dos recursos:
Strings: contiene las cadenas de caracteres de las Shapes, los
conectores y de los mensajes de error de las hard-constraints.
Images: contiene las imágenes necesarias para representar las
Shapes, y los iconos de la Toolbox.
o En Shell/CommandSet.Resource.resx, se refiere a los recursos
relacionados con los cuadros de diálogo y demás funcionalidades
añadidas en el fichero CommandSet.dslddt (consultar apartado
Cambios en el CommandSet). Se han utlizado tanto los Strings
como los recursos de tipo File.
DomainModel Project:
o En Properties/Resources.resx, este es el archivo principal de
recursos, el mismo que se obtiene al darle al botón derecho y
Properties. Al igual que en el Designer Project, tampoco se le ha
dado ninguna utilidad.
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
66
o En DomainModel.Resource.resx, se refiere a todos aquellos recursos
relacionados con el modelo de dominio. Tan sólo se han utilizado
los Strings para describir las soft-constraints.
Soft Constraints
Como hemos comentado, en este sistema de validaciones las restricciones se
producen cuando se disparan determinados eventos. Dichos eventos son:
Save: al salvar el fichero.
Open: al abrir el fichero.
Menu: al seleccionar la opción Validate All en el menú emergente cuando
hacemos click con el botón derecho en el diagrama.
Custom: al hacerlo manualmente desde el código.
El resultado cuando se produce una restricción es que aparece un mensaje en la
pestaña de errores del Visual Studio, pero el usuario podrá seguir trabajando con el modelo
aunque no estén todos los errores solucionados.
Antes de nada, para poder trabajar con las soft-constraints es necesario activar el
sistema de validaciones. Para ello, hay que abrir el fichero Designer.dsldd y modificar la
siguiente etiqueta incluida en el <designerDefinition> (al final del fichero):
<validation open="true" save="true" menu="true" custom="true" />
Dependiendo de cuando queramos que se comprueben las validaciones, pondremos
a true unos eventos u otros. Una vez modificado, salvar el fichero.
Si se ha elegido el evento de Save, es necesario añadir un par de strings al fichero
Designer.Resources.resx, para mostrar dos errores que se pueden llegar a producir mientras
se salva un fichero:
En Value se puede poner el valor que se desee. El segundo error se produce cuando
se intenta salvar el fichero habiendo errores de validación, y el primero cuando se decide
no salvar el documento.
Como las soft-constraints establecen restricciones para un esquema de modelo de
dominio determinado, están estrechamente relacionadas con el proyecto del DomainModel.
Por lo tanto añadiremos el código referente a las soft-constraints en ficheros contenidos
dentro de un directorio al que llamaremos “Validation”, dentro del proyecto del
DomainModel.
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
67
Cada restricción deberá estar incluida en un método de validación. Para añadir
dicho método, es necesario añadir una partial class para cada clase afectada. El hecho de
incluir todas las partial class en el mismo fichero o en ficheros separados es indiferente a
efectos de los resultados obtenidos, pero sí se puede tener una mejor organización de los
distintos errores si se separa en varios ficheros. En nuestro caso, los tres primeros tratan
sobre las restricciones de tres clases distintas (Fact, Base y DegenerateFact,
respectivamente), mientras que en el último hemos englobado 5 clases (todos los
Attributes), ya que las restricciones a las que nos referimos son las mismas (sobre los
atributos derivados). Los ficheros son los siguientes:
AggregationCardinality.cs. Trata sobre las restricciones relacionadas con
las cardinalidades de la relación Aggregation, entre una clase Fact y una
clase Dimension. Como dicha relación comienza siempre desde un Fact,
será dicha clase la que se modifique con una partial class.
Base.cs. En este fichero se especifican todas las restricciones referentes a la
clase Base. Al igual que en el anterior, los conectores entre bases también
tienen cardinalidades, por lo tanto habrá que añadirle dicho método.
Además, incluimos las restricciones sobre los roles +r y +d, la restricción
que obliga a tener un DescriptorAttribute y la restricción de tener como
mucho un OID Attribute.
DegenerateFact.cs. En este fichero establecemos la restricción de la clase
DegenerateFact que especifica que debe estar conectado tanto a un Fact
como a una Dimension.
DerivedAttributes.cs. Trata sobre las restricciones relacionadas con los
atributos derivados. Se incluyen 5 partial classes en este fichero:
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
68
DegenerateDimension, FactAttribute, OID, Descriptor y
DimensionAttribute.
Pasaremos a explicar en detalle cada una de estas restricciones.
Añadir cardinalidades en las relaciones
En el primer fichero, el AggregationCardinality.cs, se establen cardinalidades para
el conector asociado a la clase Aggregation. Para conseguir esto, hemos tenido que seguir
los siguientes pasos:
1. Seleccionar la relación Aggregation, darle al botón derecho y escoger la
opción Show As Class.
2. En la clase que representa a la relación, añadir dos nuevos atributos de tipo
String: SourceMultiplicity y TargetMultiplicity.
3. De manera opcional, en la ventana de propiedades de dichos atributos
ponerles un valor por defecto en el campo Default.
4. Ejecutar el DslDm2Dd, y en la ventana de Text Decorators seleccionar la
posición donde queremos que aparezcan nuestras cardinalidades (recordar
que las posiciones centrales aún no están implementadas).
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
69
5. Crear y/o modificar el fichero AggregationCardinality.cs:
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.VisualStudio.Modeling.Validation;
using Microsoft.VisualStudio.Modeling;
namespace
Proyecto.Fincarrera.ObjectOrientedMultidimensionalModel.DomainModel
{
/// <summary>
/// Partial class for staging the validation methods for
ModelCardinality
/// </summary>
[ValidationState(ValidationState.Enabled)]
public partial class Fact
{
/// <summary>
/// Ensure the cardinality is correct. Only values 0, 0..1,
0..*, 1, 1..*
/// </summary>
/// <param name="context"></param>
[ValidationMethod(ValidationCategory.Open |
ValidationCategory.Save |
ValidationCategory.Menu)]
private void ValidateCardinality(ValidationContext context)
{
foreach (ElementLink link in
this.GetElementLinks(Aggregation.FactMetaRoleGuid))
{
Aggregation aggregation = link as Aggregation;
//Validar la multiplicidad en el origen
if (aggregation.SourceMultiplicity != "0" &&
aggregation.SourceMultiplicity != "0..1" &&
aggregation.SourceMultiplicity != "0..*" &&
aggregation.SourceMultiplicity != "1" &&
aggregation.SourceMultiplicity != "1..*")
{
string warning =
string.Format(DomainModel_Resource.IncorrectAggregationCardinality);
context.LogError(warning, "Error 01", this);
}
//Validar la multiplicidad en el destino
if (aggregation.TargetMultiplicity != "0" &&
aggregation.TargetMultiplicity != "0..1" &&
aggregation.TargetMultiplicity != "0..*" &&
aggregation.TargetMultiplicity != "1" &&
aggregation.TargetMultiplicity != "1..*")
{
string warning =
string.Format(DomainModel_Resource.IncorrectAggregationCardinality);
context.LogError(warning, "Error 01", this);
}
}
}
}
}
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
70
Como se puede ver, es un código sencillo. Son necesarias dos referencias para
todos estos ficheros de validaciones:
using System.Collections.Generic;
using Microsoft.VisualStudio.Modeling.Validation;
Como las restricciones son siempre sobre una clase, debemos sobrescribirla usando
el public partial class, y justo encima de ella la etiqueta
[ValidationState(ValidationState.Enabled)], para indicar que se va a hacer una validación.
Namespace
Proyecto.Fincarrera.ObjectOrientedMultidimensionalModel.DomainModel
{
/// <summary>
/// Partial class for staging the validation methods for
ModelCardinality
/// </summary>
[ValidationState(ValidationState.Enabled)]
public partial class Fact
{
...
Para cada clase podremos tener uno o varios métodos que indiquen cada una de las
restricciones. Estos métodos deberán tener siempre delante de ellos la etiqueta
ValidationMethod, que indicará en los casos en los que se debe producir la validación. Por
ejemplo, aquí indicamos que debe lanzarse al abrir, al salvar y al seleccionarlo del menú
contextual.
/// <summary>
/// Ensure the cardinality is correct. Only values 0, 0..1,
0..*, 1, 1..*
/// </summary>
/// <param name="context"></param>
[ValidationMethod(ValidationCategory.Open |
ValidationCategory.Save |
ValidationCategory.Menu)]
private void ValidateCardinality(ValidationContext context)
{
...
Sobre el código incluido en el método, lo que hace primero es extraer todas las
relaciones de la agregación con this.GetElementLinks(Aggregation.FactMetaRoleGuid).
Casteamos cada link como una aggregation y entonces ya podemos acceder a sus atributos,
como SourceMultiplicity y TargetMultiplicity. Hay cinco posibles cardinalidades: “0”,
“0..1”, “0..*”, “1” y “1..*”. Si dichos atributos no corresponden a ninguno de esos valores,
se mostrará un mensaje de error. Y para poder hacerlo, sólo hay que rellenar la variable
warning con el string correspondiente del fichero de recursos del DomainModel, y hacer la
llamada con context.LogError. De esta manera conseguiremos hacer aparecer el error en la
ventanita Error List del Visual Studio.
foreach (ElementLink link in
this.GetElementLinks(Aggregation.FactMetaRoleGuid))
{
Aggregation aggregation = link as Aggregation;
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
71
//Validar la multiplicidad en el origen
if (aggregation.SourceMultiplicity != "0" &&
aggregation.SourceMultiplicity != "0..1" &&
aggregation.SourceMultiplicity != "0..*" &&
aggregation.SourceMultiplicity != "1" &&
aggregation.SourceMultiplicity != "1..*")
{
string warning =
string.Format(DomainModel_Resource.IncorrectAggregationCardinality);
context.LogError(warning, "Error 01", this);
}
//Validar la multiplicidad en el destino
if (aggregation.TargetMultiplicity != "0" &&
aggregation.TargetMultiplicity != "0..1" &&
aggregation.TargetMultiplicity != "0..*" &&
aggregation.TargetMultiplicity != "1" &&
aggregation.TargetMultiplicity != "1..*")
{
string warning =
string.Format(DomainModel_Resource.IncorrectAggregationCardinality);
context.LogError(warning, "Error 01", this);
}
}
Después de realizar todos estos cambios sólo queda generar templates, compilar y
depurar. Podemos probar el funcionamiento creando un conector Aggregation entre un
Fact y una Dimension. Si habíamos puesto valor por defecto aparecerán dichos valores en
el conector. Si no, podemos modificarlos bien directamente del modelo, haciendo doble-
click sobre las cardinalidades, o bien cambiando dichos valores en la ventana de
propiedades del conector, donde pone SourceMultiplicity y TargetMultiplicity.
Restricciones de la clase Base
En el caso de las clases Base tenemos más de una validación que realizar. Las
referencias y las etiquetas encima de la declaración de la clase se hacen de igual manera
que en el apartado anterior, por lo tanto nos limitaremos a explicar los métodos de
validación.
La primera de ellas es también la de las cardinalidades. Se realiza de la misma
forma que en las agregaciones, por lo tanto sólo pondremos el código:
/// <summary>
/// Ensure the cardinality is correct. Only values 0, 0..1,
0..*, 1, 1..*
/// </summary>
/// <param name="context"></param>
[ValidationMethod(ValidationCategory.Open |
ValidationCategory.Save |
ValidationCategory.Menu)]
private void ValidateCardinality(ValidationContext context)
{
foreach (ElementLink link in
this.GetElementLinks(BaseAssociatesBase.baseAssociates2MetaRoleGuid))
{
BaseAssociatesBase association = link as
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
72
BaseAssociatesBase;
//Validar la multiplicidad en el origen
if (association.SourceMultiplicity != "0" &&
association.SourceMultiplicity != "0..1" &&
association.SourceMultiplicity != "0..*" &&
association.SourceMultiplicity != "1" &&
association.SourceMultiplicity != "1..*")
{
string warning =
string.Format(DomainModel_Resource.IncorrectBasesCardinality);
context.LogError(warning, "Error 02", this);
}
//Validar la multiplicidad en el destino
if (association.TargetMultiplicity != "0" &&
association.TargetMultiplicity != "0..1" &&
association.TargetMultiplicity != "0..*" &&
association.TargetMultiplicity != "1" &&
association.TargetMultiplicity != "1..*")
{
string warning =
string.Format(DomainModel_Resource.IncorrectBasesCardinality);
context.LogError(warning, "Error 02", this);
}
}
}
Además de las cardinalidades, hay que asegurarse que los nombres de los roles son
correctos. La manera de crearlos en exactamente igual a cómo hacíamos con las
cardinalidades (añadir atributos a la clase que representa la relación, ponerle valor por
defecto, añadirle los Text Decorators al designer…). Y el código del método que los valida
es el siguiente:
/// <summary>
/// Ensure the Role names are correct . Only values +r, +d
/// Ensure the Roles in each end are different
/// </summary>
/// <param name="context"></param>
[ValidationMethod(ValidationCategory.Open |
ValidationCategory.Save |
ValidationCategory.Menu)]
private void ValidateRoles(ValidationContext context)
{
foreach (ElementLink link in
this.GetElementLinks(BaseAssociatesBase.baseAssociates1MetaRoleGuid))
{
BaseAssociatesBase association = link as
BaseAssociatesBase;
//Rol en el origen
if (association.SourceRole!="+r" &&
association.SourceRole!="+d")
{
string warning =
string.Format(DomainModel_Resource.IncorrectBaseRoles, this.Name);
context.LogError(warning, "Error 03", this);
}
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
73
//Rol en el destino
if (association.TargetRole!="+r" &&
association.TargetRole!="+d")
{
string warning =
string.Format(DomainModel_Resource.IncorrectBaseRoles, this.Name);
context.LogError(warning, "Error 03", this);
}
//Extremos distintos
if (association.SourceRole == association.TargetRole)
{
string warning =
string.Format(DomainModel_Resource.SameBaseRoles, this.Name);
context.LogError(warning, "Error 04", this);
}
}
}
El código es el mismo, tan sólo se cambian las condiciones del if y el enlace al
mensaje de error. Únicamente se añade una parte en la que se comprueba que ambos
extremos sean distintos, ya que no pueden haber dos roles +d o dos roles +r, deben ser
diferentes en ambos extremos. Dicha condición es la siguiente:
//Extremos distintos
if (association.SourceRole == association.TargetRole)
{
string warning =
string.Format(DomainModel_Resource.SameBaseRoles, this.Name);
context.LogError(warning, "Error 04", this);
}
La siguiente validación nos exige tener en cada Base un Descriptor Attribute para
que el modelo sea correcto. De no ser así, lanzará un error. La forma de saber el número de
elementos de un determinado atributo es this.nombreDelAtributo.Count.
/// <summary>
/// Ensure the Base have one (and only one) Descriptor attribute
/// </summary>
/// <param name="context"></param>
[ValidationMethod(ValidationCategory.Open |
ValidationCategory.Save |
ValidationCategory.Menu)]
private void OneDescriptorAttribute(ValidationContext context)
{
if (this.descriptor.Count != 1)
{
string warning =
string.Format(DomainModel_Resource.BaseConstraint22);
context.LogError(warning, "Error 22", this);
}
}
Y por último, queda comprobar el número de atributos OID, ya que sólo puede 0 o
1, esto es, un atributo OID como máximo. El código para esta validación es:
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
74
/// <summary>
/// Ensure the Base have not more than one OID attribute
/// </summary>
/// <param name="context"></param>
[ValidationMethod(ValidationCategory.Open |
ValidationCategory.Save |
ValidationCategory.Menu)]
private void ZeroOrOneOIDAttribute(ValidationContext context)
{
if (this.OID.Count > 1)
{
string warning =
string.Format(DomainModel_Resource.BaseConstraint23);
context.LogError(warning, "Error 23", this);
}
}
Restricciones de los conectores del DegenerateFact
En fichero se encuentra la restricción que nos impone que un DegenerateFact debe
estar relacionado tanto con una clase Fact como con una clase Dimension. Si no está
relacionada con ambas, deberá producirse un error. El código relativo a esta restricción se
encuentra en DegenerateFact.cs, y es el siguiente:
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.VisualStudio.Modeling.Validation;
using Microsoft.VisualStudio.Modeling;
namespace
Proyecto.Fincarrera.ObjectOrientedMultidimensionalModel.DomainModel
{
/// <summary>
/// Partial class for staging the validation methods for
DegenerateFact
/// </summary>
[ValidationState(ValidationState.Enabled)]
public partial class DegenerateFact
{
/// <summary>
/// Ensure that a Degenerate Fact is connected with a Dimension
and a Fact
/// </summary>
/// <param name="context"></param>
[ValidationMethod(ValidationCategory.Open |
ValidationCategory.Save |
ValidationCategory.Menu)]
private void ValidateDFLinks(ValidationContext context)
{
if (this.degenerateFactAssociatesFactClass.Count < 2)
{
string warning =
string.Format(DomainModel_Resource.DegeneratedFactConstraint31bis);
context.LogError(warning, "Error DD", this);
}
}
}
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
75
}
Como se puede apreciar, tan solo se comprueba el número de conectores que tiene
asociados una clase DegenerateFact. La razón es porque la restricción de que tan sólo se
pueda conectar a un Fact y/o a una Dimension se hace en tiempo de ejecución, y
corresponde a una hard-constraint. Por lo tanto sólo nos quedará comprobar que el número
de enlaces desde ese DegenerateFact es correcto, ya que si tiene dos enlaces es seguro que
uno irá conectado a una clase Fact, y el otro irá conectado a una clase Dimension (para más
detalles sobre esa restricción ver en el apartado de las hard constraints la parte del
DegenerateFactConnector).
Atributos derivados
Recordemos que en el modelo de dominio existen cinco clases atributo:
DegenerateDimension, FactAttribute, OID, Descriptor y DimensionAttribute. Todas ellas
heredan de una superclase Attribute, y tienen la particularidad de que todas son usadas
como atributos embebidos en otras clases, ya que se utilizan como atributos de ellas.
Pues bien, si desglosamos la clase Attribute veremos que hay una propiedad de tipo
Boolean llamada isDerived. Esto significa que algunos de los atributos pueden ser
derivados y que, por lo tanto, tendrán una regla de derivación. Pero tan sólo pueden ser
derivados los atributos FactAttribute, Descriptor y DimensionAttribute. Si los atributos
OID y DegenerateDimension se les da el valor de atributos derivados, deberemos mostrar
un error. A su vez, para aquellos atributos que son derivados, se dispone de otra propiedad
llamada derivationRule. Si un atributo es derivado, y no tiene definida en dicha propiedad
una regla de derivación, también estaremos ante un modelo incorrecto.
Resumiendo, hay dos posible tipos de errores:
1. Para OID y DegenerateDimension: no pueden ser derivados. Habrá que dar
un error si el valor de su propiedad isDerived es igual a True.
2. Para FactAttribute, Descriptor y DimensionAttribute: si son derivados,
deben tener una regla de derivación. En caso de que el valor de la propiedad
isDerived sea true, deberemos comprobar que el campo derivationRule no
esté vacío.
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
76
El código para resolver dichas restricciones se encuentra en el fichero
DerivedAttributes.cs, y es el siguiente:
Para DegenerateDimension
/// <summary>
/// Partial class for staging the validation methods for
DegenerateDimension
/// </summary>
[ValidationState(ValidationState.Enabled)]
public partial class DegenerateDimension
{
/// <summary>
/// Ensure that a DegenerateDimension cannot be derived
/// </summary>
/// <param name="context"></param>
[ValidationMethod(ValidationCategory.Open |
ValidationCategory.Save |
ValidationCategory.Menu)]
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
77
private void NoDerived(ValidationContext context)
{
if (this.isDerived==true)
{
string warning =
string.Format(DomainModel_Resource.DegenerateDimensionConstraint33);
context.LogError(warning, "Error 33", this);
}
}
}
Con this.isDerived accedemos al valor de la propiedad isDerived. El error
correspondiente se encuentra en el fichero de recursos del DomainModel.
Para FactAttribute:
/// <summary>
/// Partial class for staging the validation methods for
FactAttribute
/// </summary>
[ValidationState(ValidationState.Enabled)]
public partial class FactAttribute
{
/// <summary>
/// If a FactAttribute is derived, ensure that it has a
derivationRule
/// </summary>
/// <param name="context"></param>
[ValidationMethod(ValidationCategory.Open |
ValidationCategory.Save |
ValidationCategory.Menu)]
private void derivedHasDerivationRule(ValidationContext context)
{
if (this.isDerived == true && (this.derivationRule==null ||
this.derivationRule==""))
{
string warning =
string.Format(DomainModel_Resource.FactAttributeConstraint36);
context.LogError(warning, "Error 36", this);
}
}
}
Comprobamos que la propiedad derivationRule no sea ni null ni la cadena vacia.
Para OID es igual que para el caso de DegenerateDimension, y para Descriptor y
DimensionAttribute se hace de la misma manera que en FactAttribute, por lo tanto no
repetiremos el código.
Hard Constraints
En contraste a las soft-constraints, existe este otro tipo de sistema de restricciones,
las hard-constraints. Aquí, el chequeo de restricciones se produce a la vez que se utilizan
los conectores en el entorno gráfico, por lo tanto en tiempo de ejecución. Si se produce una
situación no permitida (una conexión semánticamente incorrecta), inmediatamente aparece
un símbolo de prohibición, indicando que no se puede continuar.
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
78
Este tipo de restricciones es mucho más sencillo a la hora de utilizarlo, ya que
conforme se va construyendo el modelo se tiene la certeza de que es válido. Sin embargo
puede llegar a ralentizar la ejecución si hay muchas restricciones o con modelos muy
grandes si las restricciones obligan a ir navegando por cada una de las otras Shapes
asociadas. Por esa razón a veces es recomendable no abusar de las hard-constraints, y
utilizar en su lugar soft-constraints.
Aquí las restricciones están relacionadas con el proyecto del Designer, al tratarse de
restricciones sobre el modelo gráfico, por lo que su código está incluido en una carpeta de
dicho proyecto. Hemos llamado a la carpeta “Custom”.
Los ficheros que incluye son los que definen las restricciones. A diferencia de las
soft-constraints, aquí las restricciones no se establecen por cada clase, sino por cada
conector, así que tendremos un fichero por cada conector afectado. Además hemos creado
un fichero llamado “library.cs”, con métodos que son utilizados por varias clases, por lo
que muchas de las anteriores lo incluyen en su código para poder hacer uso de ellos.
Para entender dicho código, vamos a explicar algunos puntos comunes para todas
las clases (menos para el “library.cs”, obviamente):
El namespace debe ser el de la ruta que corresponde al proyecto del
Designer, no el de la carpeta donde está contenido el fichero. Esto hay que
tenerlo en cuenta porque cuando se crea un fichero nuevo dentro de una
carpeta pone el namespace de la carpeta por defecto:
Namespace
Proyecto.Fincarrera.ObjectOrientedMultidimensionalModel.Designer.Custom
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
79
La clase debe ser una public partial class, y el nombre de la clase debe ser
el del conector (por ejemplo, AggregationConnector), que se puede
consultar en el fichero Designer.dsldd, más la cadena ConnectionType.
Public partial class AggregationConnectorConnectionType
{ ...
El método que decide si se puede crear o no una conexión es el
CanCreateConnection. Se le pasan tres parámetros:
o sourceShapeElement: la Shape origen.
o targetShapeElement: la Shape destino.
o connectionWarning: un string que nos muestra un mensaje de error
en forma de Tooltip cuando no se puede realizar la conexión.
Public override bool CanCreateConnection
(ShapeElement sourceShapeElement, ShapeElement targetShapeElement, ref
string connectionWarning)
{ ...
Opcionalmente, se ha añadido en algunos ficheros otro método, el
CanSelfConnect. Con él indicamos que no se pueden crear conexiones de
una Shape a la misma Shape, evitando así ciclos. Realmente nunca se
llegará a acceder a dicho método, ya que en las clases donde aparece este
caso ya se ha hecho esta comprobación en el método CanCreateConnection,
con el objeto de mostrar el Tooltip que nos dice la razón de por qué no se
puede realizar dicha conexión. Por lo tanto, tan sólo se ha dejado a modo
informativo.
/// <summary>
/// Prevent links of this type connecting back to same object.
/// </summary>
protected override bool CanSelfConnect
{
get { return false; }
}
Una vez aclarados estos puntos, pasemos a explicar en profundidad cada uno de los
ficheros que contienen las restricciones para cada conector.
AggregationConnector
Como se ha explicado antes la forma general de los ficheros de restricciones, tanto
en éste como en los sucesivos nos limitaremos a explicar el contenido del método
CanCreateConnection.
public override bool CanCreateConnection
(ShapeElement sourceShapeElement, ShapeElement targetShapeElement, ref
string connectionWarning)
{
ShapeElement realSource =
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
80
ConnectorConnectAction.TopLevelShape(sourceShapeElement);
ShapeElement realTarget =
ConnectorConnectAction.TopLevelShape(targetShapeElement);
if ((realSource is FactShape) && !(realTarget is
DimensionShape))
{
connectionWarning =
String.Format(System.Globalization.CultureInfo.CurrentCulture,
Diagram.Designer_Resource.FactConstraint15);
return false;
}
else if (realSource is DimensionShape && realTarget is
FactShape)
{
connectionWarning =
String.Format(System.Globalization.CultureInfo.CurrentCulture,
Diagram.Designer_Resource.DimensionConstraint17);
return false;
}
else if (realSource is DimensionShape)
{
connectionWarning =
String.Format(System.Globalization.CultureInfo.CurrentCulture,
Diagram.Designer_Resource.DimensionConstraint19bis);
return false;
}
else if (realSource is BaseShape)
{
connectionWarning =
String.Format(System.Globalization.CultureInfo.CurrentCulture,
Diagram.Designer_Resource.BaseConstraint24bis);
return false;
}
else if (realSource is DegenerateFactShape)
{
connectionWarning =
String.Format(System.Globalization.CultureInfo.CurrentCulture,
Diagram.Designer_Resource.DegenerateFactConstraint34bis);
return false;
}
else
return base.CanCreateConnection(sourceShapeElement,
targetShapeElement, ref connectionWarning);
}
En todos los ficheros de este estilo, el primero de los pasos es asignar a dos
variables el valor real de la Shape de origen y de la Shape destino, que son realSource y
realTarget respectivamente.
Después de esto, pasamos a los distintos casos, anidados en forma de if-else.
Recordemos que estamos en el conector Aggregation, que une a un Fact con una
Dimension. Para el significado de los errores, consultar el apartado Mensajes de error. Los
posibles casos de error son:
Que el origen sea un Fact y el destino no sea una Dimension
FactConstraint 15.
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
81
Que el origen sea una Dimension y el destino sea un Fact (recordemos que
el conector es unidireccional, y debe ir de Fact a Dimension)
DimensionConstraint 17.
Que el origen sea una Dimension y el destino cualquier otra Shape
DimensionConstraint 19bis.
Que el origen sea una Base BaseConstraint24bis.
Que el origen sea un DegenerateFact DegenerateFactConstraint34bis.
Cualquier otro caso (en principio imposible) devuelve el caso base.
AssociationConnector
Este es el conector que une una Dimension con una Base. En este caso se trata de
un conector bidireccional (se debe poder conectar ambas Shapes en los dos sentidos), lo
que habrá que tener en cuenta a la hora de establecer las restricciones. El código del
CanCreateConnection es:
public override bool CanCreateConnection
(ShapeElement sourceShapeElement, ShapeElement targetShapeElement, ref
string connectionWarning)
{
ShapeElement realSource =
ConnectorConnectAction.TopLevelShape(sourceShapeElement);
ShapeElement realTarget =
ConnectorConnectAction.TopLevelShape(targetShapeElement);
//Unica asociacion posible: dimension-base o base-dimension
if ((realSource is DimensionShape && realTarget is
BaseShape) || (realSource is BaseShape && realTarget is DimensionShape))
{
//Desde la Dimension, comprobar que no esté ya conectada
a una Base
NodeShape dimension, nodoBase;
if (realSource is DimensionShape)
{
dimension = realSource as NodeShape;
nodoBase = realTarget as NodeShape;
}
else
{
dimension = realTarget as NodeShape;
nodoBase = realSource as NodeShape;
}
//Shapes conectadas
System.Collections.Generic.List<Shape> connectedShapes =
library.GetConnectedShapes(dimension);
foreach (Shape shape in connectedShapes)
if (shape is BaseShape)
{
connectionWarning =
String.Format(System.Globalization.CultureInfo.CurrentCulture,
Diagram.Designer_Resource.DimensionConstraint20);
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
82
return false;
}
//Evitar que una Base que ya es hija en una herencia
pertenezca a una asociacion
if (library.IsAlreadyChild(nodoBase))
{
connectionWarning =
String.Format(System.Globalization.CultureInfo.CurrentCulture,
Diagram.Designer_Resource.BaseConstraint29);
return false;
}
return base.CanCreateConnection(sourceShapeElement,
targetShapeElement, ref connectionWarning);
}
------------------------------------------------------------------------
else if (realSource is FactShape)
{
connectionWarning =
String.Format(System.Globalization.CultureInfo.CurrentCulture,
Diagram.Designer_Resource.FactConstraint14);
return false;
}
else if (realSource is DimensionShape)
{
connectionWarning =
String.Format(System.Globalization.CultureInfo.CurrentCulture,
Diagram.Designer_Resource.DimensionConstraint19bis);
return false;
}
else if (realSource is BaseShape)
{
connectionWarning =
String.Format(System.Globalization.CultureInfo.CurrentCulture,
Diagram.Designer_Resource.BaseConstraint24bis);
return false;
}
else if (realSource is DegenerateFactShape)
{
connectionWarning =
String.Format(System.Globalization.CultureInfo.CurrentCulture,
Diagram.Designer_Resource.DegenerateFactConstraint34bis);
return false;
}
else
return base.CanCreateConnection(sourceShapeElement,
targetShapeElement, ref connectionWarning);
}
El método se divide en dos partes claramente diferenciadas. Se ha puesto una línea
separatoria discontinua para distinguirlas. La primera de ellas la determina el primer if, que
es el caso de cuando se realiza una conexión (en principio) correcta (de Dimension a Base
o de Base a Dimension). La segunda parte son conexiones inválidas entre otras Shapes con
este conector (y que está de la misma forma presente en todos los demás archivos para
poder presentar el mensaje de error correspondiente).
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
83
En la primera parte lo primero que se hace es asignar a sendas variables el valor de
la Base (nodoBase) y el valor de la Dimension (dimension) para que sean independientes
de si son origen o destino, y poder trabajar con ellas más cómodamente. Hay dos errores
que pueden llegar a darse:
Que la Dimension esté ya conectada a una Base. Una Dimension solo puede
conectarse a una Base. Se utiliza el método GetConnectedShapes:
//Shapes conectadas
System.Collections.Generic.List<Shape> connectedShapes =
library.GetConnectedShapes(dimension);
foreach (Shape shape in connectedShapes)
if (shape is BaseShape)
{
connectionWarning =
String.Format(System.Globalization.CultureInfo.CurrentCulture,
Diagram.Designer_Resource.DimensionConstraint20);
return false;
}
Devuelve una colección de Shapes conectadas a la Dimension y se guarda
en la variable connectedShapes. Si entre ellas encuentra alguna Base,
mostrará el mensaje de error DimensionConstraint20.
Que una Base que ya es hija en una herencia pertenezca a una asociación.
Por asociación se entiende tanto las asociaciones entre Bases como la
asociación entre Base y Dimension. Por lo tanto aquí también se debe tener
en cuenta este error. Se utiliza el método IsAlreadyChild de la librería
BaseConstraint29.
//Evitar que una Base que ya es hija en una herencia pertenezca a una
asociacion
if (library.IsAlreadyChild(nodoBase))
{
connectionWarning =
String.Format(System.Globalization.CultureInfo.CurrentCulture,
Diagram.Designer_Resource.BaseConstraint29);
return false;
}
Si no se da ninguno de estos errores estará todo correcto, con lo que devolverá el
caso base.
En la segunda parte las Shapes que se intentan unir con este conector no son las
correctas, por lo que se comprueba qué Shapes estamos intentando conectar para dar el
mensaje de error adecuado. Posibilidades:
Que el origen sea un Fact FactConstraint14.
Que el origen sea una Dimension (y el destino, por lo tanto, no sea una
Base) DimensionConstraint19bis.
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
84
Que el origen sea una Base (y el destino no sea una Dimension)
BaseConstraint24bis.
Que el origen sea un DegenerateFact DegenerateFactConstraint34bis.
BaseAssociatesBaseConnector
Este conector une dos bases entre sí. Al igual que en caso anterior hay que
distinguir dos partes: una en la que unimos realmente dos bases, pero se produce alguna
situación no permitida; y la segunda en la que se intenta utilizar alguna Shape que no es
una Base con este conector.
public override bool CanCreateConnection(ShapeElement
sourceShapeElement, ShapeElement targetShapeElement, ref string
connectionWarning)
{
ShapeElement realSource =
ConnectorConnectAction.TopLevelShape(sourceShapeElement);
ShapeElement realTarget =
ConnectorConnectAction.TopLevelShape(targetShapeElement);
//Evitar asociaciones consigo mismo
if (realSource is BaseShape && realTarget is BaseShape)
//Evitar ciclos consigo mismo
if (sourceShapeElement == targetShapeElement)
{
connectionWarning =
String.Format(System.Globalization.CultureInfo.CurrentCulture,
Diagram.Designer_Resource.BaseConstraint25);
return false;
}
//Evitar que una Base que ya es hija en una herencia
pertenezca a una asociacion
else
{
NodeShape baseOrigen = realSource as NodeShape;
NodeShape baseDestino = realTarget as NodeShape;
if (library.IsAlreadyChild(baseOrigen) ||
library.IsAlreadyChild(baseDestino))
{
connectionWarning =
String.Format(System.Globalization.CultureInfo.CurrentCulture,
Diagram.Designer_Resource.BaseConstraint29);
return false;
}
return base.CanCreateConnection(sourceShapeElement,
targetShapeElement, ref connectionWarning);
}
------------------------------------------------------------------------
else if (realSource is FactShape)
{
connectionWarning =
String.Format(System.Globalization.CultureInfo.CurrentCulture,
Diagram.Designer_Resource.FactConstraint14);
return false;
}
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
85
else if (realSource is DimensionShape)
{
connectionWarning =
String.Format(System.Globalization.CultureInfo.CurrentCulture,
Diagram.Designer_Resource.DimensionConstraint19bis);
return false;
}
else if (realSource is BaseShape)
{
connectionWarning =
String.Format(System.Globalization.CultureInfo.CurrentCulture,
Diagram.Designer_Resource.BaseConstraint24bis);
return false;
}
else if (realSource is DegenerateFactShape)
{
connectionWarning =
String.Format(System.Globalization.CultureInfo.CurrentCulture,
Diagram.Designer_Resource.DegenerateFactConstraint34bis);
return false;
}
else
return base.CanCreateConnection(sourceShapeElement,
targetShapeElement, ref connectionWarning);
}
En la primera parte, para el caso que se unen dos bases entre sí nos podemos
encontrar:
Que se intente unir una base consigo misma BaseConstraint25.
//Evitar ciclos consigo mismo
if (sourceShapeElement == targetShapeElement)
{
connectionWarning =
String.Format(System.Globalization.CultureInfo.CurrentCulture,
Diagram.Designer_Resource.BaseConstraint25);
return false;
}
Que se intente conectar una Base que ya es hija en una herencia entre Bases
BaseConstraint29
if (library.IsAlreadyChild(baseOrigen) ||
library.IsAlreadyChild(baseDestino))
{
connectionWarning =
String.Format(System.Globalization.CultureInfo.CurrentCulture,
Diagram.Designer_Resource.BaseConstraint29);
return false;
}
En la segunda parte, se testean los demás casos:
Que el origen sea un Fact FactConstraint14.
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
86
Que el origen sea una Dimension DimensionConstraint19bis.
Que el origen sea una Base (y el destino cualquier otra Shape)
BaseConstraint24bis.
Que el origen sea un DegenerateFact DegenerateFactConstraint34bis.
BaseInheritsBaseConnector
También conector entre Bases, pero en este caso con una relación de herencia
padre-hijo.
public override bool CanCreateConnection(ShapeElement
sourceShapeElement, ShapeElement targetShapeElement, ref string
connectionWarning)
{
ShapeElement realSource =
ConnectorConnectAction.TopLevelShape(sourceShapeElement);
ShapeElement realTarget =
ConnectorConnectAction.TopLevelShape(targetShapeElement);
//Evitar que una Base que pertenezca a una asociacion sea
hija en una herencia
NodeShape nodoBase = realSource as NodeShape;
if (realSource is BaseShape && realTarget is BaseShape &&
sourceShapeElement != targetShapeElement)
if (library.IsAlreadyChild(nodoBase))
{
connectionWarning =
String.Format(System.Globalization.CultureInfo.CurrentCulture,
Diagram.Designer_Resource.BaseConstraint28);
return false;
}
else if (library.HasAssociationConnectors(nodoBase))
{
connectionWarning =
String.Format(System.Globalization.CultureInfo.CurrentCulture,
Diagram.Designer_Resource.BaseConstraint29);
return false;
}
else
return base.CanCreateConnection(sourceShapeElement,
targetShapeElement, ref connectionWarning);
------------------------------------------------------------------------
else if (realSource is BaseShape)
{
connectionWarning =
String.Format(System.Globalization.CultureInfo.CurrentCulture,
Diagram.Designer_Resource.BaseConstraint26);
return false;
}
else if (realTarget is BaseShape)
{
connectionWarning =
String.Format(System.Globalization.CultureInfo.CurrentCulture,
Diagram.Designer_Resource.BaseConstraint27);
return false;
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
87
}
else if (realSource is FactShape)
{
connectionWarning =
String.Format(System.Globalization.CultureInfo.CurrentCulture,
Diagram.Designer_Resource.FactConstraint14);
return false;
}
else if (realSource is DimensionShape)
{
connectionWarning =
String.Format(System.Globalization.CultureInfo.CurrentCulture,
Diagram.Designer_Resource.DimensionConstraint19bis);
return false;
}
else if (realSource is DegenerateFactShape)
{
connectionWarning =
String.Format(System.Globalization.CultureInfo.CurrentCulture,
Diagram.Designer_Resource.DegenerateFactConstraint34bis);
return false;
}
else
return base.CanCreateConnection(sourceShapeElement,
targetShapeElement, ref connectionWarning);
}
Aquí en la primera parte no se debe cumplir solo que el origen y el destino sean
clases Base, también deben ser Bases distintas entre ellas (el caso de que sean la misma
Base se comprueba en la segunda parte).
Tendremos dos posibilidades de error:
Que la clase Base que va a ser hija en la herencia (nodoBase) no sea ya hija
en otra herencia (no se permite herencia múltiple) BaseConstraint28.
if (library.IsAlreadyChild(nodoBase))
{
connectionWarning =
String.Format(System.Globalization.CultureInfo.CurrentCulture,
Diagram.Designer_Resource.BaseConstraint28);
return false;
}
Que la clase Base que va a ser hija en la herencia (nodoBase) no pertenezca
ya a una asociación BaseConstraint29.
else if (library.HasAssociationConnectors(nodoBase))
{
connectionWarning =
String.Format(System.Globalization.CultureInfo.CurrentCulture,
Diagram.Designer_Resource.BaseConstraint29);
return false;
}
En esta última se utiliza el método HasAssociationConnectors, de la librería.
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
88
En la segunda parte nos podemos encontrar:
Que el origen sea una Base (y el destino cualquier otra Shape o la misma
Base) BaseConstraint26.
Que el destino sea una Base (y el origen cualquier otra Shape)
BaseConstraint27.
Que el origen sea un Fact FactConstraint14.
Que el origen sea una Dimension DimensionConstraint19bis..
Que el origen sea un DegenerateFact DegenerateFactConstraint34bis.
DegenerateFactConnector
Este último conector es el relacionado con el DegenerateFact. Para que tuviese
sentido, un DegenerateFact debe estar conectado a un Fact por una parte, y a una
Dimension por otra, por lo tanto se necesitan dos conectores para cada vez.
Ya se habían añadido restricciones para esta clase en la parte de las soft-constraints
(ver apartado Restricciones de los conectores del DegenerateFact). Recordemos que dicha
restricciones obligaban a que un DegenerateFact estuviese conectado exactamente a dos
elementos. Sin embargo no se decía a qué elementos. Precisamente eso es lo que se va a
hacer aquí con las hard-constraints.
public override bool CanCreateConnection(ShapeElement
sourceShapeElement, ShapeElement targetShapeElement, ref string
connectionWarning)
{
ShapeElement realSource =
ConnectorConnectAction.TopLevelShape(sourceShapeElement);
ShapeElement realTarget =
ConnectorConnectAction.TopLevelShape(targetShapeElement);
if (realSource is DegenerateFactShape && (realTarget is
FactShape || realTarget is DimensionShape))
{
//Obtener las shapes conectadas al realSource, esto es,
al Degenerate Fact
System.Collections.Generic.List<Shape> connectedShapes =
library.GetConnectedShapes(realSource as NodeShape);
if (connectedShapes.Count >= 2)
{
connectionWarning =
String.Format(System.Globalization.CultureInfo.CurrentCulture,
Diagram.Designer_Resource.DegeneratedFactConstraint31);
return false;
}
else if (connectedShapes.Count == 1)
{
//Averiguar cual es la otra clase a la que está
conectada
Shape shape = connectedShapes[0];
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
89
if (shape.GetType() == realTarget.GetType())
{
connectionWarning =
String.Format(System.Globalization.CultureInfo.CurrentCulture,
Diagram.Designer_Resource.DegenerateFactConstraint32);
return false;
}
}
return base.CanCreateConnection(sourceShapeElement,
targetShapeElement, ref connectionWarning);
}
------------------------------------------------------------------------
else if (realSource is DegenerateFactShape)
{
connectionWarning =
String.Format(System.Globalization.CultureInfo.CurrentCulture,
Diagram.Designer_Resource.DegenerateFactConstraint32);
return false;
}
else if (realTarget is DegenerateFactShape)
{
connectionWarning =
String.Format(System.Globalization.CultureInfo.CurrentCulture,
Diagram.Designer_Resource.DegenerateFactConstraint33bis);
return false;
}
else if (realSource is FactShape)
{
connectionWarning =
String.Format(System.Globalization.CultureInfo.CurrentCulture,
Diagram.Designer_Resource.FactConstraint14);
return false;
}
else if (realSource is DimensionShape)
{
connectionWarning =
String.Format(System.Globalization.CultureInfo.CurrentCulture,
Diagram.Designer_Resource.DimensionConstraint19bis);
return false;
}
else if (realSource is BaseShape)
{
connectionWarning =
String.Format(System.Globalization.CultureInfo.CurrentCulture,
Diagram.Designer_Resource.BaseConstraint24bis);
return false;
}
else
return base.CanCreateConnection(sourceShapeElement,
targetShapeElement, ref connectionWarning);
}
Como siempre primero chequeamos el caso de estar conectando las Shapes
correctas, que son un DegenerateFact como origen y un Fact o una Dimension como
destino (recordemos que el DegenerateFactConnector se había definido como
unidireccional, y sólo se podrá realizar la conexión en ese sentido). Nos podemos encontrar
con dos posibles casos de error:
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
90
Que ya tengamos las dos Shapes (Fact y Dimension) conectadas y estemos
intentando conectar alguna más DegenerateFactConstraint31.
if (connectedShapes.Count >= 2)
{
connectionWarning =
String.Format(System.Globalization.CultureInfo.CurrentCulture,
Diagram.Designer_Resource.DegeneratedFactConstraint31);
return false;
}
Que ya haya una Shape conectada, y que la otra a la que intentemos
conectar el DegenerateFact sea del mismo tipo (esto es, intentar conectarse
a dos Facts o a dos Dimensions) DegenerateFactConstraint32.
else if (connectedShapes.Count == 1)
{
//Averiguar cual es la otra clase a la que está
conectada
Shape shape = connectedShapes[0];
if (shape.GetType() == realTarget.GetType())
{
connectionWarning =
String.Format(System.Globalization.CultureInfo.CurrentCulture,
Diagram.Designer_Resource.DegenerateFactConstraint32);
return false;
}
}
En la segunda parte del CanCreateConnection encontramos las posibilidades de:
Que el origen sea un DegenerateFact (entonces el destino no es ni un Fact ni
una Dimension) DegenerateFactConstraint32.
Que el destino sea un DegenerateFact. Probablemente se esté intentando
conectar las Shapes en sentido contrario DegenerateFactConstraint33bis.
Que el origen sea un Fact FactConstraint14.
Que el origen sea una Dimension DimensionConstraint19bis.
Que el origen sea una Base BaseConstraint24bis.
Library
Este archivo contiene las funciones que hemos ido utilizando en las restricciones
anteriores. Estas funciones son:
HasAssociationConnectors
static public bool HasAssociationConnectors(NodeShape nodeShape)
{
if (nodeShape != null)
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
91
{
System.Type tipo;
// go through the list of connectors for which this
shape is the From end.
foreach (ShapeElement shape in
nodeShape.FromRoleLinkShapes)
{
tipo = shape.GetType();
if (tipo.Name == "AssociationConnector" || tipo.Name
== "BaseAssociatesBaseConnector")
return true;
}
// go through the list of connectors for which this
shape is the To end.
foreach (ShapeElement shape in
nodeShape.ToRoleLinkShapes)
{
tipo = shape.GetType();
if (tipo.Name == "AssociationConnector" || tipo.Name
== "BaseAssociatesBaseConnector")
return true;
}
return false;
}
else
return false;
}
A partir del NodeShape que recibe como parámetro, busca si existe alguna relación
de asociación conectada a él. Con relación de asociación nos referimos tanto a la
asociación entre Bases como a la asociación Dimension-Base.
Como los roles pueden ir desde o hacia la Shape, se deben de comprobar ambos.
De ahí que existan dos “foreach”: FromRoleLinkShapes y ToRoleLinkShapes. En cada uno
de ellos se debe comprobar si el tipo de datos del conector es AssociationConnector o
BaseAssociatesBaseConnector. En caso de que sea así sabremos que existe una asociación
conectada a la Shape.
Este método se utiliza para el caso de las excepciones con las clases Base, debido a
que una clase base que pertenezca a una asociación no puede ser hija en una herencia entre
bases. Gracias al método podemos averiguar rápidamente si la Base pertenece a una
asociación.
IsAlreadyChild
static public bool IsAlreadyChild(NodeShape nodeShape)
{
System.Type tipo;
//En las relaciones de herencia nos interesa la clase Hija,
y por lo tanto el FromRole
foreach (ShapeElement shape in nodeShape.FromRoleLinkShapes)
{
tipo = shape.GetType();
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
92
if (tipo.Name == "BaseInheritsBaseConnector")
return true;
}
return false;
}
Para un NodeShape (que para nosotros será siempre una clase Base), comprueba si
hay alguna relación de herencia en la que dicho nodo sea hijo de la relación.
Por lo tanto estamos buscando los conectores de tipo BaseInheritsBaseConnector.
Y como en este caso solo nos interesan las clases hijas, sólo tendremos que buscar en el
role origen (FromRoleLinkShapes), que es desde donde se pinta la relación de herencia,
siempre de hijo a padre.
GetConnectedShapes
static public System.Collections.Generic.List<Shape>
GetConnectedShapes(NodeShape nodeShape)
{
System.Collections.Generic.List<Shape> connectedShapes = new
System.Collections.Generic.List<Shape>();
if (nodeShape != null)
{
// go through the list of connectors for which this
shape is the From end.
foreach (ShapeElement shape in
nodeShape.FromRoleLinkShapes)
{
Connector connector = shape as Connector;
if (connector != null && connector.ToShape != null)
{
// add the shape at the other end to the list.
connectedShapes.Add(connector.ToShape as Shape);
}
}
// go through the list of connectors for which this
shape is the To end.
foreach (ShapeElement shape in
nodeShape.ToRoleLinkShapes)
{
Connector connector = shape as Connector;
if (connector != null && connector.FromShape !=
null)
{
// add the shape at the other end to the list.
connectedShapes.Add(connector.FromShape as
Shape);
}
}
}
return connectedShapes;
}
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
93
Devuelve una colección de Shapes conectadas a un determinado nodo. Al igual que
en el método HasAssociationConnectors, hay que comprobar tanto en los roles que van
desde la Shape (FromRoleLinkShapes) como en aquellos que acaban en ella
(ToRoleLinkShapes), por ello que existan dos “foreach” que vayan rellenando la lista.
Mensajes de error
Se ha intentado seguir en la medida de lo posible la nomenclatura de los errores del
modelo de datos multidimensional cedido por la Universidad de Alicante. En dicho
modelo, estos errores están ubicados en un vector, donde cada uno tiene un número que
pertenece al índice del vector. En nuestro caso los hemos llamado de la siguiente manera:
nombre de la clase origen del error + Constraint + índice del error + [bis]
La palabra “bis” sólo se ha añadido para aquellos casos en los que hay algún
cambio en el texto del error con respecto al del artículo original, ya que puede llegar a
crear confusión al utilizar distintos conectores dependiendo de las Shapes que se conectan.
Esto es, en muchos errores se ha aclarado qué conector que se debe utilizar, ya que se
puede estar conectando dos clases que realmente pueden estar relacionadas entre sí, pero
no se está usando el conector correcto (por ejemplo, al intentar unir dos Bases con un
Association, en lugar de con un BasesAssociation).
NOTA: Sin embargo, no confundir los errores DegenerateFactConstraint33bis y
DegenerateFactConstraint34bis ya que son completamente nuevos, y no guardan relación
con el respectivo número en la lista de errores original (en realidad, dichos números
pertenecen a errores del DegenerateDimension, no del DegenerateFact).
Existen otros errores que sin embargo no pertenecen a dicha lista, ya que son
errores de otra índole (errores al escribir incorrectamente las cardinalidades o los roles en
las clases Base, por ejemplo).
Todos ellos están distribuidos entre los archivos de recursos del DomainModel y
los del Designer, dependiendo si el error es mostrado por una soft-constraint o por una
hard-constraint, respectivamente.
Lista de errores original (modelo de la Universidad de Alicante)
ErrorArray
Number Error Text
StarPackage constraints
0 "A StarPackage can only contain FactPackages or DimensionPackages"
1 "A StarPackage can only contain one FactPackage"
2 "A StarPackage cannot import a FactPackage from another StarPackage
(only DimensionPackages)"
3 "Cycles are not allowed in the dependency structure"
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
94
DimensionPackage constraints
4 "It is no possible to create a dependency from a DimensionPackage to a
FactPackage (only to another DimensionPackage)"
5 "Cycles are not allowed in the dependency structure"
6 "A DimensionPackage cannot contain Packages"
7 "A DimensionPackage can only contain Dimension or Base classes"
8 "A DimensionPackage must have a Dimension class (and only one)"
FactPackage constraints
9 "Cycles are not allowed in the dependency structure"
10 "A FactPackage cannot contain Packages"
11 "A FactPackage can only contain Fact, DegeneratedFact, Dimension or
Base classes"
12 "A FactPackage must have a Fact class (and only one)"
Fact constraints
13 "All attributes of a Fact must be DegeneratedDimensions or FactAttributes"
14 "All associations of a Fact must be aggregations (neither none nor
complete)"
15 "A Fact can only be associated with Dimension classes"
Dimension constraints
16 "A Dimension cannot have neither attributes nor methods"
17 "All associations of a Dimension with a Fact must be aggregations at the
end of the Fact (the opposite end)"
18 "All associations of a Dimension with a Fact must not be aggregations at
the end of the Dimension (the current end)"
19 "A Dimension can only be associated with Fact or Base classes"
20 "A Dimension can only be associated with one Base class"
Base constraints
21 "All attributes of a Base must be OID, Descriptor or DimensionAttribute"
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
95
22 "A Base must have a Descriptor attribute (and only one)"
23 "A Base may have an OID attribute"
24 "A Base can only be associated with Dimension or Base classes"
25 "A Base cannot be associated with itself (in order to avoid cycles)"
26 "A Base class may only inherited from another Base class"
27 "A Base class may only be parent of another Base class"
28 "A Base can only be child in one generalization (no multiple inheritance)"
29 "A Base cannot simultaneosly be a child in a generalization/specialization
hierarchy and belong to an association hierarchy"
DegenerateFact constraints
30 "All attributes of a DegenerateFact class must be DegenerateDimension or
FactAttribute"
31 "A DegenerateFact association can only be connected to two elements"
32 "One of the ends of a DegenerateFact has to be a Fact and the other end has
to be a Dimension
DegenerateDimension constraints
33 "A DegenerateDimension cannot be derived"
34 "A DegenerateDimension can only belong to a Fact or a DegenerateFact"
FactAttribute constraints
35 "A FactAttribute can only belong to a Fact or a DegenerateFact"
36 "If a FactAttribute is derived, then it needs a derivation rule (an OCL
expression)"
OID constraints
37 "An OID can only belong to a Base"
38 "An OID cannot be derived"
Descriptor constraints
39 "A Descriptor can only belong to a Base"
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
96
40 "If a Descriptor is derived, then it needs a derivation rule (an OCL
expression)"
DimensionAttribute constraints
41 "A DimensionAttribute can only belong to a Base"
42 "If a DimensionAttribute is derived, then it needs a derivation rule (an OCL
expression)"
Rolls-upTo constraints
43 "The ends of a Roll-upTo association can only be Base classes"
44 "A Roll-upTo association can only be connected to two elements"
45 "In a Roll-upTo association, one of the ends contains the role r and the other
end contains the role d"
Completeness constraints
46 "The ends of a Completeness association can only be Base classes"
47 "A Completeness association can only be connected to two elements"
48 "In a Completeness association, one of the ends contains the role r and the
other end contains the role d"
Correct Stereotypes
49 "Incorrect Stereotype. In a Class, you can only use Base, Dimension, Fact
and DegenerateFact Stereotypes"
50 "Incorrect Stereotype. In a Package, you can only use StarPackage,
DimensionPackage and FactPackage Stereotypes"
(*) Los errores en negrita son los que se han incluido en el proyecto.
Lista de errores del proyecto
Con respecto a la lista de errores anterior, existen algunos por diversas razones no
hemos incluido en nuestro proyecto:
Los errores del 0 al 12 y el 50 pertenecen a errores del StarPackage,
DimensionPackage y FactPackage, todos ellos referidos a los modelos de
1er y 2º nivel. Por limitaciones de la herramienta, solo se ha implementado
el modelo de 3er nivel, por lo que estos errores no aparecerán en el proyecto.
Los errores 13, 16, 21, 30, 34, 35, 37, 39 y 41 se refieren a los atributos
permitidos para cada clase. En nuestro modelo al estar dichos atributos
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
97
embebidos en las clases les obligamos a que sea así, por lo tanto nunca
podrá dar un error de este tipo.
El error 18. Este error y el anterior se refieren al sentido correcto de la
conexión entre Fact y Dimension. Sólo se puede errar si se conecta de
Dimension a Fact, y el mensaje de error en este caso queda reflejado por el
número 17, por lo que el 18 no nos hace falta.
Los errores 43, 44, 46 y 47 se refieren a los elementos conectados a la
RollsUp To y a la Completeness association. Ambos solo pueden estar
asociados a clases base porque la relación BaseAssociatesBase (que es la
que contiene el atributo type que indica si es RollsUp To o Completeness)
solo puede estar relacionado con clases Base. Y obviamente, dicho conector
solo puede esta conectado a dos elementos, por lo que esos errores no tienen
cabida en nuestro modelo.
Por último, el error 49 sobre los estereotipos siempre se cumplirá, ya que no
es posible elegir en un archivo oomm otros estereotipos distintos a Base,
Dimension, Fact o DegenerateFact.
Por lo tanto, una vez hechas todas las aclaraciones pertinentes, pasamos a describir
nuestra lista de errores:
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
98
Error Texto Archivo de recursos NOTAS
FactConstraint14
All associations of a Fact must be
aggregations (neither none nor
complete)
Designer.Resource.resx
FactConstraint15 A Fact can only be associated with
Dimension classes Designer.Resource.resx
DimensionConstraint17
All associations of a Dimension with a
Fact must be aggregations at the end of
the Fact (the opposite end)
Designer.Resource.resx
DimensionConstraint19bis
All associations of a Dimension must be
either aggregations with a Fact or
associations with Base classes
Designer.Resource.resx
Mismo significado que el
error 19, pero especificando
los conectores adecuados
DimensionConstraint20 A Dimension can only be associated
with one Base class Designer.Resource.resx
BaseConstraint22 A Base must have a Descriptor attribute
(and only one) DomainModel.Resource.resx
BaseConstraint23 A Base may have an OID attribute DomainModel.Resource.resx
BaseConstraint24bis
A Base can only be associated with
Dimension (Association connector) or
Base classes (BasesAssociation
connector)
Designer.Resource.resx
Exactamente el mismo que el
error 24, pero se especifica
además entre paréntesis con
que conectores debe usarse la
clase Base
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
99
Error Texto Archivo de recursos NOTAS
BaseConstraint25 A Base cannot be associated with itself
(in order to avoid cycles) Designer.Resource.resx
BaseConstraint26 A Base class may only inherited from
another Base class Designer.Resource.resx
BaseConstraint27 A Base class may only be parent of
another Base class Designer.Resource.resx
BaseConstraint28 A Base can only be child in one
generalization (no multiple inheritance) Designer.Resource.resx
BaseConstraint29
A Base cannot simultaneosly be a child
in a generalization/specialization
hierarchy and belong to an association
hierarchy
Designer.Resource.resx
DegeneratedFactConstraint31 A DegenerateFact association can only
be connected to two elements Designer.Resource.resx
DegeneratedFactConstraint31bis A DegenerateFact class must be
connected to two elements DomainModel.Resource.resx
Para asegurar que hay un
conector a Fact y otro a
Dimension
DegenerateFactConstraint32
One of the ends of a DegenerateFact has
to be a Fact and the other end has to be a
Dimension
Designer.Resource.resx
DegenerateFactConstraint33bis A DegenerateFact association must be
connected from a DegenerateFact class Designer.Resource.resx (NUEVO) Para asegurar que
se realiza la conexión en el
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
100
Error Texto Archivo de recursos NOTAS
to either a Dimension or a Fact class sentido correcto (siempre
desde el DegenerateFact)
DegenerateFactConstraint34bis
Use the DegenerateFact Association
connector to connect a DegenerateFact
class
Designer.Resource.resx
(NUEVO) Para asegurar que
se usa el conector adecuado
para unir un DegenerateFact
DegenerateDimensionConstraint33 A DegenerateDimension cannot be
derived DomainModel.Resource.resx
FactAttributeConstraint36
If a FactAttribute is derived, then it
needs a derivation rule (an OCL
expression)
DomainModel.Resource.resx
OIDConstraint38 An OID cannot be derived DomainModel.Resource.resx
DescriptorConstraint40 If a Descriptor is derived, then it needs a
derivation rule (an OCL expression) DomainModel.Resource.resx
DimensionAttributeConstraint42
If a DimensionAttribute is derived, then
it needs a derivation rule (an OCL
expression)
DomainModel.Resource.resx
Otros errores
IncorrectAggregationCardinality The aggregation cardinalities only can
be 0, 0..1, 0..*, 1 and 1..* DomainModel.Resource.resx
IncorrectBasesCardinality The bases association cardinalities only
can be 0, 0..1, 0..*, 1 and 1..* DomainModel.Resource.resx
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
101
Error Texto Archivo de recursos NOTAS
IncorrectBaseRoles
In a Rolls-upTo or Completeness
association, one of the ends contains the
role '+r' and the other end contains the
role '+d'
DomainModel.Resource.resx
Mezcla entre el error 45 y el
error 48. Se produce cuando
se introduce un valor no
válido (distinto a ‘+r’ o ‘+d’)
SameBaseRoles
In a Rolls-upTo or Completeness
association, one of the ends contains the
role '+r' and the other end contains the
role '+d'
DomainModel.Resource.resx
Exactamente el mismo que el
anterior, pero se produce
cuando se introduce un valor
válido pero ya existente en el
otro extremo
SaveValidationFailed There were validation errors, continue
save? Designer.Resource.resx Ver apartado Soft Constraints
SaveOperationCancelled The model was not saved. Designer.Resource.resx Ver apartado Soft Constraints
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
102
Menú de comandos
Llegados a este punto, ya disponemos de un modelo completo. El modelo lógico
está acabado, la interfaz del diseñador tiene el aspecto que buscábamos (dentro de las
limitaciones de la herramienta) y toda la semántica queda establecida tras definir las
validaciones y restricciones. También suponemos que está implementada la parte de
generación de código (ver punto III, Generación de Código). La única parte gráfica que
falta es la que relaciona el diseñador de ficheros oomm con la generación de código.
Durante las pruebas con la generación de código, lo que se ha estado haciendo es
incluir los ficheros en el proyecto de depuración, y para generar el código simplemente se
le daba al botón de Transform All Templates (o seleccionar el fichero, botón derecho y
Run Custom Tool). Para que esto funcionara, había que poner en la propiedad Custom
Tool el valor “TextTemplatingFileGenerator”. De esta manera ya se podía usar el botón
para generar el código.
Pero en la versión final no queríamos que esto fuese así. Quedaba mucho más
vistoso seleccionar la opción de generar el código mediante un cuadro de diálogo. Además,
existían diversas opciones que tenían que ser elegidas por el usuario, así que la opción del
cuadro de diálogo se convertía en casi obligatoria. De lo contrario, el usuario tendría que
haber abierto cada vez los ficheros de generación de código y haber cambiado las variables
a mano para poder elegir, por ejemplo, el motor de base de datos para generar el fichero
sql, o las dimensiones a ser normalizadas en el modelo SnowFlake. Además, la idea de que
los ficheros que contenían el código apareciesen en la versión final del proyecto tampoco
nos era de mucho agrado, y podía resultar bastante sobrecargado para el usuario.
Por lo tanto en este apartado tratará sobre todo lo que hemos tenido que hacer para
solucionar estos puntos. Se hablará del CommandSet.dslddt, un fichero que ha sido crucial
para poder crear el menú contextual. También de cómo hemos incluido los ficheros de
generación de código como atributos embebidos, para que no apareciesen en la versión
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
103
final del proyecto, y por último hablaremos de cada uno de los cuadros de diálogo y de los
pasos que hemos seguido para crearlos.
Cambios en el CommandSet
Lo primero para realizar todo lo que hemos expuesto en el apartado anterior es
conseguir un menú contextual para poder seleccionar las distintas opciones. La idea
consistía en darle con el botón derecho a una parte vacía del esquema, y que nos dejara
elegir entre las opciones de generar el código con cada uno de los tres modelos. Y para
poder hacer esto, hemos tenido que realizar unos cambios en el archivo
Shell/CommandSet.dslddt principalmente, incluido en el proyecto del Designer.
Pues bien, para poder añadir un comando en el menú hay que seguir los siguientes
pasos:
1. Traer una copia del archivo CommandSet.dslddt. Si hasta ahora no se ha
modificado es muy posible que solo exista una referencia al archivo
CommandSet.dslddi. Si es así, hay que buscarlo en el directorio
TextTemplates de la ruta de instalación de las DSL Tools, reemplazarlo por
su include correspondiente y ponerlo al final del fichero.
2. Añadir un identificador para cada comando. Para ello, al final del archivo
CommandSet.dslddt hay que añadir una nueva clase llamada
CustomCommandIdList con el siguiente código:
internal static class CustomCommandIdList
{
//Values must be equal than those in CustomCmd.ctc
public const uint cmdIdEstrella = 0x400;
public const uint cmdIdSnowFlake = 0x500;
public const uint cmdIdWarehouseBuilder = 0x600;
}
Después, en Designer\CtcComponents\CustomCmd.ctc hay que añadir un
#define por cada uno de ellos:
#define cmdidEstrella 0x400
#define cmdidSnowFlake 0x500
#define cmdidWarehouseBuilder 0x600
Y en este mismo fichero, debajo de “GENERATED_BUTTONS” poner:
guidCmdSet:cmdidEstrella, guidMenu:grpidContextMain, 0x0200, OI_NOID,
BUTTON, DIS_DEF, "Generar Modelo Estrella";
guidCmdSet:cmdidSnowFlake, guidMenu:grpidContextMain, 0x0200, OI_NOID,
BUTTON, DIS_DEF, "Generar Modelo SnowFlake";
guidCmdSet:cmdidWarehouseBuilder, guidMenu:grpidContextMain, 0x0200,
OI_NOID, BUTTON, DIS_DEF, "Generar Modelo WarehouseBuilder";
El texto que pondremos ahí entre comillas será el que aparezca en el menú.
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
104
3. Añadir los comandos al CommandSet. Aquí es la parte donde se incluirá el
código que deberá realizarse cada vez que seleccionemos la opción del
menú. Primero hay que añadir un par de líneas por cada nuevo comando en
el método GetMenuCommands():
// Add "Genera Modelo Estrella" menu command
menuCommand = new DynamicStatusMenuCommand (
new EventHandler(OnStatusEstrella),
new EventHandler(OnMenuEstrella),
new CommandID(GuidList.guidObjectOrientedMultidimensionalModelCmdSet,
(int)CustomCommandIdList.cmdIdEstrella));
commandList.Add(menuCommand);
// Add "Genera Modelo SnowFlake" menu command
menuCommand = new DynamicStatusMenuCommand(
new EventHandler(OnStatusSnowFlake),
new EventHandler(OnMenuSnowFlake),
new CommandID(GuidList.guidObjectOrientedMultidimensionalModelCmdSet,
(int)CustomCommandIdList.cmdIdSnowFlake));
commandList.Add(menuCommand);
// Add "Genera Modelo WarehouseBuilder" menu command
menuCommand = new DynamicStatusMenuCommand(
new EventHandler(OnStatusWarehouseBuilder),
new EventHandler(OnMenuWarehouseBuilder),
new CommandID(GuidList.guidObjectOrientedMultidimensionalModelCmdSet,
(int)CustomCommandIdList.cmdIdWarehouseBuilder));
commandList.Add(menuCommand);
Y luego añadir los métodos propios que definirán su comportamiento. Esto
se compone de dos métodos por cada comando, OnStatus y OnMenu:
internal void OnStatusEstrella(object sender, EventArgs e) {...}
internal void OnMenuEstrella(object sender, EventArgs e) {...}
internal void OnStatusSnowFlake(object sender, EventArgs e) {...}
internal void OnMenuSnowFlake(object sender, EventArgs e) {...}
internal void OnStatusWarehouseBuilder(object sender, EventArgs e) {...}
internal void OnMenuWarehouseBuilder(object sender, EventArgs e) {...}
El código del OnMenu se explicará en el apartado de Cuadros de diálogo, y
es donde le damos funcionalidad. El código del OnStatus será el mismo
para todos, y en ellos es donde se especifica que tan solo queremos que
aparezca el comando en el menú cuando no hay ningún elemento del
esquema seleccionado. Ponemos como ejemplo el OnStatusEstrella:
internal void OnStatusEstrella(object sender, EventArgs e)
{
MenuCommand cmd = sender as MenuCommand;
//Only visible when nothing is selected
cmd.Enabled = cmd.Visible = false;
IEnumerator selection=this.CurrentSelection.GetEnumerator();
selection.MoveNext();
if(selection.Current is ObjectOrientedMultidimensionalModelDiagram)
cmd.Enabled = cmd.Visible = true;
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
105
}
4. Incrementar índice del menú. Lo encontramos en
Designer\Shell\Package.dslddt.
[ProvideMenuResource(1000, 3)]
5. Compilar y depurar.
Los comandos deben aparecer al darle al botón derecho en una zona vacía de un
diagrama oomm.
Para poder trabajar mejor con el fichero CommandSet.dslddt se recomienda
modificar primero su fichero generado, el CommandSet.cs, ya que este hace uso de la
sintaxis coloreada y de la llamada Intellisense prompting, que te puede ir mostrando código
de ayuda conforme se va escribiendo. Pero hay que acordarse al acabar de modificar
siempre el CommandSet.dslddt, si no en cuanto se generen templates los cambios
realizados en el CommandSet.cs desaparecerán.
Ficheros embebidos como recursos
Los ficheros que contienen toda la generación de código que daba lugar a los
archivos finales .sql se habían mantenido desde un principio en el proyecto que debería
utilizar el usuario final. Como ya se ha dicho, no se quería que estos ficheros pudiesen
verse, porque toda esa generación de código debía de ser transparente al usuario y el que
estuviesen incluidos en el proyecto solo resultaban un estorbo para el diseñador. Por ello se
decidió incluir estos ficheros como recursos embebidos en el proyecto
ObjectOrientedMultidimensionalModel, y recurrir a ellos cuando fuese necesario.
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
106
Antes debemos hablar un poco del fichero de recursos CommandSet.Resource.resx.
Este archivo no existía en un principio, se añadió debido a la cantidad de recursos que nos
han sido necesarios a la hora de modificar el CommandSet y añadir los nuevos comandos
para el menú. Se han usado dos tipos de recursos:
Strings. Cadenas de caracteres que podemos dividir en:
o Ficheros de entrada y salida: fileInputEstrella, fileInputSnowFlake,
fileInputWarehouseBuilder, fileOutputEstrella,
fileOutputSnowFlake y fileOutputWarehouseBuilder.
o Cadenas identificativas para sustituir en los ficheros de generación
de código: conditionNormalizeDims, fileModel y motorBDModel.
Estas cadenas tiene el formato: $_CADENA_EN_FICHERO_$,
donde CADENA_EN_FICHERO es la cadena que aparece en el
fichero de generación de código que habrá que sustituir por un valor
determinado. Dicho valor dependerá de la elección del usuario.
o Motores de base de datos, para seleccionar el motor de base de datos
con el que se generará el código: motorOracle y motorSQLServer.
Ficheros. Aquí se encuentran embebidos los archivos de los que antes
hablábamos, utilizados para la generación de código. Son once:
o cabecera.comentarios,
o ClaseTClaveAjena.t4,
o ClaseTColumna.t4,
o ClaseTTabla.t4,
o ClaseTTablaMultidimensional.t4,
o ClaseValidacion.t4,
o DepuracionGeneracion.ReportTemplate,
o GeneradorSQLEstrella.ReportTemplate,
o GeneradorSnowFlake.ReportTemplate,
o GeneradorWarehouseBuilder.ReportTemplate y
o RecursosApoyoGeneracionCodigo.t4.
Aunque los principales son los Generador*.ReportTemplate, los demás son
librerías necesarias sin las que no podríamos generar los ficheros .sql. Por lo tanto se ha
programado de forma que cuando se genere código por primera vez, todos estos ficheros se
copien en el path del proyecto, y así puedan estar a mano para cualquier otro momento que
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
107
se necesiten. Esto se ha englobado en una método privado llamado crearFicsApoyo(), que
podemos encontrar en el CommandSet:
private void crearFicsApoyo()
{
//cabecera.comentarios
string filePath = getSolutionPath() +
"cabecera.comentarios";
if (!File.Exists(filePath))
{
string templateContent =
System.Text.Encoding.Default.GetString(
Shell.CommandSet_Resource.cabecera);
string interFicPath = getSolutionPath() + "tmp.txt";
StreamWriter stw = new StreamWriter(interFicPath);
stw.Write(templateContent);
stw.Close();
File.Move(interFicPath, filePath);
}
//ClaseTClaveAjena.t4
filePath = getSolutionPath() + "ClaseTClaveAjena.t4";
if (!File.Exists(filePath))
{
string templateContent =
System.Text.Encoding.Default.GetString(
Shell.CommandSet_Resource.ClaseTClaveAjena);
string interFicPath = getSolutionPath() + "tmp.txt";
StreamWriter stw = new StreamWriter(interFicPath);
stw.Write(templateContent);
stw.Close();
File.Move(interFicPath, filePath);
}
//ClaseTColumna.t4
filePath = getSolutionPath() + "ClaseTColumna.t4";
if (!File.Exists(filePath))
{
string templateContent =
System.Text.Encoding.Default.GetString(
Shell.CommandSet_Resource.ClaseTColumna);
string interFicPath = getSolutionPath() + "tmp.txt";
StreamWriter stw = new StreamWriter(interFicPath);
stw.Write(templateContent);
stw.Close();
File.Move(interFicPath, filePath);
}
//ClaseTTabla.t4
filePath = getSolutionPath() + "ClaseTTabla.t4";
if (!File.Exists(filePath))
{
string templateContent =
System.Text.Encoding.Default.GetString(
Shell.CommandSet_Resource.ClaseTTabla);
string interFicPath = getSolutionPath() + "tmp.txt";
StreamWriter stw = new StreamWriter(interFicPath);
stw.Write(templateContent);
stw.Close();
File.Move(interFicPath, filePath);
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
108
}
//ClaseTTablaMultidimensional.t4
filePath = getSolutionPath() +
"ClaseTTablaMultidimensional.t4";
if (!File.Exists(filePath))
{
string templateContent =
System.Text.Encoding.Default.GetString(
Shell.CommandSet_Resource.ClaseTTablaMultidimensional);
string interFicPath = getSolutionPath() + "tmp.txt";
StreamWriter stw = new StreamWriter(interFicPath);
stw.Write(templateContent);
stw.Close();
File.Move(interFicPath, filePath);
}
//ClaseValidacion.t4
filePath = getSolutionPath() + "ClaseValidacion.t4";
if (!File.Exists(filePath))
{
string templateContent =
System.Text.Encoding.Default.GetString(
Shell.CommandSet_Resource.ClaseValidacion);
string interFicPath = getSolutionPath() + "tmp.txt";
StreamWriter stw = new StreamWriter(interFicPath);
stw.Write(templateContent);
stw.Close();
File.Move(interFicPath, filePath);
}
//DepuracionGeneracion.ReportTemplate
filePath = getSolutionPath() +
"DepuracionGeneracion.ReportTemplate";
if (!File.Exists(filePath))
{
string templateContent =
System.Text.Encoding.Default.GetString(
Shell.CommandSet_Resource.DepuracionGeneracion);
string interFicPath = getSolutionPath() + "tmp.txt";
StreamWriter stw = new StreamWriter(interFicPath);
stw.Write(templateContent);
stw.Close();
File.Move(interFicPath, filePath);
}
//GeneradorWarehouseBuilder.ReportTemplate
filePath = getSolutionPath() +
"GeneradorWarehouseBuilder.ReportTemplate";
if (!File.Exists(filePath))
{
string templateContent =
System.Text.Encoding.Default.GetString(
Shell.CommandSet_Resource.GeneradorWarehouseBuilder);
string interFicPath = getSolutionPath() + "tmp.txt";
StreamWriter stw = new StreamWriter(interFicPath);
stw.Write(templateContent);
stw.Close();
File.Move(interFicPath, filePath);
}
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
109
//GeneradorSQLEstrella.ReportTemplate
filePath = getSolutionPath() +
"GeneradorSQLEstrella.ReportTemplate";
if (!File.Exists(filePath))
{
string templateContent =
System.Text.Encoding.Default.GetString(
Shell.CommandSet_Resource.GeneradorSQLEstrella);
string interFicPath = getSolutionPath() + "tmp.txt";
StreamWriter stw = new StreamWriter(interFicPath);
stw.Write(templateContent);
stw.Close();
File.Move(interFicPath, filePath);
}
//GeneradorSQLSnowflake.ReportTemplate
filePath = getSolutionPath() +
"GeneradorSQLSnowflake.ReportTemplate";
if (!File.Exists(filePath))
{
string templateContent =
System.Text.Encoding.Default.GetString(
Shell.CommandSet_Resource.GeneradorSQLSnowflake);
string interFicPath = getSolutionPath() + "tmp.txt";
StreamWriter stw = new StreamWriter(interFicPath);
stw.Write(templateContent);
stw.Close();
File.Move(interFicPath, filePath);
}
//RecursosApoyoGeneracionCodigo.t4
filePath = getSolutionPath() +
"RecursosApoyoGeneracionCodigo.t4";
if (!File.Exists(filePath))
{
string templateContent =
System.Text.Encoding.Default.GetString(
Shell.CommandSet_Resource.RecursosApoyoGeneracionCodigo);
string interFicPath = getSolutionPath() + "tmp.txt";
StreamWriter stw = new StreamWriter(interFicPath);
stw.Write(templateContent);
stw.Close();
File.Move(interFicPath, filePath);
}
}
Como se ve, hay un trozo de código para cada uno de los ficheros. Todos ellos
consisten en lo mismo. Primero cogemos el path donde debería estar el fichero. Si está ahí
no haremos nada, pero si el fichero no existe entonces lo crearemos. Para ellos cogemos el
contenido del fichero desde el archivo de recursos, y lo volcamos en un fichero de texto
intermedio. Esto se hace así porque si no se copia en un fichero de texto los caracteres no
aparecerán con el formato adecuado. Al final se le cambia el nombre del fichero de texto al
que debe realmente tener.
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
110
De esta manera también conseguimos que si en algún momento se borra alguno de
los ficheros por la razón que sea, podamos recuperarlo de forma transparente. Este método
se le llamará cada vez que se ejecute un comando, al principio del método OnMenu.
Por último comentar que existía otro fichero llamado PalabrasReservadas.txt que
en un principio se embebió al igual que los otros. Sin embargo este fichero, a diferencia de
los demás, se abre y se lee como un StreamReader dentro del código generado, y la ruta en
la que se busca no es la misma que donde se crea el proyecto. Por eso se decidió copiarlo
en una ruta fija junto con la instalación del proyecto, en el directorio
C:\WINDOWS\OOMM Files. Esto se lleva a cabo en el proyecto del Setup, y todos los
nuevos proyectos OOMM leerán dicho archivo desde esa ruta.
Cuadros de diálogo
Aquí explicaremos como se han implementado las funciones de los comandos, más
concretamente el código que se encuentra en los métodos OnMenu del CommandSet.
Antes de pasar a explicar cada uno, debemos comentar algunas funciones auxiliares que
utilizan:
condicionDimsNormalizadas(). Crea una cadena de texto con las
dimensiones que van a ser normalizadas en forma de condición, para que así
se puedan sustituir más adelante en el fichero
GeneradorSQLSnowFlake.ReportTemplate.
private string condicionDimsNormalizadas(IList dimensiones, bool[]
esDimNormalizada)
{
string sentenciaIF = "";
bool ningunaNormalizada = true;
for (int i = 0; i < esDimNormalizada.Length; i++)
if (esDimNormalizada[i])
{
ningunaNormalizada = false;
sentenciaIF += "dim.Name==\"" +
((Dimension)dimensiones[i]).AccessibleName + "\" || ";
}
if (ningunaNormalizada)
sentenciaIF = "false";
else
sentenciaIF = sentenciaIF.Substring(0,
sentenciaIF.Length - 4);
return sentenciaIF;
}
Se le pasan dos parámetros. El primero es la lista de las dimensiones, y se
utiliza para obtener su nombre. El segundo es un vector de booleanos que
indica las dimensiones que deben estar normalizadas y las que no. Un true
en la posición de una dimensión indica que dicha dimensión debe estar
normalizada.
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
111
Recorreremos el vector de booleanos, y cada vez que se encuentre un valor
true se concatenará en el string sentenciaIF la cadena
“dim.Name=="nombreDeLaDimension" ||”, donde nombreDeLaDimension
se obtiene de la lista de dimensiones. Si no se encuentra ninguna dimensión
a normalizar devolverá la cadena “false”. En caso contrario se le quitarán
los cuatro últimos caracteres, que corresponden al símbolo OR (||) más los
espacios.
Así se devolverá la variable sentenciaIF que contendrá una expresión del
tipo: dim.Name == "dimension_a_normalizar_1" || dim.Name ==
"dimension_a_normalizar_2" ..., o bien la cadena “false”.
getSolutionPath(). Devuelve el path donde se encuentra nuestro proyecto
actual.
private string getSolutionPath()
{
EnvDTE80.DTE2 dte2 =
this.ServiceProvider.GetService(typeof(EnvDTE.DTE)) as EnvDTE80.DTE2;
EnvDTE.Project prj = dte2.Solution.Projects.Item(1);
//El path sera aquel en donde este ubicado el fichero del
proyecto
string path = Path.GetDirectoryName(prj.FileName) + "\\";
return path;
}
addToProject(). Añade el fichero que se le pasa por parámetro al proyecto
actual. Esto se producirá cuando generemos código por primera vez en
alguno de los tres modelos, para que nos aparezca el fichero con el código
generado incluido en el proyecto de nuestra solución.
private void addToProject(string path)
{
EnvDTE80.DTE2 dte2 =
this.ServiceProvider.GetService(typeof(EnvDTE.DTE)) as EnvDTE80.DTE2;
dte2.ItemOperations.AddExistingItem(path);
}
Y ahora pasemos a explicar cada uno de los modelos:
Modelo Estrella
El código que genera los cuadros de diálogo del modelo estrella es el siguiente:
internal void OnMenuEstrella(object sender, EventArgs e)
{
string inFic = this.getSolutionPath() +
Shell.CommandSet_Resource.fileInputEstrella;
string outFic = this.getSolutionPath() +
Shell.CommandSet_Resource.fileOutputEstrella;
bool existiaOutput = File.Exists(outFic);
//Creacion de los ficheros de apoyo
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
112
crearFicsApoyo();
//Preguntamos por el motor de base de datos
MotorBDForm bdForm = new MotorBDForm();
DialogResult dr = bdForm.ShowDialog();
if (dr == DialogResult.OK)
{
//Leemos el codigo del fichero, y reemplazamos el nombre
StreamReader str = new StreamReader(inFic);
string templateContent = str.ReadToEnd();
templateContent = templateContent.Replace(
Shell.CommandSet_Resource.fileModel, this.CurrentData.FileName);
//Reemplazamos el motor de base de datos
if(bdForm.getMotorBD()==1)
templateContent =
templateContent.Replace(Shell.CommandSet_Resource.motorBDModel,
Shell.CommandSet_Resource.motorSQLServer);
else if(bdForm.getMotorBD()==2)
templateContent = templateContent.Replace(
Shell.CommandSet_Resource.motorBDModel,
Shell.CommandSet_Resource.motorOracle);
//Generacion de codigo
ITextTemplating templateGen =
(ITextTemplating)ObjectOrientedMultidimensionalModelPackage.GetGlobalService(
typeof(STextTemplating));
string result = templateGen.ProcessTemplate(inFic,
templateContent, null);
//Escribimos en el fichero de salida
StreamWriter stw = new StreamWriter(outFic);
stw.Write(result);
str.Close();
stw.Close();
//Añadimos el fichero al proyecto
if (!existiaOutput) addToProject(outFic);
System.Windows.Forms.MessageBox.Show("El codigo generado se
ha guardado en el fichero \"" + Shell.CommandSet_Resource.fileOutputEstrella
+ "\"", "Sql Estrella Generado");
}
}
Lo primero siempre en cada modelo es guardar los paths de entrada (fichero
GeneradorSQLEstrella.ReportTemplate, en este caso) y de salida (GeneradorEstrella.sql),
en las variables inFic y outFic, respectivamente. También se guarda en la variable
existiaOutput si el fichero de salida ya existía, y hacemos la llamada a la creación de los
ficheros de apoyo (que los creará en el caso que sea necesario, como se explicó en el
apartado Ficheros embebidos como recursos).
A continuación sacamos un cuadro de diálogo que nos pregunte por el motor de
base de datos. Se ha creado una nueva clase llamada MotorBDForm para esto, y la
explicaremos más adelante. Ahora, por el momento nos basta saber que el método
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
113
ShowDialog aplicado a dicha clase nos devuelve una variable de tipo DialogResult que nos
dice si se le ha dado al botón de aceptar. En caso de ser así continuamos, y realizamos los
siguientes pasos:
1. Leer el código del fichero de entrada (el template que generará el código
final), y guardarlo en la variable templateContent.
2. Reemplazar el nombre del fichero (el de extensión oomm) en el que
estamos actualmente. Para ello usamos la cadena contenida en fileModel
que encontraremos en el fichero de entrada.
3. Reemplazar el motor de base de datos que se usará. Esta información hay
que sacarla del cuadro de diálogo que preguntaba por el motor de base de
datos. Concretamente se obtiene llamando al método getMotorBD() de la
clase MotorBDForm. El valor 1 corresponde a SQL Server, y el valor 2 a
Oracle.
4. Generar código, y guardar el resultado en la variable result.
5. Escribir el resultado en el fichero de salida.
6. Añadir el fichero al proyecto si no existía de antes.
7. Mostrar mensaje informativo diciendo que se ha generado el código.
La clase MotorBDForm se encuentra en el proyecto del Designer, en su directorio
raíz. Contiene el código necesario para crear una ventana que te pregunta sobre el motor de
base de datos con el que quieres generar el código sql.
En el archivo MotorBDForm.cs se han añadido un par de métodos útiles para
comunicarse con el CommandSet:
public partial class MotorBDForm : Form
{
int motorBD;
public MotorBDForm()
{
InitializeComponent();
}
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
114
private void aceptar_Click(object sender, EventArgs e)
{
this.DialogResult = DialogResult.OK;
if (sqlServerRB.Checked == true)
motorBD = 1;
else
motorBD = 2;
this.Close();
}
public int getMotorBD()
{ return this.motorBD; }
}
Se ha creado una variable para la clase llamada motorBD. El método
aceptar_Click() le dará el valor de 1 si se elige SQL Server como motor de base de datos o
2 si es Oracle el seleccionado cuando se le da la botón de Aceptar. El método
getMotorBD() lo que hará será devolver el valor de dicha variable.
Esta clase se usa también en el modelo SnowFlake para seleccionar entre SQL
Server u Oracle como motor de base de datos.
Modelo SnowFlake
El código que conlleva seleccionar esta opción del menú es:
internal void OnMenuSnowFlake(object sender, EventArgs e)
{
string inFic = this.getSolutionPath() +
Shell.CommandSet_Resource.fileInputSnowFlake;
string outFic = this.getSolutionPath() +
Shell.CommandSet_Resource.fileOutputSnowFlake;
bool existiaOutput = File.Exists(outFic);
//Creacion de los ficheros de apoyo
crearFicsApoyo();
//Preguntamos por el motor de base de datos
MotorBDForm bdForm = new MotorBDForm();
DialogResult drBD = bdForm.ShowDialog();
if (drBD == DialogResult.OK)
{
//Preguntamos por las dimensiones a normalizar
IList dimensions =
this.CurrentData.Store.ElementDirectory.GetElements(Dimension.MetaClassGuid);
bool[] dimsParaNormalizar = null;
dimsParaNormalizar = new bool[dimensions.Count];
SnowFlakeForm sfWin = new SnowFlakeForm(dimensions, ref
dimsParaNormalizar);
DialogResult drDim = sfWin.ShowDialog();
if (drDim == DialogResult.OK)
{
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
115
//Creacion de la condicion IF que muestre las dimensiones
que estan normallizadas
string sentenciaIF =
this.condicionDimsNormalizadas(dimensions, dimsParaNormalizar);
//Leemos el codigo del fichero
StreamReader str = new StreamReader(inFic);
string templateContent = str.ReadToEnd();
//Reemplazamos el nombre, el motor de BD y la condicion
de normalizar/desnormalizar dimensiones
templateContent =
templateContent.Replace(Shell.CommandSet_Resource.fileModel,
this.CurrentData.FileName);
if (bdForm.getMotorBD() == 1)
templateContent =
templateContent.Replace(Shell.CommandSet_Resource.motorBDModel,
Shell.CommandSet_Resource.motorSQLServer);
else if (bdForm.getMotorBD() == 2)
templateContent =
templateContent.Replace(Shell.CommandSet_Resource.motorBDModel,
Shell.CommandSet_Resource.motorOracle);
templateContent =
templateContent.Replace(Shell.CommandSet_Resource.conditionNormalizeDims,
sentenciaIF);
//Generacion de codigo
ITextTemplating templateGen =
(ITextTemplating)ObjectOrientedMultidimensionalModelPackage.GetGlobalService(
typeof(STextTemplating));
string result = templateGen.ProcessTemplate(inFic,
templateContent, null);
//Escribimos en el fichero de salida
StreamWriter stw = new StreamWriter(outFic);
stw.Write(result);
str.Close();
stw.Close();
//Añadimos el fichero al proyecto (si lo hemos creado
nuevo)
if (!existiaOutput) addToProject(outFic);
System.Windows.Forms.MessageBox.Show("El codigo generado
se ha guardado en el fichero \"" +
Shell.CommandSet_Resource.fileOutputSnowFlake + "\"", "Sql SnowFlake
Generado");
}
}
}
Empezamos de la misma manera que en el modelo Estrella. Después de comprobar
que se le ha dado a Aceptar en el diálogo del MotorBDForm, debemos mostrar otro
diálogo con respecto a las dimensiones que queremos normalizar. Aquí también se ha
hecho una nueva clase para este cuadro de diálogo llamada SnowFlakeForm. Tenemos que
pasarle dos parámetros a dicha clase para poder crear un objeto: una lista con las
dimensiones del modelo actual, y un vector de booleanos del mismo tamaño que el número
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
116
de dimensiones, y que contendrá “true” o “false” dependiendo si la dimensión con el
mismo índice está o no normalizada.
Una vez creadas dichas variables invocamos el cuadro de diálogo con ShowDialog,
y si el usuario le da a Aceptar continuaremos. Los pasos a seguir en este caso son muy
parecidos a los del modelo Estrella, pero con un par de añadidos:
1. Creación de la condición if que muestre las dimensiones que están
normalizadas. Para ello usamos el método auxiliar
condicionDimsNormalizadas(), explicado anteriormente en el apartado
Cuadros de diálogo.
2. Leer el código del fichero de entrada y guardarlo en la variable
templateContent.
3. Reemplazar el nombre del fichero oomm en el que estamos actualmente.
4. Reemplazar el motor de base de datos que se usará.
5. Reemplazar la condición para las dimensiones que hay que normalizar
creada en el punto 1.
6. Generar código, y guardar el resultado en la variable result.
7. Escribir el resultado en el fichero de salida.
8. Añadir el fichero al proyecto si no existía de antes.
9. Mostrar mensaje informativo diciendo que se ha generado el código.
La única diferencia es la selección de las dimensiones que hay que normalizar. Para
hacerlo nos valemos de la clase SnowFlakeForm, creada en la raíz del proyecto Designer.
Nos muestra una lista con las dimensiones del fichero actual, y un checkBox a la izquierda
de cada una de ellas para seleccionar aquellas que se quieren normalizar.
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
117
Si intentamos abrir el fichero SnowFlakeForm.cs con el diseñador del Visual
Studio de ventanas de Windows, veremos que nos aparece un cuadro de diálogo vacío.
Esto es porque se ha tenido que modificar el código generado por el Visual para poder
crear un cuadro de diálogo en tiempo de ejecución, ya que será distinto dependiendo del
número de dimensiones que tengamos en un determinado momento.
Si vemos el código nos encontraremos con lo siguiente:
public partial class SnowFlakeForm : Form
{
bool[] dimsParaNormalizar;
public SnowFlakeForm(IList listaDims, ref bool[] vector)
{
dimsParaNormalizar = vector;
InitializeComponent(this.getListaStrings(listaDims));
}
private void aceptar_Click(object sender, EventArgs e)
{
this.DialogResult = DialogResult.OK;
for (int i = 0; i < this.checkBoxes.Length; i++)
dimsParaNormalizar[i] = this.checkBoxes[i].Checked;
this.Close();
}
/// <summary>
/// A partir de una lista con las dimensiones del modelo,
devuelve otra con los
/// strings de los nombres de las dimensiones para manejarlos
mas facilmente
/// </summary>
/// <param name="listaDims"> Lista de dimensiones del modelo
</param>
/// <returns> Lista con los nombres de las dimensiones
</returns>
private IList getListaStrings(IList listaDims)
{
IList listaStrings = new ArrayList();
foreach (Dimension dim in listaDims)
listaStrings.Add(dim.GetComponentName());
return listaStrings;
}
}
Tenemos un vector de booleanos global llamado dimsParaNormalizar que será el
que contenga justo eso, un “true” en aquellas posiciones correspondientes a las
dimensiones que van a ser normalizadas. Dicho vector se asigna por referencia al vector
que se le pasa al crear el objeto. El objetivo de esta clase es rellenar dicho vector.
Se ha cambiado la definición del InitializeComponent, de manera que se le pase un
parámetro que se obtiene del método getListaStrings. Este método recibe una IList con las
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
118
dimensiones del modelo, y devuelve otra IList con los strings de los nombres de dichas
dimensiones. Como esos strings son lo único que necesitamos, se ha creado este método
para extraerlos y poder trabajar con ellos más fácilmente.
Para ver como conseguir obtener las dimensiones que selecciona el usuario para
normalizar hay que abrir el fichero SnowFlakeForm.Designer.cs. Se ha modificado el
método InitializeComponent.
private void InitializeComponent(IList dimensiones)
{
this.label1 = new System.Windows.Forms.Label();
this.aceptar = new System.Windows.Forms.Button();
this.checkBoxes = new
System.Windows.Forms.CheckBox[dimensiones.Count];
for (int i = 0; i < dimensiones.Count; i++)
this.checkBoxes[i] = new
System.Windows.Forms.CheckBox();
this.SuspendLayout();
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(49, 40);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(218, 13);
this.label1.TabIndex = 0;
this.label1.Text = "Elige las dimensiones que quieres
normalizar:";
//
// aceptar
//
this.aceptar.Location = new System.Drawing.Point(355, 90 +
dimensiones.Count * 20);
this.aceptar.Name = "aceptar";
this.aceptar.Size = new System.Drawing.Size(75, 23);
this.aceptar.TabIndex = 1;
this.aceptar.Text = "Aceptar";
this.aceptar.UseVisualStyleBackColor = true;
this.aceptar.Click += new
System.EventHandler(this.aceptar_Click);
//
// checkBoxes
//
for (int i = 0; i < dimensiones.Count; i++)
{
this.checkBoxes[i].AutoSize = true;
this.checkBoxes[i].Location = new
System.Drawing.Point(52, 81 + i * 20);
this.checkBoxes[i].Name = (string)dimensiones[i];//
"checkBox1";
this.checkBoxes[i].Size = new System.Drawing.Size(80,
17);
this.checkBoxes[i].TabIndex = 1;
this.checkBoxes[i].Text = (string)dimensiones[i];//
"checkBox1";
this.checkBoxes[i].UseVisualStyleBackColor = true;
}
//
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
119
// SnowFlakeForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F,
13F);
this.AutoScaleMode =
System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(440, 121 +
dimensiones.Count * 20);
this.Controls.Add(this.aceptar);
this.Controls.Add(this.label1);
for (int i = 0; i < dimensiones.Count; i++)
this.Controls.Add(this.checkBoxes[i]);
this.FormBorderStyle =
System.Windows.Forms.FormBorderStyle.FixedDialog;
this.Name = "SnowFlakeForm";
this.MaximizeBox = false;
this.MinimizeBox = false;
this.ResumeLayout(false);
this.StartPosition =
System.Windows.Forms.FormStartPosition.CenterScreen;
this.PerformLayout();
this.Text = "Generar SnowFlake";
}
Ahora se le pasa una IList con los strings de las todas las dimensiones del modelo.
Necesitamos estos strings para mostrarle las dimensiones al usuario y que pueda
seleccionar las adecuadas. Lo único que se ha modificado con respecto al código generado
es que ahora tenemos un vector de CheckBoxes, ya que no sabemos con seguridad cuántos
van a haber. Eso nos lo indicará la IList dimensiones, por lo tanto se le dará valor a cada
uno de los elementos dentro de un bucle for y su situación en la ventana deberá ser relativa
a la de los demás elementos.
El Add de los CheckBoxes también será un bucle for, y el tamaño de la ventana
dependerá del número de dimensiones. Por lo tanto al ser todos estos valores relativos, no
podremos tener una vista preliminar de cómo quedaría en la pantalla del diseñador.
Por último, volviendo al fichero SnowFlakeForm.cs, cuando le demos al botón
Aceptar entraremos en el método aceptar_Click(). Aquí es donde a partir del vector de
CheckBoxes daremos valores a los elementos del vector dimsParaNormalizar, mirando en
la propiedad Checked de cada uno de sus elementos que nos indicará si está seleccionado o
no y, por tanto, si queremos que se normalice o no.
Modelo Oracle WarehouseBuilder
Este es el más sencillo de los tres ficheros, porque no hay que elegir motor de base
de datos (siempre se generará bajo Oracle), ni tampoco hay que elegir dimensiones para
normalizar. El código de su método OnMenu es:
internal void OnMenuWarehouseBuilder(object sender, EventArgs e)
{
string inFic = this.getSolutionPath() +
Shell.CommandSet_Resource.fileInputWarehouseBuilder;
string outFic = this.getSolutionPath() +
Shell.CommandSet_Resource.fileOutputWarehouseBuilder;
bool existiaOutput = File.Exists(outFic);
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
120
//Creacion de los ficheros de apoyo
crearFicsApoyo();
//Leemos el codigo del fichero, y reemplazamos el nombre
StreamReader str = new StreamReader(inFic);
string templateContent = str.ReadToEnd();
templateContent =
templateContent.Replace(Shell.CommandSet_Resource.fileModel,
this.CurrentData.FileName);
//Generacion de codigo
ITextTemplating templateGen =
(ITextTemplating)ObjectOrientedMultidimensionalModelPackage.GetGlobalService(
typeof(STextTemplating));
string result = templateGen.ProcessTemplate(inFic,
templateContent, null);
//Escribimos en el fichero de salida
StreamWriter stw = new StreamWriter(outFic);
stw.Write(result);
str.Close();
stw.Close();
//Añadimos el fichero al proyecto (si lo hemos creado nuevo)
if (!existiaOutput) addToProject(outFic);
System.Windows.Forms.MessageBox.Show("El codigo generado se ha
guardado en el fichero \"" +
Shell.CommandSet_Resource.fileOutputWarehouseBuilder + "\"", "Sql Oracle
WarehouseBuilder Generado");
}
Y los pasos para su creación:
1. Obtener los paths de entrada y salida, y crear los ficheros de apoyo de ser
necesario.
2. Leer el código del fichero de entrada y guardarlo en templateContent.
3. Reemplazar el nombre del fichero del modelo en el que estamos
actualmente.
4. Generar código, y guardar el resultado en la variable result.
5. Escribir el resultado en el fichero de salida.
6. Añadir el fichero al proyecto si no existía de antes.
7. Mostrar mensaje informativo diciendo que se ha generado el código.
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
121
III. Generación de código
Modelo de generación de código
Introducción
El modelo de generación de código ha sido diseñado pensando especialmente en el
desarrollo rápido de scripts de generación de código, el mantenimiento y la ampliación de
motores de Bases de Datos soportados.
Se ha hecho hincapié sobre todo en disponer de una API de programación sencilla
de utilizar y fácil de ampliar por futuros diseñadores de DSL.
Aquí podemos ver el diagrama de las clases implicadas en el proceso de generación
de código a partir de los diagramas .oomm que el usuario ha diseñado utilizando una
solución ObjectOrientedMuldimensionalModel desde Visual Studio 2005 (ver manual de
usuario).
(*) Diagrama de clases de las clases implicadas en la generación de Código.
Podemos ver que tenemos solo 6 clases relacionadas con la generación de código y
una Enumeración llamada MotorBBDD.
Tal y como hemos comentado anteriormente, la API de generación de código la
hemos diseñado pensando en la facilidad de cara a la generación de scripts SQL y es por
ello que el programador de nuevos scripts de SQL va a tener que utilizar únicamente la
clase TTabla o TTablaMultimensional, por lo que el resto de clases no van a tener que
aprender a utilizarlas.
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
122
La enumeración MotorBBDD se ha pensado para que se vayan añadiendo nuevos
motores de Bases de Datos ( informix, DB2,cache,…) de forma fácil puesto que las clases
son independientes del lenguaje de generación de código para el que el script vaya a
generarse, teniendo que ampliar unos pocos métodos de TTabla o TTablaMultimensional
para soportarlos, quedando el grueso de la generación de código 100% válido e intacto.
El ejemplo palpable de todas las bondades de esta API es que en nuestro proyecto
damos la opción de generar código para los dos motores de Bases de Datos mas potentes
de hoy en día, ORACLE y SQL Server y que los mismos scripts de generación de código
SQL son exactamente idénticos para ambas bases de datos. Esto es posible en gran medida
a que tanto para la generación de SQL Estrella como para la generación de SQL
Snowflake, se ha utilizado el ANSI SQL-99, quedando adaptadas algunas particularidades
de cada motor de base de datos como simples mejoras ( como el soportar nombres largos,
con espacios,… como nombres de objetos de la BBDD a generar ).
Para la programación de la API hemos optado por el lenguaje de programación C#
2.0 por dos motivos: El primero es por tratarse del lenguaje de programación que se ha
seguido desde un principio para la construcción del diseñador gráfico y el segundo porque
nos proporciona una legibilidad similar a la de C++, Java,… que nos da la seguridad de
que el abanico de potenciales programadores capaces de hacer frente a una extensión de la
API sea mayor ( esto no seria así de haber elegido la opción de Visual Basic 2005 ).
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
123
Descripción del proceso de procesamiento de los diagramas OOMM
En este punto vamos a explicar brevemente el proceso de procesamiento de los
diagramas OOMM. No se pretende aun hacer un análisis detallado de los scripts de
generación de código, sino explicar los razonamientos que nos han llevado a la
construcción de la API así como de los scripts de procesamiento que se verán mas
adelante.
Los diagramas diseñados desde el editor visual de Visual Studio 2005 pueden ser
desglosados en varias partes de procesamiento diferenciadas, que pueden ser fácilmente
distinguibles si atendemos al siguiente diagrama de ejemplo:
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
124
*Diagrama de ejemplo que contiene el 100% de funcionalidad.
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
125
En color verde, podemos distinguir un bloque formado por una clase Dimension,
una clase Fact y una clase Degenerate Fact.
Este tipo de estructuras van a generar siempre un código fijo consistente en lo
siguiente:
Una tabla que producirá la clase Fact ( en este caso Auto-Sales )
Una tabla que producirá la clase Dimension ( según sea SQL Estrella
o SQL Snowflake tendrá una u otra estructura).
Una tabla con el mismo nombre que el Degenerate Fact ( en este
caso se llamara [SP Commission] ) que tendrá como clave primaria una serie de
columnas que a su vez tendrán que ser clave ajena apuntando a la clave primaria de
la tabla que genera la clase Fact y a la clave primaria de la tabla que genera la clase
Dimension.
El código SQL Estrella del bloque verde del diagrama anterior para SQL Server
seria el siguiente:
CREATE TABLE Salesperson
(
Salesperson int NOT NULL IDENTITY PRIMARY KEY
,[SP personal data_Fullname] varchar(255),
[SP personal data_Name] varchar(255),
[SP personal data_Surname] varchar(255),
[SP personal data_Borndate] varchar(255),
Group_Name varchar(255),
Group_Description varchar(600),
Position_Name char(255),
Position_Description varchar(255)
)
GO
CREATE TABLE [Auto-Sales]
(
ContractN int NOT NULL,
Auto int NOT NULL,
Customer int NOT NULL,Commission varchar(255),
Quality decimal(5,3),
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
126
Price varchar(255),
Total varchar(255)
)
GO
---
---Creacion de la clave primaria compuesta de la tabla [Auto-Sales]
---
ALTER TABLE [Auto-Sales]
ADD CONSTRAINT [pk_Auto-Sales] PRIMARY KEY(
ContractN,Auto,Customer )
GO
---
---Creacion de las claves ajenas a las tablas principales de la
tabla Auto-Sales
---
ALTER TABLE [Auto-Sales]
ADD CONSTRAINT [FK_Auto-Sales_to_Auto]
FOREIGN KEY ( Auto )
REFERENCES Auto ( Auto )
GO
ALTER TABLE [Auto-Sales]
ADD CONSTRAINT [FK_Auto-Sales_to_Customer]
FOREIGN KEY ( Customer )
REFERENCES Customer ( Customer )
GO
--
-- Ahora van las asociaciones de DegenerateFact.
--
CREATE TABLE [SP commission]
(
ContractN int NOT NULL,
Auto int NOT NULL,
Customer int NOT NULL,
Salesperson int NOT NULL,Commission char(255)
)
GO
---
---Creacion de la clave primaria compuesta de la tabla [SP
commission]
---
ALTER TABLE [SP commission]
ADD CONSTRAINT [pk_SP commission] PRIMARY KEY(
ContractN,Auto,Customer,Salesperson )
GO
---
---Creacion de las claves ajenas a las tablas principales de la
tabla SP commission
---
ALTER TABLE [SP commission]
ADD CONSTRAINT [FK_SP commission_to_Auto-Sales]
FOREIGN KEY ( ContractN,Auto,Customer )
REFERENCES [Auto-Sales] ( ContractN,Auto,Customer )
GO
ALTER TABLE [SP commission]
ADD CONSTRAINT [FK_SP commission_to_Salesperson]
FOREIGN KEY ( Salesperson )
REFERENCES Salesperson ( Salesperson )
GO * Se han omitido las tablas Customer y Auto por no ser necesarias para la explicación
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
127
En color azul, podemos distinguir un bloque formado por una clase Fact y una
clase Dimension que se encuentra libre, sin estar unida a ninguna clase Base.
En este caso, también tendremos una estructura fija para describir este bloque del
diagrama. Tendremos dos tablas, la primera la formara la clase Fact y la segunda la tabla
Dimension. La relación entre ambas quedara definida por la clave ajena que habrá entre
ambas y que irá desde la tabla Fact a la tabla Dimension.
El código SQL Estrella del bloque azul del diagrama anterior para SQL Server seria
el siguiente:
--
-- Tabla Dimension llamada Auto
--
CREATE TABLE Auto
(
Auto int NOT NULL IDENTITY PRIMARY KEY
)
GO
--
-- Tabla Fact llamada Auto-Sales
--
CREATE TABLE [Auto-Sales]
(
ContractN int NOT NULL,
Auto int NOT NULL,
Customer int NOT NULL,Commission varchar(255),
Quality decimal(5,3),
Price varchar(255),
Total varchar(255)
)
GO
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
128
---
---Creacion de la clave primaria compuesta de la tabla [Auto-Sales]
---
ALTER TABLE [Auto-Sales]
ADD CONSTRAINT [pk_Auto-Sales] PRIMARY KEY(
ContractN,Auto,Customer )
GO
---
---Creacion de las claves ajenas a las tablas principales de la
tabla Auto-Sales
---
ALTER TABLE [Auto-Sales]
ADD CONSTRAINT [FK_Auto-Sales_to_Auto]
FOREIGN KEY ( Auto )
REFERENCES Auto ( Auto )
GO
ALTER TABLE [Auto-Sales]
ADD CONSTRAINT [FK_Auto-Sales_to_Customer]
FOREIGN KEY ( Customer )
REFERENCES Customer ( Customer )
GO
* Se ha omitido la tabla Customer
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
129
En color rosa podemos distinguir un Grafo Acíclico Dirigido (GAD) formado por 4
clases Base, unido a una clase Dimension que esta unida a su vez a la clase Fact.
Este tipo de estructuras mas complejas ya no son fijas, puesto que ahora ya depende
tanto del código SQL que queramos obtener (Snowflake, Estrella,…), como de la
organización del GAD que forman entre si las clases Base. Es por ello que no nos vamos a
extender en este apartado hablando del código que generan este tipo de estructuras porque
para eso están los apartados específicos de generación de scripts SQL Estrella ,Snowflake
y ORACLE Warehouse Builder.
Lo que si que podemos explicar es que este tipo de bloques se procesan de la
siguiente forma:
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
130
1. Partimos de la clase Fact (Auto-Sales) y rellenamos la estructura
TTabla que lo compone. No escribimos directamente el código que genera esta
clase Fact porque será de las últimas sentencias SQL que tendrá el script ya que
hay varias tablas que deben estar con anterioridad creadas en la BBDD antes de
poder generar esta estructura de tipo Fact (ya que tiene claves ajenas a las tablas
Dimension (customer) ).
2. Una vez tenemos la estructura TTabla de la clase Fact, nos vamos a
la Dimension (en este caso Customer ) y generamos su estructura TTabla.
En este caso la estructura de una TTabla Dimension puede ser muy
compleja puesto que según la organización del GAD de las clases Base, podemos
tener múltiples tablas con múltiples relaciones entre las mismas. Además la
organización de las mismas diferirá si se trata de un script Snowflake y se ha
decidido normalizar la dimensión, a si se trata de un script Estrella en el que las
dimensiones están todas desnormalizadas.
Una vez tengamos la TTabla rellena, generamos las sentencias pertinentes y
las escribimos al fichero .sql
3. Por último, una vez tenemos generado ya el código de generación de
tablas para las tablas Dimension, ya podremos generar las sentencias pertinentes de
la tabla Fact que saldrán de la estructura TTabla que hemos rellenado en el paso 1.
No dará error porque las tablas a las que apuntan sus claves ajenas ya estarán
creadas con anterioridad en el paso 2.
En relación al paso 2, da igual que estemos ante un caso snowflake, estrella o oracle
warehouse builder, hemos de anticipar ( se verá con mas detalle mas adelante ), que las
estructuras GAD se van recorriendo mediante un algoritmo de recorrido en profundidad (
DFS ) de Grafos Aciclicos Dirigidos y que para dar la sensación de que los scripts son
generados “al vuelo” se utilizan estructuras tipo Queue de .NET ( colas FIFO ) que
después de ser procesadas se escribirán a fichero dando la apariencia de haber sido
generadas en tiempo de ejecución y sin preprocesado alguno.
De este modo, el GAD que forman las clases Base se convertirá en una cola FIFO
de tipos TTabla perfectamente definidos y rellenados que posteriormente se iran
imprimiendo uno a uno a fichero como si se hubiera realizado “al vuelo”.
Esta última afirmación hay que comprenderla bien para afrontar el siguiente
escalafón que es el bloque marcado con color amarillo. En este bloque se complican
mucho mas las cosas porque cada uno de los nodos clase Base de los que aquí hablamos
puede ser a su vez la raíz de un árbol de clases Base. Esto hay que tenerlo en cuenta porque
la afirmación realizada anteriormente se complica algo mas, siendo estas estructuras como
Cola de Colas TTabla. Lo veremos mas detalladamente en el siguiente apartado.
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
131
En color Amarillo podemos distinguir un árbol de clases Base formado por 4
bases. La particularidad de este árbol reside en que tiene como raíz a la base “Region”, por
lo que sus nodos van a ser independientes del anterior grafo y entonces podremos ver al
grafo de color rosa como un GRAFO DE ÁRBOLES puesto que de cada nodo podrá salir
un nuevo árbol.
Esta particularidad ha de tenerse muy en cuenta a la hora de procesar el diagrama
para la generación de código puesto que las tablas y relaciones entre las mismas
dependerán en gran medida de lo que se acaba de explicar.
*árbol de clases Base con raíz en la clase Base “Region”
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
132
Dicho esto, para que se vea mas claro, vamos a ver la situación desde el punto de
vista práctico.
Aquí podemos ver el diagrama de la estructura GRAFO DE ÁRBOLES de clases
Base que hemos mencionado.
En ella podemos ver como existe un grafo (color rosa) y un árbol ( color amarillo)
con raíz en un nodo del grafo ( “Region”).
Para procesar este tipo de estructuras, siempre se hace de la misma forma y es
generando una estructura COLA DE COLAS de tipos TTabla.
Lo que hacemos es ir recorriendo el Grafo Aciclico Dirigido de forma que en cada
nodo del grafo nos adentramos para ver si existe algún árbol en el; de esta forma vamos
encolando las distintas instancias de TTabla que vamos instanciando conforme vamos
accediendo a una nueva clase Base dando lugar a una estructura mas simple de procesar y a
partir de la cual ya podemos obtener el código del script que sea de una manera mas
sencilla.
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
133
Para que nos hagamos a la idea de forma gráfica, a continuación se muestra un
ejemplo de la famosa estructura COLA DE COLAS.
Estructura COLA DE COLAS que se obtiene del bloque anterior
Podemos fijarnos en que el rojo se corresponde al GAD y el amarillo al ARBOL
En color rojo se ha marcado la COLA principal cuyos nodos están compuesto de
COLAS de clases TTabla.
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
134
Esto quiere decir que:
El primer elemento de la cola principal ( la de color rojo ) será una
COLA con un único elemento TTabla denominado “Customer Personal Data”
El Segundo elemento de la cola principal será una COLA con un
único elemento TTabla denominado “City”
El Tercer elemento de la cola principal será una COLA con 5
elementos TTabla encolados de forma que el primer elemento de la cola (el
primero en salir) será el que tiene por nombre “Region”
El Cuarto elemento de la cola principal será una COLA con un único
elemento denominado “State”
Esta estructura se obtiene tal y como hemos comentado, de explorar el grafo
principal en busca de los árboles que parten de cada nodo del mismo.
Tal y como hemos comentado anteriormente, la bondad de esta estructura esta en
que si nos fijamos detenidamente veremos que la estructura si se va desencolando y se va
generando el código ( da igual el tipo de esquema y de lenguaje salida ), el resultado será
similar al de ir procesando “al vuelo” el diagrama que el usuario ha dibujado, dándole un
toque mas profesional.
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
135
Tipos de datos implicados en la generación de código
En este apartado vamos a explicar los tipos de datos implicados en la generación de
código, uno a uno.
Teniendo el diagrama de clases visto anteriormente presente, pasamos a explicar
cada tipo de datos.
Enumeración MotorBBDD
Tipo de datos Enumeración que contiene los motores de Bases de Datos que
soporta la aplicación. Por defecto solo soporta ORACLE y SQL Server pero en el punto
“extensión de nuevos motores de BBDD destino”, vamos a explicar lo sencillo que
resultará la extensión de los mismos.
Este tipo de datos es utilizado internamente para realizar las conversiones
necesarias y únicamente ha de ser tenido en cuenta a la hora de programar nuevos scripts
para indicar el motor de base de datos destino en la clase TTabla ( se vera en el punto
“Especificación del motor de BBDD destino de la generación de código” ).
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
136
Clase TClaveAjena
Implementada para proporcionar una interfaz con la que generar nombres de
restricciones de claves ajenas o columnas afectadas por una clave ajena.
Posee 2 metodos estáticos unicamente que pasaremos a explicar:
GenerarNombreConstraint:
Genera un nombre de restriccion de clave ajena. Este nombre no esta validado
contra el Motor de BBDD por lo que hay que validarlo seguidamente.
o nombreTabla
Nombre de la tabla origen
o nombreTablaReferenciada
Nombre de la tabla destino
o Devuelve
Nombre de la restriccion como FK_nombretabla_to_nombretablareferenciada
public static string GenerarNombreConstraint(string nombreTabla, string
nombreTablaReferenciada)
{
return( "FK_" + nombreTabla + "_to_" + nombreTablaReferenciada
);
}
GenerarListaColumnas:
DEPRECATED - No se usa, pero se da soporte por si se desea utilizar.
Genera una lista de las columnas afectadas por la clave ajena.
Esto es util por ejemplo cuando nuestra tabla ajena tiene una clave primaria
compuesta de varias columnas y por lo tanto tenemos varias columnas en nuestra tabla que
apuntan hacia la tabla principal.
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
137
o colaColumnas
Cola FIFO con TColumna columnas afectadas (generalmente se pasa la cola
FIFO de clave primaria de la tabla principal.
o Devuelve
Cadena separada con comas con los nombres de las columnas que formaran
parte de la clave ajena
public static string GenerarListaColumnas(Queue colaColumnas,
MotorBBDD motor)
{
//Inicializo a vacio por si las moscas
string listaColumnas="";
foreach(TColumna col in colaColumnas)
{
listaColumnas +=
Validacion.ValidateString(col.NombreColumna,motor);
}
// Le quito la coma del final
listaColumnas =
listaColumnas.Substring(0,listaColumnas.Length-1);
return(listaColumnas);
}
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
138
Clase MetodosBase
Esta clase contiene únicamente los métodos utilizados para obtener información
sobre las clases Base de nuestro modelo multidimensional.
Posee únicamente 4 métodos y son utilizados internamente por la clase TTabla para
la generación de los scripts SQL.
Pasamos a detallar su funcionamiento de forma mas detallada a continuación:
Contiene
Sirve para saber si una base determinada ya se ha añadido a una COLA de
COLAS de bases
Devuelve true si la base esta contenida en la cola de colas de bases o false si no lo
es.
public static bool Contiene(Base baseRaiz, Queue colaBases)
{
bool retorno = false;
// Recorremos las colas que tenemos dentro de la cola de colas y
vemos en las bases que contienen por si estuvieran.
foreach(Queue cola in colaBases)
{
if (cola.Contains(baseRaiz))
retorno = true;
}
return(retorno);
}
EsBaseHoja
Una clase base es una hoja, cuando de ella no podemos llegar a otra base.
Devuelve true si la base es una base hoja.
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
139
public static bool EsBaseHoja(Base b)
{
bool retorno;
// Es una base hoja si no podemos ir a otra base desde ella (su
contador de bases relacionadas es 0)
retorno = ( (b.baseAssociates2.Count == 0) ? true : false );
return(retorno);
}
RecorrerBasesHijas
Implementacion de un algoritmo DFS de recorrido de grafos dirigidos en
profundidad.
Esta funcion se utiliza para recorrer las clases Bases que heredan de una clase Base.
Estas clases "Base" se organizan segun un arbol por lo que la mejor forma para
recorrerlo es utilizando un algoritmo para ello.
o baseRaiz
Es la base a partir de la cual vamos a ir recorriendo hacia abajo siguiendo un
recorrido en profundidad
o Visitados
Cola donde se van a ir guardando los nodos (Bases) visitadas según el
recorrido en profundidad del árbol. Se guardan en una estructura tipo FIFO para
que al generarse el código, se genere como si lo hubiéramos ido generando online.
Se pasa por referencia puesto que esa estructura se va a ir modificando
(añadiéndose nodos visitados).
public static void RecorrerBasesHijas(Base baseRaiz, ref Queue
visitados)
{
visitados.Enqueue(baseRaiz);
// Me meto por todas las bases hijas. Solo me interesan ellas.
foreach(Base b in baseRaiz.parentToChild)
{
if (!visitados.Contains(b))
{
// Recordemos que solo nos interesan las bases hijas (
las que hereden de ella )
RecorrerBasesHijas(b,ref visitados);
}
}
}
RecorrerBases
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
140
Implementacion de un algoritmo DFS de recorrido de grafos dirigidos en
profundidad.
Esta funcion se utiliza para recorrer las clases Bases que cuelgan de las clases
Dimension.
Estas clases "Base" se organizan segun un grafo aciclico dirigido por lo que la
mejor forma para recorrerlo es utilizando un algoritmo para ello.
o baseRaiz
Es la base a partir de la cual vamos a ir recorriendo hacia abajo siguiendo un
recorrido en profundidad
o visitados
Cola donde se van a ir guardando los nodos (Bases) visitadas según el
recorrido en profundidad del grafo. El contenido van a ser Colas de Bases. De
forma que tendremos una Cola de Colas Base. De esta forma tendremos en una
posición de la cola a todas las bases relacionadas con parentesco. Se guardan en una
estructura tipo FIFO para que al generarse el código, se genere como si lo
hubiéramos ido generando online. Se pasa por referencia puesto que esa estructura
se va a ir modificando (añadiéndose nodos visitados).
public static void RecorrerBases(Base baseRaiz, ref Queue
visitados)
{
Queue basesHijas = new Queue();
RecorrerBasesHijas(baseRaiz,ref basesHijas);
visitados.Enqueue(basesHijas);
foreach(Base b in baseRaiz.baseAssociates2)
{
if (!Contiene(b,visitados))
{
RecorrerBases(b,ref visitados);
}
}
}
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
141
Clase Validacion
Contiene la clase que se encarga de todo lo que tenga que ver con la validación de
objetos en la generación de código. En ella podremos validar nombres de objetos dentro de
un determinado motor de base de datos, construir tipos de datos de columnas validos
dentro de un motor de base de datos específicos...
Implementación de la clase:
Validacion (constructor de la clase)
Constructor de la clase estatica Validacion
Puesto que es una clase estática, este se ejecuta una sola vez y de forma
transparente, cargando en la propiedad estatica palabrasReservadas, la lista de palabras
reservadas de los motores de Bases de Datos. Esto nos servirá para validar nuestra cadena
contra las Bases de Datos y determinar si el nombre de objeto que queremos usar es una
palabra reservada. De ser así, tendremos que marcarla para que el motor de base de datos
no de error al procesar el script.
El fichero con las palabras reservadas se llama PalabrasReservadas.txt y se instala
en la ruta por defecto de instalacion de Windows, cuando se realiza la instalacion del
pluggin. Editando ese fichero podremos añadir o quitar palabras reservadas de los motores
de bases de datos. Actualmente el fichero contiene TODAS las palabras reservadas del
motor de Base de Datos SQL Server 2005
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
142
La unica separacion entre palabra y palabra dentro del fichero es el \n por lo que no
hace falta marcar ni el inicio ni el fin de fichero.
static Validacion()
{
palabrasReservadas = new Queue();
StreamReader sr;
try
{
sr = new
StreamReader(@"PalabrasReservadas.txt");
String palabra;
while((palabra = sr.ReadLine()) != null)
{
palabrasReservadas.Enqueue(palabra);
}
}
catch(Exception e)
{
Throw;
}
}
BuildSQLDataType
Construye el tipo de datos en función del tipo de datos seleccionados en el combo
de la interfaz del Visual Studio 2005. Este cuadro aparece cuando pulsamos sobre las
propiedades del objeto ( menú view -> properties window ( F4 ) )
De esta forma, si tiene que tener tamaño y no se le pasa (o se le pasa cuando no
tendría que hacerse), da un warning.
o a
Tipo Attribute (FactAttribute, DimensionAttribute,...)
o MotorBBDDSalida
Lenguaje del Motor de BBDD que va a soportar las sentencias generadas
(ORACLE, SQL SERVER...)
public static string
BuildSQLDataType(Proyecto.Fincarrera.ObjectOrientedMultidimensionalModel.DomainModel.Attri
bute a, MotorBBDD MotorBBDDSalida)
{
string retorno="";
bool mostrarWarningSobraTam = false;
bool mostrarWarningFaltaTam = false;
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
143
switch(a.dataType)
{
case(DataType.BOOLEAN):
switch(MotorBBDDSalida)
{
case(MotorBBDD.ORACLE):
retorno = "boolean";
break;
case(MotorBBDD.SQLServer):
retorno = "bit";
break;
default:break;
}
if(a.tam!="")
{
mostrarWarningSobraTam = true;
}
break;
case(DataType.CHARACTER):
switch(MotorBBDDSalida)
{
case(MotorBBDD.ORACLE):
retorno = "char";
break;
case(MotorBBDD.SQLServer):
retorno = "char";
break;
default:break;
}
if(a.tam=="")
{
mostrarWarningFaltaTam = true;
retorno += "(10)";
}
else
{
retorno+= "(" + a.tam + ")";
}
break;
case(DataType.CHARACTER_VARYING):
switch(MotorBBDDSalida)
{
case(MotorBBDD.ORACLE):
retorno = "varchar2";
break;
case(MotorBBDD.SQLServer):
retorno = "varchar";
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
144
break;
default:break;
}
if(a.tam=="")
{
mostrarWarningFaltaTam = true;
retorno += "(10)";
}
else
{
retorno+= "(" + a.tam + ")";
}
break;
case(DataType.DATE):
switch(MotorBBDDSalida)
{
case(MotorBBDD.ORACLE):
retorno = "date";
break;
case(MotorBBDD.SQLServer):
retorno = "datetime";
break;
default:break;
}
if(a.tam!="")
{
mostrarWarningSobraTam = true;
}
break;
case(DataType.DECIMAL):
switch(MotorBBDDSalida)
{
case(MotorBBDD.ORACLE):
retorno = "number";
break;
case(MotorBBDD.SQLServer):
retorno = "decimal";
break;
default:break;
}
if(a.tam=="")
{
mostrarWarningFaltaTam = true;
retorno += "(10,2)";
}
else
{
retorno+= "(" + a.tam + ")";
}
break;
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
145
case(DataType.FLOAT):
switch(MotorBBDDSalida)
{
case(MotorBBDD.ORACLE):
retorno = "number";
break;
case(MotorBBDD.SQLServer):
retorno = "float";
break;
default:break;
}
// Ni en sql server, ni en oracle se necesita precision,
pero se puede poner opcional
if(a.tam!="")
{
retorno+= "(" + a.tam + ")";
}
break;
case(DataType.INTEGER):
switch(MotorBBDDSalida)
{
case(MotorBBDD.ORACLE):
retorno = "number";
break;
case(MotorBBDD.SQLServer):
retorno = "int";
break;
default:break;
}
if(a.tam!="")
{
mostrarWarningSobraTam = true;
}
break;
case(DataType.REAL):
switch(MotorBBDDSalida)
{
case(MotorBBDD.ORACLE):
retorno = "number";
break;
case(MotorBBDD.SQLServer):
retorno = "real";
break;
default:break;
}
break;
default:break;
}
if (mostrarWarningSobraTam)
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
146
{
Warning(String.Format("El atributo {0} es de tipo {1} y a pesar de que
no tiene tamaño, se le ha puesto un tamaño de {2}. Por favor, dejar vacio para evitar este
warning.",a.Name,a.dataType,a.tam));
}
else if (mostrarWarningFaltaTam)
{
Warning(String.Format("El atributo {0} es de tipo {1} y no tiene tamaño,
se le ha puesto un tamaño de 10 por defecto . Por favor, pon un tamaño
correcto.",a.Name,a.dataType));
}
return (retorno);
}
FixStringORACLE
Esto es útil para poder utilizar nombres de tablas, columnas,...con espacios en
blanco, pero que solo es compatible con ORACLE. Si se superan los 16 caracteres por
nombre, se trunca a 16 para que no falle el script ya que en ORACLE 9i, el máximo
nombre tanto de tabla como de columna viene dado por identificadores de 16 caracteres.
o typeName
Nombre con espacios en blanco, o caracteres raros, que hay que delimitar.
private static string FixStringORACLE(string typeName)
{
string retorno;
// El tamaño tope para nombres (tanto para tablas como
para columnas) es de 128 caracteres.
if (typeName.Length > 16 )
retorno = typeName.Substring(typeName.Length - 16
,16);
else
retorno = typeName;
return("\"" + retorno + "\"");
}
FixStringSQLServer
Esto es útil para poder utilizar nombres de tablas, columnas,...con espacios en
blanco, pero que solo es compatible con SQL Server. Si se superan los 128 caracteres por
nombre, se trunca a 128 para que no falle el script ya que en SQL Server 2005, el máximo
nombre tanto de tabla como de columna viene dado por identificadores de 128 caracteres.
o typeName
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
147
Nombre con espacios en blanco que hay que delimitar ( o caracteres
especiales )
private static string FixStringSQLServer(string typeName)
{
string retorno;
// El tamaño tope para nombres (tanto para tablas como
para columnas) es de 128 caracteres.
if (typeName.Length > 128 )
retorno = typeName.Substring(typeName.Length -
128,128);
else
retorno = typeName;
return("[" + retorno + "]");
}
FixWhiteSpacesANSI
DEPRECATED - No se usa Este método sirve para reemplazar todos los espacios
en blanco que hay en el nombre que se le pasa, por caracteres "_" Todos los nombres que
devuelve, son por tanto, compatibles con SQL Standard.
o typeName
Nombre con espacios en blanco, o caracteres raros, que hay que arreglar.
private static string FixWhiteSpacesANSI(string typeName)
{
return(Regex.Replace(typeName," ","_").ToString());
}
GetTipoDatosClavePrimaria
Devuelve el tipo de datos de la clave primaria de una columna para el motor de
Base de datos que se especifique. útil para columnas auto incrementadas o claves primarias
de una sola columna.
o Ls
Motor de Base de datos destino para el que se esta generando el script.
o Return Value
devuelve un tipo number si es para oracle o un tipo int para SQL server.
public static string GetTipoDatosClavePrimaria(MotorBBDD ls)
{
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
148
string retorno="";
switch(ls)
{
case(MotorBBDD.ORACLE):
retorno="number";
break;
case(MotorBBDD.SQLServer):
retorno="int";
break;
default:break;
}
return(retorno);
}
PuedeSerAutonumerico
Nos indica si el tipo de datos que se le pasa puede ser auto numérico o no. Los tipos
auto numéricos de SQL Server son: int, bigint, smallint, tinyint, or decimal or numeric with
a scale of 0 El generador de tipos Validacion.BuildSQLDataType(...) solo genera tipos
"int" , luego solo hay que comprara con este.
o tipoDatos
String con el tipo de datos
public static bool PuedeSerAutonumerico(string tipoDatos)
{
bool retorno = false;
if (tipoDatos == "int")
retorno = true;
return(retorno);
}
ValidateStringANSI
Validacion de cadenas para adecuarlas a SQL Standard.
o cadena
String a validar con espacios en blanco o guiones
o Return Value
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
149
Devuelve todos aquellos espacios en blanco que pueda tener el string en "_" para
que sea SQL compilable.
private static string ValidateStringANSI(string cadena)
{
string retorno = "";
// Con esta expresion regular le digo que si encuentra
espacios (\s) o signos "-", se use la que le he dicho
string expresionRegular = @"(\s|-)";
Regex regex = new Regex(expresionRegular);
if (regex.IsMatch(cadena) ||
palabrasReservadas.Contains(cadena.ToUpper()))
{
//Generamos un Warning en la lista de errores de
Visual Studio usando el metodo TextTransformation.Warning.
MostrarWarning(String.Format("La clase \"{0}\"
contenia espacios en blanco y han sido sustituidos por carácteres
\"_\".", cadena));
retorno = FixWhiteSpacesANSI(cadena);
}
else
{
retorno = cadena;
}
return(retorno);
}
ValidateStringORACLE
Validacion de cadenas para adecuarlas a SQL Server.
Le ponemos unas comillas dobles.
o Cadena
Cadena a validar
private static string ValidateStringORACLE(string cadena)
{
string retorno = "";
// Con esta expresion regular le digo que si encuentra
espacios (\s) o signos "-", se use la que le he dicho
string expresionRegular = @"(\s|-)";
Regex regex = new Regex(expresionRegular);
if (regex.IsMatch(cadena) ||
palabrasReservadas.Contains(cadena.ToUpper()))
{
//Generamos un Warning en la lista de errores de
Visual Studio usando el metodo TextTransformation.Warning.
// MostrarWarning(String.Format("La clase \"{0}\"
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
150
contenia caracteres no permitidos, se ha utilizado la sintaxis específica
de ORACLE que puede que no funcione en todos los motores de BBDD.",
cadena));
retorno = FixStringORACLE(cadena);
}
else
{
retorno = cadena;
}
return(retorno);
}
ValidateStringSQLServer
Validacion de cadenas para adecuarlas a SQL Server.
Le ponemos unos corchetes.
o Cadena
String con el texto a validar
private static string ValidateStringSQLServer(string cadena)
{
string retorno = "";
// Con esta expresion regular le digo que si encuentra
espacios (\s) o signos "-", se use la que le he dicho
string expresionRegular = @"(\s|-)";
Regex regex = new Regex(expresionRegular);
if (regex.IsMatch(cadena) ||
palabrasReservadas.Contains(cadena.ToUpper()))
{
//Generamos un Warning en la lista de errores de
Visual Studio usando el metodo TextTransformation.Warning.
MostrarWarning(String.Format("La clase \"{0}\"
contenia caracteres no permitidos, se ha utilizado la sintaxis específica
de SQL Server que puede que no funcione en todos los motores de BBDD.",
cadena));
retorno = FixStringSQLServer(cadena);
}
else
{
retorno = cadena;
}
return(retorno);
}
ValidateString
Validacion de cadenas para adecuarlas al motor de base de datos que se especifique.
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
151
o Cadena
String con la cadena a validar
o Ls
Motor de base de datos destino con el que queremos validar la cadena
public static string ValidateString(string cadena, MotorBBDD
ls)
{
string retorno ="";
switch(ls)
{
case(MotorBBDD.SQLServer):
retorno = ValidateStringSQLServer(cadena);
break;
case(MotorBBDD.ORACLE):
retorno = ValidateStringORACLE(cadena);
break;
default:break;
}
return(retorno);
}
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
152
Clase Tcolumna
Tipo de datos Columna. Usada por la clase TTabla Contiene el nombre y tipo de
datos de una determinada columna. Además, como norma se ha establecido guardar como
nombre de la columna, el nombre real que el usuario le ha dado al objeto, sin validar ni
nada. De esta forma podemos construir nombres mas complejos ( como nombres
identificación de restricciones de clave ajena) sin que afecten para nada los métodos de
validación.
Tanto el nombre de la columna como el tipo de datos se definen al crear la instancia
de la clase y ya no se pueden modificar.
El acceso a estas propiedades privadas se realiza mediante las propiedades públicas
NombreColumna y TipoDatos, que tienen implementado únicamente la operación get ,
impidiendo así modificación alguna.
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
153
Clase TTabla
Tipo de datos Tabla.
Es la clase principal que usamos para la generación de código. En ella se
especifican las tablas ajenas, las claves primarias, las columnas...que necesitamos conocer
para generar las sentencias SQL. Internamente soporta todo esto mediante tipos de datos
Colas FIFO para que parezca que se genera el código mientras se procesa el archivo
.oomm.
Hay que indicar que las columnas de la tabla se indican por medio de dos métodos
que son AddColumna y AddClavePrimaria. Si nos damos cuenta no existe ninguno para
indicar claves ajenas puesto que eso se indica añadiendo tablas ajenas. De esta forma no
tenemos que saber que columnas de la tabla a añadir forman parte de nuestras columnas ni
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
154
nada. Mediante el funcionamiento interno del TTabla, se detectaran las columnas que han
de formar parte de la tabla resultante y que han de ir como tabla ajena a las tablas marcadas
como tablas ajenas.
Este método es bastante simple porque nosotros si que sabemos en todo momento
qué tablas vamos a tener que apuntar desde donde estamos, pero en un primer momento no
sabemos qué columnas tiene esa tabla y cuales de las nuestras van a apuntar a ella sin tener
que procesar la tabla ajena antes. El proceso de generación de sentencias de foreign key se
realizará en la fase de generación de código y no en la de preprocesamiento del diagrama.
Esto quiere decir que nosotros primero generaremos las estructuras TTabla y cuando
tengamos que generar el código será cuando analicemos las claves ajenas que necesita y si
la tabla a la que apunta existe ya o no.
Campos Privados
clavePrimaria Cola FIFO de objetos TColumna que identifica a la clave primaria. De esta forma tendremos en esa estructura a todas las columnas que formen la clave primaria
columnas Cola FIFO de objetos TColumna que identifica a las columnas de la tabla que no forman parte de la clave primaria.
nombreTabla Nombre de la tabla que se generara si ejecutamos su método GenerarSentenciaCreateTable()
tablasAjenas Cola FIFO con todas las TTabla ajenas (todas las dimensiones a las que apunta un hecho, por ejemplo). útil para que podamos generar las sentencias ALTER TABLE ... FOREIGN KEY fácilmente.
typeClaseDiagrama Almacena el tipo de datos de la clase que ha generado este tipo tabla. (Dimension, Fact,...) útil para generar create tables y demas.
Campos Estáticos
MotorBBDDSalida Propiedad estática que identifica el motor de BBDD para el que va a ir destinado el código que se auto generará.
StringMotorBBDD Propiedad estática con la que obtendremos el string del motor de BBDD para el que va a ir destinado el código que se auto generará.
Propiedades públicas
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
155
Todas las propiedades públicas tienen permisos de lectura únicamente por lo tanto
no se pueden modificar una vez asignadas. De hecho se generan conforme vamos
añadiendo columnas y/o tablas ajenas.
ClavePrimaria Cola FIFO de tipos TColumna con las columnas que forman parte de la clave primaria
Columnas Cola FIFO de tipos TColumna con las columnas de la tabla que no forman parte de la clave primaria
NombreTabla Solo se permite el acceso de lectura. El nombre de la tabla se da en el constructor únicamente.
StringClavePrimaria String con la clave primaria, ya validado. Si es compuesta, ya nos da las columnas separadas por comas y validadas.
Métodos
TTabla – Constructor
Constructor de tipo TTabla.
Unicamente hace falta indicar el nombre de la tabla que va a generar y el tipo de
clase de nuestro diagrama que la ha generado(Fact, Dimension, Base...).
o Nom
Nombre de la tabla en cuestion
o tipoClase
Tipo de datos de la clase que va a generar la tabla ( Fact, Dimension,
Base,…)
public TTabla(string nom, Type tipoClase)
{
this.nombreTabla = nom;
this.typeClaseDiagrama = tipoClase;
clavePrimaria = new Queue();
columnas = new Queue();
tablasAjenas = new Queue();
}
AddClavePrimaria
Añade una columna a nuestra cola fifo de columnas de claves primarias.
o Col
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
156
Tcolumna a añadir como clave primaria de la tabla.
Puesto que puede ser clave primaria compuesta con lo que iríamos llamando a este
método con cada una de las claves que quisiéramos que formaran parte de la clave primaria
de la tabla.
public void AddClavePrimaria(TColumna col)
{
clavePrimaria.Enqueue(col);
}
AddColumna
Añade una columna a nuestra cola fifo de columnas (no forma parte de las claves
primarias).
o Col
Tcolumna a añadir como clave primaria de la tabla
Las columnas que se añadan aquí, no formaran parte de la clave primaria por lo
tanto o se añaden como columna normal, o se añaden como clave primaria.
public void AddColumna(TColumna col)
{
columnas.Enqueue(col);
}
AddTablaAjena
Añade una tabla a nuestra cola fifo de tablas a las que apunta.
Util para generar las sentencias alter table...foreign key...
o tablaAjena
TTabla a la que apunta. No es necesario indicar nada mas como que
columnas las relacionan ni nada
public void AddTablaAjena(TTabla tablaAjena)
{
tablasAjenas.Enqueue(tablaAjena);
}
AñadirAtributosComoTColumnas
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
157
Añade todos los atributos de la clase que se le pase como parámetro, a la lista de
TColumnas columnas.
o clase
Clase ( Fact, Base, DegenerateFact,…) de la cual queremos extraer todos los
TColumnas necesarios y añadírselos a la TTabla que estamos rellenando
actualmente
clase actualmente solo tiene sentido que sea una base, es por ello que esta dentro
de un if, pero se puede extender su funcionalidad si se desea fácilmente añadiendo if´s.
private void AñadirAtributosComoTColumnas(Clase clase)
{
string nombreColumna, tipoDatos;
if(clase is Base)
{
Base bv = (Base)clase;
string nombreBase = bv.Name;
// Hemos restringido a que solo exista un único OID y no tiene
porque estar definido por defecto
if (bv.OID.Count==1)
{
OID oid = (OID)bv.OID[0];
nombreColumna = nombreBase + "_" + oid.Name;
tipoDatos = Validacion.BuildSQLDataType(oid ,
MotorBBDDSalida);
AddColumna(new TColumna(nombreColumna,tipoDatos));
}
// Ahora voy a por el atributo "Descriptor"
// Como solo hay uno y ademas es obligatorio:
Descriptor descriptor = (Descriptor)bv.descriptor[0];
nombreColumna = nombreBase + "_" + descriptor.Name;
tipoDatos = Validacion.BuildSQLDataType(descriptor ,
MotorBBDDSalida);
AddColumna(new TColumna(nombreColumna,tipoDatos));
//Ahora voy a sacar cada uno de los DimensionAttribute para que
salga segun se pide Nombrebase_DimensionAtribute
foreach(DimensionAttribute da in bv.dimensionAttribute)
{
nombreColumna = nombreBase + "_" + da.Name;
//Ahora construye el tipo de datos en funcion de la
informacion proporcionada por el Atributo.
tipoDatos = Validacion.BuildSQLDataType(da ,
MotorBBDDSalida);
AddColumna(new TColumna(nombreColumna,tipoDatos));
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
158
} // cierro el foreach
} // if (clase is Base)
}
DevolverBasesNormalizadas
Extraemos todas las bases que cuelgan y están conectadas de la dimensión Vamos
almacenando las bases visitadas en una cola(FIFO) según las vayamos visitando según un
algoritmo DFS (Recorrido de grafos aciclicos dirigidos en profundidad). Ya se ha
explicado con anterioridad la estructura de COLA DE COLAS TTabla de la que aquí se
hace gala.
o bv
Esta base que se le pasa, ha de ser la base que esta conectada a la
Dimension. Es decir, de la que cuelgan el resto de bases formando una estructura
de Grafo Aciclico Dirigido.
private Queue DevolverBasesNormalizadas(Base bv)
{
Queue retorno = new Queue();
// Ahora extraemos todas las bases que cuelgan y estan conectadas de
la dimension
// Vamos almacenando las bases visitadas en una cola(FIFO) segun las
vayamos visitando segun un algoritmo DFS (Recorrido de grafos aciclicos dirigidos
en profundidad)
Queue colaBasesVisitadas = new Queue();
//Recorro las bases que tiene por debajo la clase "Dimension" y que
siguen una estructura
// de tipo Grafo Aciclico Dirigido.
RecorrerBases(bv,ref colaBasesVisitadas);
// La primera cola que tenemos contiene la base principal (la que
esta conectada a la dmension) ya esta en una tabla con la dimension en su primera
posicion de la cola , por lo que no hay que crear ninguna tabla con ella
colaBasesVisitadas.Dequeue();
//Una vez tengo ya todas las bases en esta estructura, las voy a ir
recorriendo y generando las estructuras TTabla que luego devolvere en un Array
foreach(Queue colaBases in colaBasesVisitadas)
{
// Recordemos que en las colas de bases que obtenemos, la
primera de las bases que hay en cada cola es la que
// nos interesa porque las que hay despues de ella son hijas
Base b = (Base) colaBases.Peek();
//Creo el tipo TTabla
TTabla tablaBase = new TTabla(b.Name,b.GetType());
tablaBase.GenerarClavePrimaria(b);
tablaBase.ObtenerColumnasRestantes(b);
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
159
// Puede ser que esta base tenga hijos, por lo que una vez
definida, vamos a añadirle las columnas que vengan de los hijos (si es que tiene)
Queue colaBasesHijas = new Queue();
RecorrerBasesHijas(b, ref colaBasesHijas);
// Elimino la primera de la cola porque soy yo misma ;)
colaBasesHijas.Dequeue();
foreach(Base beis in colaBasesHijas)
{
tablaBase.AñadirAtributosComoTColumnas(beis);
}
retorno.Enqueue(tablaBase);
}
return(retorno);
}
DevolverColumnasClavePrimariaConTipoDatos
Solo se lanzara si la clave primaria es compuesta. Especial para casos como las
clases Fact. Se llama desde GenerarSentenciaCreateTable(..) Genera la cadena
clavePrimaria útil cuando la clave primaria es compuesta por varias columnas.
ALTER TABLE nombreFact
ADD CONSTRAINT nombreConstraint
FOREIGN KEY ( nombreFK )
REFERENCES nombreTabla ( clavePrimaria )
o Return Value
String con las columnas que necesitamos para definir una sentencia
ALTER TABLE ... PRIMARY KEY...
o Remarks
GenerarSentenciaAlterTablePrimaryKey (desde aquí se llama)
protected string DevolverColumnasClavePrimariaConTipoDatos()
{
string retorno ="";
foreach(TColumna col in clavePrimaria)
{
string nombreColumnaValidado =
Validacion.ValidateString(col.NombreColumna, MotorBBDDSalida);
retorno += nombreColumnaValidado + " " + col.TipoDatos + "
NOT NULL,\n";
}
// Le quito la coma del final.
retorno = retorno.Substring(0,retorno.Length-2);
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
160
return(retorno);
}
ExisteDegenerateFact
Con esto miramos si hay alguna clase FactAttribute entre la Dimension y la clase
Fact que queramos. Esto es útil para evitar añadir columnas en Fact de una Dimension
cuando hay una relación FactAttribute de por medio.
o dim
Dimension a la que tendría que haber una clase DegenerateFact conectada.
o clase
Clase Fact a la que tendría que haber una clase DegenerateFact
conectada
o Return Value
true -> si existe degenerate fact conectada a la dimension y a la clase Fact en
cuestión
false -> caso contrario
public bool ExisteDegenerateFact(Dimension dim, Fact clase)
{
bool retorno = false;
if(dim.factClassAssociatesDegenerateFact.Count>0)
{
foreach(DegenerateFact df in
dim.factClassAssociatesDegenerateFact)
{
Fact efe =
df.degenerateFactAssociatesFactClass[0] is Fact ?
(Fact)df.degenerateFactAssociatesFactClass[0] :
(Fact)df.degenerateFactAssociatesFactClass[1];
if( ((Fact)clase).Name == efe.Name )
{
retorno = true;
}
}
}
return(retorno);
}
GenerarClavePrimaria
Genera la clave primaria de una clase que le pasemos de nuestro diagrama.
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
161
Simplemente le pasamos la clase Dimension, Fact, DegenerateFact,... y nos genera
la estructura ClavePrimaria que necesitamos.
o Si le pasamos una Dimension, la clave primaria es auto generada con
PK_nombreDimension
o Si le pasamos una Fact, la clave primaria es la que forman todos los
DegenerateDimension mas todas las claves primarias de las dimensiones a las
que apunta.
o Si le pasamos un DegenerateFact, la clave primaria es la unión de las
claves primarias de la clase Fact y Dimension a las que esta unida.
Solo genera la clave primaria, no tiene en cuenta para nada las demas columnas
que deba tener la tabla, para eso ya existe otro método. Una vez ha finalizado el método, ya
tenemos rellena la estructura Queue clavePrimaria de objetos TColumna.
o clase
Objeto clase de nuestro diagrama (Fact,Dimension,DegenerateFact)
public void GenerarClavePrimaria(Clase clase)
{
string nombreTablaSinValidar;
string clavePrimariaSinValidar;
string tipoDatos;
if (typeClaseDiagrama == typeof(Fact) )
{
// Ahora voy a por los atributos "DegenerateDimension"
foreach(DegenerateDimension dd in
((Fact)clase).f_degenerateDimension)
{
tipoDatos = Validacion.BuildSQLDataType(dd,MotorBBDDSalida);
nombreTablaSinValidar=((Fact)clase).Name;
clavePrimariaSinValidar = dd.Name;
// Lo guardo en la cola de claves primarias, las cadenas
validadas, porque como luego voy a ir haciendo concatenaciones, no quiero
problemas.
// Ademas como no va a ser esta columna clave ajena de otra,
sino que solamente formara parte de la clave primaria, lo indico diciendo que no
quiero que se genere ALTER TABLE con ella.
clavePrimaria.Enqueue(new
TColumna(clavePrimariaSinValidar,tipoDatos));
}
/// Estoy dentro de un "Fact" y voy a recorrer todas las dimensiones
del "Fact"
foreach( Dimension dim in ((Fact)clase).dimension)
{
bool dfExists = false;
/// Miramos si existe un DegenerateFact entre la dimension y su
clase Fact desde la que es apuntada
dfExists = ExisteDegenerateFact(dim, (Fact)clase);
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
162
if(!dfExists)
{
nombreTablaSinValidar = dim.Name;
clavePrimariaSinValidar = dim.Name;
tipoDatos =
Validacion.GetTipoDatosClavePrimaria(MotorBBDDSalida);
// No guardo en la cola de claves primarias, las
cadenas validadas, porque como luego voy a ir haciendo concatenaciones, no quiero
problemas.
clavePrimaria.Enqueue(new
TColumna(clavePrimariaSinValidar,tipoDatos));
}
}
}
//else if (clase is Dimension)
else if (typeClaseDiagrama == typeof(Dimension) )
{
// Como es una dimension, la clave primaria es autogenerada
clavePrimariaSinValidar= ((Dimension)clase).Name;
tipoDatos = Validacion.GetTipoDatosClavePrimaria(MotorBBDDSalida);
clavePrimaria.Enqueue(new
TColumna(clavePrimariaSinValidar,tipoDatos));
}
else if (typeClaseDiagrama == typeof(DegenerateFact) )
{
// Uno de los extremos es un tipo Dimension y el otro es un tipo
Fact.
// La clave primaria estara formada por las columnas de la clave
primaria de las dos clases a las que apunta, y luego ademas
// tendremos que apuntar con claves ajenas a esas columnas.
foreach(Clase cls in
((DegenerateFact)clase).degenerateFactAssociatesFactClass)
{
if (cls is Dimension)
{
// Generamos la estructura TTabla con las claves primarias de
la Dimension del extremo y luego añadimos sus columnas
Dimension dim = (Dimension)cls;
TTabla tablaDimension = new TTabla(dim.Name,dim.GetType());
tablaDimension.GenerarClavePrimaria(dim);
foreach(TColumna col in tablaDimension.ClavePrimaria)
{
clavePrimaria.Enqueue(col);
}
this.AddTablaAjena(tablaDimension);
}
if (cls is Fact)
{
// Generamos la estructura TTabla con las claves primarias de
la Fact del extremo y luego añadimos sus columnas
Fact fact = (Fact)cls;
TTabla tablaFact= new TTabla(fact.Name,fact.GetType());
tablaFact.GenerarClavePrimaria(fact);
foreach(TColumna col in tablaFact.ClavePrimaria)
{
clavePrimaria.Enqueue(col);
}
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
163
this.AddTablaAjena(tablaFact);
}
}
} //(typeClaseDiagrama == typeof(DegenerateFact) )
else if ( typeClaseDiagrama == typeof(Base) )
{
// En el caso de que sea una Base, su clave primaria viene dada por el
atributo Descriptor, ya que es unico y seguro que tiene uno
// Ahora voy a por el atributo "Descriptor"
// Como solo hay uno y ademas es obligatorio:
Base b = (Base)clase;
Descriptor descriptor = (Descriptor)b.descriptor[0];
clavePrimariaSinValidar = descriptor.Name;
tipoDatos = Validacion.BuildSQLDataType(descriptor , MotorBBDDSalida);
AddClavePrimaria(new TColumna(clavePrimariaSinValidar,tipoDatos));
}
}
GenerarComentario
Devuelve una cadena como comentario de SQL
o Comentario
Comentario que queremos que aparezca en el script de SQL
protected string GenerarComentario(string comentario)
{
string retorno;
retorno = "---\n";
retorno += "---" + comentario + "\n";
retorno += "---\n";
return(retorno);
}
GenerarSentenciaAlterTableForeignKey
Especial para casos como las clases Fact. Se llama desde
GenerarSentenciaCreateTable(..) Genera la cadena ALTER TABLE ... FOREIGN KEY …
El resultado es el siguiente:
ALTER TABLE nombreTabla
ADD CONSTRAINT nombreConstraint
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
164
FOREIGN KEY ( nombreFK )
REFERENCES nombreTabla ( clavePrimaria )
public string GenerarSentenciaAlterTableForeignKey()
{
string retorno="";
if (tablasAjenas.Count>0)
retorno = GenerarComentario("Creacion de las claves
ajenas a las tablas principales de la tabla " + NombreTabla);
if (typeClaseDiagrama != typeof(Base) && typeClaseDiagrama !=
typeof(Dimension) )
{
/// Recuerda que tanto en la tabla principal como en la
tabla ajena, tenemos el mismo nombre de columna. Por lo que simplemente
/// hay que hacer un alter table de las columas de mi
tabla a la tabla principal para que se hagan foreign key.
foreach(TTabla tabla in tablasAjenas)
{
string nombreTablaValidado =
Validacion.ValidateString(NombreTabla, MotorBBDDSalida);
string nombreConstraintValidado =
Validacion.ValidateString( TClaveAjena.GenerarNombreConstraint(
NombreTabla , tabla.NombreTabla ), MotorBBDDSalida );
string nombreTablaReferenciadaValidado =
Validacion.ValidateString(tabla.NombreTabla, MotorBBDDSalida);
retorno += "ALTER TABLE " + nombreTablaValidado +
"\n";
retorno += " ADD CONSTRAINT " +
nombreConstraintValidado + "\n";
retorno += " FOREIGN KEY ( " +
tabla.StringClavePrimaria + " )\n";
retorno += " REFERENCES " +
nombreTablaReferenciadaValidado + " ( " + tabla.StringClavePrimaria + "
)\n";
retorno += SeparadorSentencias() + "\n";
}
}
else
{
// El caso de las Base es algo mas especifico porque en
este caso las claves ajenas no son de la clave primaria, sino columnas
aparte.
foreach(TTabla tabla in tablasAjenas)
{
string nombreTablaValidado =
Validacion.ValidateString(NombreTabla, MotorBBDDSalida);
string nombreConstraintValidado =
Validacion.ValidateString( TClaveAjena.GenerarNombreConstraint(
NombreTabla , tabla.NombreTabla ), MotorBBDDSalida );
string nombreTablaReferenciadaValidado =
Validacion.ValidateString(tabla.NombreTabla, MotorBBDDSalida);
string nombreClaveAjenaValidado =
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
165
Validacion.ValidateString( "fk_" + tabla.NombreTabla , MotorBBDDSalida);
retorno += "ALTER TABLE " + nombreTablaValidado +
"\n";
retorno += " ADD CONSTRAINT " +
nombreConstraintValidado + "\n";
retorno += " FOREIGN KEY ( " +
nombreClaveAjenaValidado + " )\n";
retorno += " REFERENCES " +
nombreTablaReferenciadaValidado + " ( " + tabla.StringClavePrimaria + "
)\n";
retorno += SeparadorSentencias() + "\n";
}
}
return(retorno);
}
GenerarSentenciaAlterTablePrimaryKey
Solo se lanzara si la clave primaria es compuesta. Especial para casos como las
clases Fact. Se llama desde GenerarSentenciaCreateTable(..) Genera la cadena ALTER
TABLE ... PRIMARY KEY ... útil cuando la clave primaria es compuesta por varias
columnas.
El código que devuelve es el siguiente:
ALTER TABLE nombreTabla
ADD CONSTRAINT nombreConstraint
PRIMARY KEY( tabla.StringClavePrimaria )
private string GenerarSentenciaAlterTablePrimaryKey()
{
string retorno;
string nombreConstraint = Validacion.ValidateString("pk_" +
NombreTabla , MotorBBDDSalida);
string nombreTablaValidado =
Validacion.ValidateString(NombreTabla, MotorBBDDSalida);
retorno = GenerarComentario("Creacion de la clave primaria
compuesta de la tabla " + nombreTablaValidado);
retorno += "ALTER TABLE "+ nombreTablaValidado + "\n";
retorno += " ADD CONSTRAINT " + nombreConstraint + " PRIMARY
KEY( " + StringClavePrimaria + " )\n";
return(retorno);
}
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
166
GenerarSentenciaCreateTable
Genera toda la Sentencia Create Table.
Genera el create table y si hace falta los alter tables para claves primarias
compuestas y claves ajenas que apuntan a otras tablas.
Se hace uso de los métodos GenerarSentenciaCreateTableSinClavesAjenas en las
que se devuelve la sentencia sin las claves ajenas a otras tablas más el método de
GenerarSentenciaAlterTableForeignKey en la que se añaden las claves ajenas que pueda
tener la tabla ( si no tiene, no se generaran ).
Si nos fijamos tiene el módificador “virtual” para que pueda ser sobrescrito en
cualquier clase que herede de la clase TTabla ( como vamos a ver cuando veamos
TTablaMultidimensional).
public virtual string GenerarSentenciaCreateTable()
{
string retorno;
retorno = GenerarSentenciaCreateTableSinClavesAjenas();
retorno += GenerarSentenciaAlterTableForeignKey();
return(retorno);
}
GenerarSentenciaCreateTableSinClavesAjenas
Genera la sentencia CREATE TABLE, pero no se tiene en cuenta si la tabla tiene
claves ajenas. Esto es útil cuando se esta generando el código de SNOWFLAKE para
generar primero las tablas y luego las relaciones entre las mismas.
Al igual que antes , posee el modificador “virtual” para que podamos sobrescribir el
método en las clases que hereden de ella.
public virtual string GenerarSentenciaCreateTableSinClavesAjenas()
{
string retorno;
string nombreTablaValidado =
Validacion.ValidateString(NombreTabla,MotorBBDDSalida);
// Esto valdra true cuando tengamos que eliminar la coma del
final de alguna columna
bool flagBorrarComaDelFinal = false;
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
167
retorno = "CREATE TABLE " + nombreTablaValidado + "\n";
retorno += " (\n";
retorno += this.DevolverColumnasClavePrimariaConTipoDatos();
// Si el tipo de clase que ha generado esta tabla es una
"Dimension", tiene una clave autogenerada y por tanto no hace falta un
alter table para indicar la clave primaria.
// Mas aun, si solo tenemos una columna como clave primaria (es
una dimension, pero puede darse luego en otro sitio)
if(ClavePrimaria.Count==1)
{
// En SQL Server tenemos el modificador IDENTITY para indicar
que se autocontrole el autonumérico.
// Si el tipo de datos de la columna puede ser autonumérico,
se pondra como identity (si no se controla esto, da error de sintaxis)
if( (MotorBBDDSalida == MotorBBDD.SQLServer) &&
Validacion.PuedeSerAutonumerico(( (TColumna)ClavePrimaria.Peek()
).TipoDatos) )
retorno += " IDENTITY";
retorno += " PRIMARY KEY \n";
}
// La coma de rigor para separar columnas la pondre solo si hay
mas columnas que generar ;)
if(Columnas.Count>0)
{
retorno += ",";
// Puesto que hay mas columnas, se generaran comas al final a
piñon y habra que borrar la última
flagBorrarComaDelFinal = true;
}
// Ahora faltan el resto de columnas que forman parte de la
tabla, con sus tipos de datos, claro.
foreach(TColumna col in this.Columnas)
{
string nombreColumnaValidado =
Validacion.ValidateString(col.NombreColumna, MotorBBDDSalida);
retorno += nombreColumnaValidado + " " + col.TipoDatos +
",\n";
}
// Le quito la coma del final.
if (flagBorrarComaDelFinal)
retorno = retorno.Substring(0,retorno.Length-2);
// Cierro el create table
retorno += "\n )\n";
// Tengo que separar el create table del alter table. Segun si
es oracle o sql server, usaremos uno u otro.
retorno += SeparadorSentencias() + "\n";
if(ClavePrimaria.Count>1)
{
// Si tengo clave primaria compuesta, la tengo que generar
ahora.
retorno += GenerarSentenciaAlterTablePrimaryKey();
retorno += SeparadorSentencias() + "\n";
}
return(retorno);
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
168
}
ObtenerColumnasRestantes
Genera las columnas en la estructura Columnas de nuestra TTabla. Simplemente le
pasamos la clase Dimension, Fact, DegenerateFact,... y nos genera la estructura Columnas
que necesitamos.
o Si le pasamos una Fact, las columnas serán los FactAttributes
o Si le pasamos un DegenerateFact, las columnas restantes serán las
DegenerateDimension y los FactAttribute
No tiene en cuenta las claves primarias, para eso ya existe otro método.
Una vez ha finalizado el método, ya tenemos rellena la estructura Columnas de
objetos TColumna.
public void ObtenerColumnasRestantes(Clase clase)
{
string nombreColumna;
string tipoDatos;
if (clase is Fact)
{
// Los atributos DegenerateDimension forman parte de la
clave primaria de la clase Fact, por eso no estan aqui, se generan en la
clave primaria
//Ahora voy a por los atributos "FactAttribute"
foreach(FactAttribute fa in
((Fact)clase).f_factAttribute)
{
nombreColumna = fa.Name;
tipoDatos = Validacion.BuildSQLDataType(fa ,
MotorBBDDSalida);
AddColumna(new TColumna(nombreColumna,tipoDatos));
}
}
else if (clase is DegenerateFact)
{
// Añado las columnas DegenerateDimension
foreach(DegenerateDimension dd in
((DegenerateFact)clase).df_degenerateDimension)
{
nombreColumna= dd.Name;
tipoDatos = Validacion.BuildSQLDataType(dd ,
MotorBBDDSalida);
AddColumna(new TColumna(nombreColumna,tipoDatos));
}
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
169
// Añado las columnas FactAttribute
foreach(FactAttribute fa in
((DegenerateFact)clase).df_factAttribute)
{
nombreColumna = fa.Name;
tipoDatos = Validacion.BuildSQLDataType(fa ,
MotorBBDDSalida);
AddColumna(new TColumna(nombreColumna,tipoDatos));
}
} //(clase is DegenerateFact)
else if (clase is Base)
{
Base bv = (Base)clase;
// Hemos restringido a que solo exista un único OID y no
tiene porque estar definido por defecto
if (bv.OID.Count==1)
{
OID oid = (OID)bv.OID[0];
nombreColumna = oid.Name;
tipoDatos = Validacion.BuildSQLDataType(oid ,
MotorBBDDSalida);
AddColumna(new TColumna(nombreColumna,tipoDatos));
}
//Ahora voy a sacar cada uno de los DimensionAttribute
para que salga segun se pide Nombrebase_DimensionAtribute
foreach(DimensionAttribute da in bv.dimensionAttribute)
{
nombreColumna = da.Name;
//Ahora construye el tipo de datos en funcion de la
informacion proporcionada por el Atributo.
tipoDatos = Validacion.BuildSQLDataType(da ,
MotorBBDDSalida);
AddColumna(new TColumna(nombreColumna,tipoDatos));
} // cierro el foreach
// Ahora van las columnas que van a ser clave ajena a las
otras bases a las que apuntemos con la direccion +d a +r
foreach(Base b in bv.baseAssociates2)
{
// Lo que voy a hacer es crearme una columna llamada
fk_nombreTablaBaseALaqueApunta
nombreColumna = "fk_" + b.Name;
Descriptor descriptor = (Descriptor)b.descriptor[0];
tipoDatos = Validacion.BuildSQLDataType(descriptor ,
MotorBBDDSalida);
AddColumna(new TColumna(nombreColumna, tipoDatos));
// A la vez lo añado en las tablas ajenas
TTabla tmp = new TTabla(b.Name, b.GetType());
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
170
tmp.GenerarClavePrimaria(b);
this.AddTablaAjena(tmp);
}
}
}
ObtenerDimensionDesnormalizada
útil para SQLEstrella y para Sowflake si optamos por desnormalizar la dimension
especifica que se le pasa
Obtiene las columnas restantes que debe tener la TTabla si estamos
desnormalizando por esa dimension. Esto quiere decir que vamos a pasarle un objeto tipo
Dimension y va a recorrer el Grafo Aciclico Dirigido de Bases que cuelga de el y nos va a
generar una tabla con todas las columnas que le pertenecen, desnormalizandolo todo.
Una vez ha finalizado el método, ya tendremos rellena la estructura Columnas
de objetos TColumna.
o dim
Dimension que vamos a desnormalizar obteniendo todo como columnas
para la tabla principal.
public virtual void ObtenerDimensionDesnormalizada(Dimension dim)
{
string nombreColumna;
string tipoDatos;
// Solo puede tener una relacion entre base y dimension
, pero como esta puede hacerse en los dos
// sentidos, pues lo controlo con los sentidos Base->
Dimension o Dimension -> Base (esto viene limitado por el DSL que podemos
crear con las DSL Tools)
if ( dim.baseAssociatesDim.Count == 1 ||
dim.dimAssociatesBase.Count == 1 )
{
Base b1 = ( (dim.baseAssociatesDim.Count==1) ?
(Base)dim.baseAssociatesDim[0] : (Base)dim.dimAssociatesBase[0]);
// Vamos almacenando las bases visitadas en una
cola(FIFO) segun las vayamos visitando segun un algoritmo DFS (Recorrido
de grafos aciclicos dirigidos en profundidad)
Queue colaBasesVisitadas = new Queue();
//Recorro las bases que tiene por debajo la clase
"Dimension" y que siguen una estructura
// de tipo Grafo Aciclico Dirigido.
RecorrerBases(b1,ref colaBasesVisitadas);
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
171
// Primero voy a sacar las colas de bases que
tenemos en la cola principal y que estan relacionadas por parentesco.
foreach(Queue colaFamiliaBases in
colaBasesVisitadas)
{
foreach(Base bv in colaFamiliaBases)
{
AñadirAtributosComoTColumnas(bv);
} // cierro el foreach (Base bv in
colaFamiliaBases
}// cierro el foreach de las colas de bases
}// cierro el if (dim.baseAssociatesDim != null)
}
ObtenerDimensionNormalizada
método para Snowflake. Genera la estructura para la dimension y devuelve un
Array con TTablas de las Bases.
o dim
Dimension que queremos normalizar
public Queue ObtenerDimensionNormalizada(Dimension dim)
{
string nombreColumna;
string tipoDatos;
Queue retorno = null;
// Solo puede tener una relacion entre base y dimension
, pero como esta puede hacerse en los dos
// sentidos, pues lo controlo con los sentidos Base->
Dimension o Dimension -> Base (esto viene limitado por el DSL que podemos
crear con las DSL Tools)
if ( dim.baseAssociatesDim.Count == 1 ||
dim.dimAssociatesBase.Count == 1 )
{
// Ahora la dimension al estar normalizada consta de
la clase dimension y la Base que tiene unida a el (si la tiene)
Base bv = ( (dim.baseAssociatesDim.Count==1) ?
(Base)dim.baseAssociatesDim[0] : (Base)dim.dimAssociatesBase[0]);
// Ahora toca añadir todas las columnas que hayan
provenientes de las bases hijas que pueda tener la base principal.
// Una vez ya se la base que va unida a la dimension,
tengo que recorrer sus hijas ( puede que tenga bases que hereden de ella)
Queue colaBasesHijas = new Queue();
RecorrerBasesHijas(bv, ref colaBasesHijas);
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
172
foreach(Base bb in colaBasesHijas)
{
AñadirAtributosComoTColumnas(bb);
// Ahora le faltan las claves primarias a las Bases
a las que esta conectada
// Por un lado se hacen columnas para cada
tabla base a la que esta conectada y por otro se añade esa TTabla a la
lista de claves ajenas
foreach(Base b in bb.baseAssociates2)
{
// Creamos columnas que haran referencia a
las otras tablas base a las que pueda estar conectada
nombreColumna = "fk_" + b.Name;
// Su clave primaria viene dada por el
atributo Descriptor, ya que es unico y seguro que tiene uno
// Ahora voy a por el atributo
"Descriptor"
// Como solo hay uno y ademas es
obligatorio:
Descriptor d =
(Descriptor)b.descriptor[0];
tipoDatos = Validacion.BuildSQLDataType(d
, MotorBBDDSalida);
this.AddColumna(new
TColumna(nombreColumna, tipoDatos));
// Añadimos a la lista de claves ajenas
TTabla tmpb = new TTabla(b.Name,
b.GetType());
tmpb.GenerarClavePrimaria(b);
this.AddTablaAjena(tmpb);
}
}
/// Ahora ya tenemos creada la TTabla de la dimension,
lo que vamos a hacer ahora es crearnos un Array donde meter las TTabla de
las Bases que hay por debajo
retorno = DevolverBasesNormalizadas(bv);
}// cierro el if (dim.baseAssociatesDim != null)
return(retorno);
}
SeparadorSentencias
Devuelve el separador de sentencias según el motor de BBDD al que va destinado
o Oracle ;
o SQL Server go
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
173
protected string SeparadorSentencias()
{
string retorno ="NO SE HA DEFINIDO CORRECTAMENTE EL LENGUAJE DE
SALIDA";
switch(MotorBBDDSalida)
{
case MotorBBDD.SQLServer:
retorno = "GO";
break;
case MotorBBDD.ORACLE:
retorno = ";";
break;
default:break;
}
return(retorno);
}
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
174
Clase TTablaMultidimensional
Tipo de datos Tabla. Es la clase principal que usamos para la generación de código
multidimensional. Hereda de la clase TTabla y redefine aquellos métodos necesarios para
generar código multidimensional.
Actualmente solo genera código multidimensional para ORACLE Warehouse
Builder, pero es sencillo añadir código de generación para MDX de SQL Server como se
podrá ver en el apartado “extensión de nuevos motores de BBD destino”.
Campos privados
colaBasesVisitadas
Contiene la cola FIFO de bases que
tiene por debajo la TTabla. Esta estructura
solo estará rellenada en el caso de que
seamos una dimension, porque son las
únicas que tienen el grafo de bases debajo.
La estructura se rellena en
ObtenerDimensionDesnormalizada()
Constructor TTablaMultidimensional
Llama al constructor de su clase base ( TTabla ) e inicializa a null la cola de bases
visitadas para que puedan generarse mas adelante desde el método
ObtenerDimensionDesnormalizada
public TTablaMultidimensional(string nombre, Type
tipo):base(nombre,tipo)
{
/// Sus datos se rellenan en el metodo
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
175
ObtenerDimensionDesnormalizada
colaBasesVisitadas = null;
}
GenerarCadenasAttribute
Genera la cadena ATTRIBUTE nombreAttribute DETERMINES ( ... ) necesaria para
la sentencia CREATE DIMENSION de ORACLE
private string GenerarCadenasAttribute()
{
string retorno,nombreAttribute,nombreColumna;
retorno = "";
if(colaBasesVisitadas!=null)
foreach(Queue colaBases in colaBasesVisitadas)
foreach(Base bv in colaBases)
{
nombreAttribute=
Validacion.ValidateString(bv.Name, MotorBBDDSalida);
// Los atributos solo van a poder ser OID y
DimensionAttribute, ya que los atributos Descriptor nos indican el nivel.
if (bv.OID.Count==1)
{
OID oid = (OID)bv.OID[0];
nombreColumna =
Validacion.ValidateString(bv.Name + "_" + oid.Name , MotorBBDDSalida);
retorno += "attribute " + nombreAttribute
+ " determines ( " + nombreColumna + " )\n";
}
foreach(DimensionAttribute da in
bv.dimensionAttribute)
{
nombreColumna =
Validacion.ValidateString(bv.Name + "_" + da.Name , MotorBBDDSalida );
retorno += "attribute " +
nombreAttribute + " determines ( " + nombreColumna + " )\n";
}
}
return(retorno);
}
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
176
GenerarCadenasLevel
Genera las cadenas level ... is ... que necesitamos para definir na estructura de
CREATE DIMENSION para ORACLE.
Devuelve todos los niveles de una dimension.
private string GenerarCadenasLevel()
{
string retorno;
string nombreLevel;
string nombreDimensionValidado;
string nombreColumna;
retorno="";
nombreDimensionValidado =
Validacion.ValidateString(NombreTabla,MotorBBDDSalida);
// Si no tiene bases por debajo es porque es una dimension
suelta, sin bases enganchadas a ella, por lo tanto se devolvera vacio
if (colaBasesVisitadas != null)
foreach(Queue colaBases in colaBasesVisitadas)
foreach(Base bv in colaBases)
{
nombreLevel =
Validacion.ValidateString(bv.Name, MotorBBDDSalida);
// El descriptor es el que actua como "clave
primaria", por eso voy directamente a por el.
Descriptor descriptor =
(Descriptor)bv.descriptor[0];
nombreColumna =
Validacion.ValidateString(bv.Name + "_" + descriptor.Name ,
MotorBBDDSalida);
retorno += "level " + nombreLevel + " is " +
nombreDimensionValidado + "." + nombreColumna + "\n";
}
return(retorno);
}
GenerarSentenciaAlterTableUnique
Genera una sentencia que hace que una determinada columna/s se conviertan en
unique.
Esto es necesario puesto que en oracle Warehouse Builder no tenemos claves
primarias en las tablas y con esto definimos las restricciones de unicidad necesarias para lo
que serian las claves primarias en el caso de que estuviéramos generando SQL snowflake o
estrella.
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
177
El código que generamos es el siguiente:
ALTER TABLE nombreTablaValidado
ADD CONSTRAINT nombreConstraint UNIQUE
(nombreColumnaValidado )
private string GenerarSentenciaAlterTableUnique()
{
string retorno;
string nombreConstraint =
Validacion.ValidateString(NombreTabla + "_uk", MotorBBDDSalida);
string nombreTablaValidado =
Validacion.ValidateString(NombreTabla, MotorBBDDSalida);
// Puesto que la clave unica es unica porque esto es sobre
una dimension, no tengo problemas en hacerlo asi
TColumna col = (TColumna)clavePrimaria.Peek();
string nombreColumnaValidado =
Validacion.ValidateString(col.NombreColumna, MotorBBDDSalida);
retorno = "ALTER TABLE " + nombreTablaValidado + " ADD
CONSTRAINT " + nombreConstraint + " UNIQUE ( " + nombreColumnaValidado +
" )\n";
retorno += SeparadorSentencias() + "\n";
return(retorno);
}
GenerarSentenciaCreateDimension
Genera la sentencia de CREATE DIMENSION sobre una tabla/s definidas con
anterioridad. De momento solo funciona con ORACLE por lo que el código es específico
para el mismo y habría que meter una condición que sirva para SQL Server por ejemplo.
public string GenerarSentenciaCreateDimension()
{
string retorno;
retorno = "";
// Si no tiene bases por debajo es porque es una dimension
suelta, sin bases enganchadas a ella, por lo tanto se devolvera vacio
if (colaBasesVisitadas != null)
{
string nombreTablaValidado =
Validacion.ValidateString(NombreTabla,MotorBBDDSalida);
retorno = "CREATE DIMENSION " + nombreTablaValidado +
"\n";
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
178
// Primero van las cadenas donde se definen los niveles
de la dimension
retorno += GenerarCadenasLevel();
// Luego se va a definir la cadena de las jerarquias
que tiene la dimension (formadas por las bases)
string stringJerarquias = "";
string temporal = "";
int contador = 0 ;
this.RecorrerJerarquias((Base)(
(Queue)(colaBasesVisitadas).Peek() ).Peek(), temporal, ref
stringJerarquias, ref contador);
// La cadena con las jerarquias (todas las que haya) se
devuelve en la variable recorrido
retorno += stringJerarquias;
// Ahora se genera la cadena donde se definen
retorno += GenerarCadenasAttribute();
// Cierro el create table
retorno += "\n";
// Tengo que separar el create table del alter table.
Segun si es oracle o sql server, usaremos uno u otro.
retorno += SeparadorSentencias() + "\n";
}
return(retorno);
}
GenerarSentenciaCreateTable
Genera una sentencia de CREATE TABLE sobrescribiendo el método de
GenerarSentenciaCreateTable de la clase base ( TTabla ). En este caso las tablas no tienen
claves primarias porque van a ser la base de un modelo multidimensional y en este caso
van a tener claves unique.
El resultado de esto será un CREATE TABLE sin claves ajenas y con claves unique
en lugar de claves primarias.
Además se realiza un COMMIT al final para que no de problemas de ejecución el
script en ORACLE cuando se ejecute todo de golpe porque puede que comience un
CREATE DIMENSION sobre esta tabla pero que no se hayan commiteado los cambios y
den error las sentencias posteriormente.
public override string GenerarSentenciaCreateTable()
{
string retorno;
retorno = this.GenerarComentario("Creacion de la tabla " +
NombreTabla);
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
179
retorno += this.GenerarSentenciaCreateTableSinClavesAjenas();
// En estas tablas multidimensionales no hay claves primarias,
pero si que hemos de definirnos una columna unique.
retorno += this.GenerarComentario("Modificacion para añadirle
la restriccion UNIQUE.");
retorno += this.GenerarSentenciaAlterTableUnique();
// Ahora pongo un commit para que los cambios se suban al
gestor de BBDD y no den problemas las modificacioens como CREATE
DIMENSION
retorno += GenerarComentario("Commiteo los cambios en la tabla
" + NombreTabla + " para que no den problemas las sentencias CREATE
DIMENSION que le puedan afectar.");
retorno += "COMMIT\n";
retorno += SeparadorSentencias() + "\n";
return(retorno);
}
GenerarSentenciaCreateTableSinClavesAjenas
Genera la sentencia de CREATE TABLE nombretabla pero no incluye en el código
devuelto las sentencias de claves ajenas a otras tablas.
public override string GenerarSentenciaCreateTableSinClavesAjenas()
{
string retorno;
string nombreTablaValidado =
Validacion.ValidateString(NombreTabla,MotorBBDDSalida);
retorno = "CREATE TABLE " + nombreTablaValidado + "\n";
retorno += " (\n";
retorno += this.DevolverColumnasClavePrimariaConTipoDatos();
// La coma de rigor para separar columnas la pondre solo si hay
mas columnas que generar ;)
if(Columnas.Count>0)
{
retorno += ",";
}
// Ahora faltan el resto de columnas que forman parte de la
tabla, con sus tipos de datos, claro.
foreach(TColumna col in this.Columnas)
{
string nombreColumnaValidado =
Validacion.ValidateString(col.NombreColumna, MotorBBDDSalida);
retorno += nombreColumnaValidado + " " + col.TipoDatos +
",\n";
}
// Le quito la coma del final, solo si tiene mas de una columna
if(Columnas.Count>1)
retorno = retorno.Substring(0,retorno.Length-2);
// Cierro el create table
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
180
retorno += "\n )\n";
// Tengo que separar el create table del alter table. Segun si
es oracle o sql server, usaremos uno u otro.
retorno += SeparadorSentencias() + "\n";
return(retorno);
}
ObtenerDimensionDesnormalizada
Rellena la estructura de "colaBasesVisitadas" que contiene la cola FIFO de bases
que tiene por debajo la ttabla con la que estamos trabajando.
public override void ObtenerDimensionDesnormalizada(Dimension
dim)
{
string nombreColumna;
string tipoDatos;
// Solo puede tener una relacion entre base y dimension
, pero como esta puede hacerse en los dos
// sentidos, pues lo controlo con los sentidos Base->
Dimension o Dimension -> Base (esto viene limitado por el DSL que podemos
crear con las DSL Tools)
if ( dim.baseAssociatesDim.Count == 1 ||
dim.dimAssociatesBase.Count == 1 )
{
Base b1 = ( (dim.baseAssociatesDim.Count==1) ?
(Base)dim.baseAssociatesDim[0] : (Base)dim.dimAssociatesBase[0]);
// Vamos almacenando las bases visitadas en una
cola(FIFO) segun las vayamos visitando segun un algoritmo DFS (Recorrido
de grafos aciclicos dirigidos en profundidad)
colaBasesVisitadas = new Queue();
//Recorro las bases que tiene por debajo la clase
"Dimension" y que siguen una estructura
// de tipo Grafo Aciclico Dirigido.
RecorrerBases(b1,ref colaBasesVisitadas);
// Ahora voy a sacar las bases que tiene por
debajo cada Clase "Dimension" y que han sido obtenidas por visitas
siguiendo el algoritmo DFS.
// Primero voy a sacar las colas de bases que
tenemos en la cola principal y que estan relacionadas por parentesco.
foreach(Queue colaFamiliaBases in
colaBasesVisitadas)
{
foreach(Base bv in colaFamiliaBases)
{
string nombreBase = bv.Name;
// Hemos restringido a que solo exista
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
181
un único OID y no tiene porque estar definido por defecto
if (bv.OID.Count==1)
{
OID oid = (OID)bv.OID[0];
nombreColumna = nombreBase + "_" +
oid.Name;
tipoDatos =
Validacion.BuildSQLDataType(oid , MotorBBDDSalida);
AddColumna(new
TColumna(nombreColumna,tipoDatos));
}
// Ahora voy a por el atributo
"Descriptor"
// Como solo hay uno y ademas es
obligatorio:
Descriptor descriptor =
(Descriptor)bv.descriptor[0];
nombreColumna = nombreBase + "_" +
descriptor.Name;
tipoDatos =
Validacion.BuildSQLDataType(descriptor , MotorBBDDSalida);
AddColumna(new
TColumna(nombreColumna,tipoDatos));
//Ahora voy a sacar cada uno de los
DimensionAttribute para que salga segun se pide
Nombrebase_DimensionAtribute
foreach(DimensionAttribute da in
bv.dimensionAttribute)
{
nombreColumna = nombreBase + "_" +
da.Name;
//Ahora construye el tipo de datos
en funcion de la informacion proporcionada por el Atributo.
tipoDatos =
Validacion.BuildSQLDataType(da , MotorBBDDSalida);
AddColumna(new
TColumna(nombreColumna,tipoDatos));
} // cierro el foreach
} // cierro el foreach (Base bv in
colaFamiliaBases)
}// cierro el foreach de las bases visitadas
}// cierro el if (dim.baseAssociatesDim != null)
}
RecorrerJerarquias
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
182
Implementación de un algoritmo DFS de recorrido de grafos dirigidos en
profundidad modificado para generar cadenas hierarchy. Esta función se utiliza para
generar la cadena de las jerarquías de una dimension. Estas clases "Base" se organizan
según un grafo aciclico dirigido por lo que la mejor forma para recorrerlo es utilizando un
algoritmo para ello.
o baseRaiz
Es la base a partir de la cual vamos a ir recorriendo hacia abajo
siguiendo un recorrido en profundidad
o stringJerarquias
Cadena que va a contener texto intermedio para generar la cadena que
devolverá (contenido) Inicialmente, entra cadena vacía.
o stringContenido
Resultado de la ejecución. Contendrá tantas jerarquías como caminos tenga
el grafo hasta las hojas. Inicialmente, cadena vacía.
o contador
Contador del numero de jerarquías (caminos) que hay desde la dimension a
las bases hojas. Inicialmente vale 0.
o Return Value
Cadenas hierarchi ... child of... necesarias para la generación de una cadena
CREATE DIMENSION de oracle.
public void RecorrerJerarquias(Base baseRaiz, string
stringJerarquias,ref string contenido, ref int contador)
{
stringJerarquias +=
Validacion.ValidateString(baseRaiz.Name,MotorBBDDSalida);
//Si no es una base hoja, vamos recorriendo en profundidad
el grafo
if ( !EsBaseHoja(baseRaiz) )
{
stringJerarquias += " child of ";
// Cada uno de los hijos es un camino posible para
llegar a una hoja, por eso hacemos la llamada recursiva.
foreach(Base b in baseRaiz.baseAssociates2)
{
RecorrerJerarquias(b, stringJerarquias, ref
contenido, ref contador);
}
}
else
{
// Si es una base hoja, concatenamos lo que tenemos al
final y continuamos.
stringJerarquias = "hierarchy h" + contador + " (" +
stringJerarquias + " )\n";
contador++;
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
183
contenido += stringJerarquias;
}
}
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
184
Especificación del motor de BBDD destino de la generación de código
La especificación del motor de Base de Datos destino de la generación de código se
hace mediante una propiedad estática de la clase TTabla , por la cual podremos indicar
para que Motor de BBDD queremos que salga nuestro script.
Esta propiedad estática tiene el tipo de datos enumeración MotorBBDD y existe
implementación para SQLServer y para ORACLE.
Una vez indicado el motor destino del script, se procederá a utilizar la clase TTabla
de la misma forma tanto si deseamos trabajar para oracle, como si deseamos trabajar para
SQL Server y gracias a tratarse de una propiedad estática, podremos generar cualquier
instancia nueva de tipo TTabla o TTablaMultidimensional nueva y podremos usarla con
normalidad sabiendo que el código generado por ella será el esperado.
Ejemplo de uso:
/// Especificacion del motor de Base de Datos para el que va a ir
destinado el script.
/// Por ahora: MotorBBDD.ORACLE
/// MotorBBDD.SQLServer
TTabla.MotorBBDDSalida = MotorBBDD.ORACLE;
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
185
Extensión de nuevos motores de BBDD destino
Si deseamos que nuestros scripts puedan generarse para nuevos motores de BBDD
que no sean los actualmente soportados ( ORACLE y SQL Server ), podemos hacerlo
fácilmente siguiendo los 6 pasos que a continuación describimos:
1. Añadir un identificador de enumeración nuevo para el motor de base
de datos nuevo en la enumeración MotorBBDD
2. Añadir el separador de sentencias que utiliza el motor de Base de
Datos destino para identificar dentro de un mismo script, las distintas ordenes (
“GO” en SQL Server y “;” en Oracle )
Para ello tenemos que añadir en el método SeparadorSentencias de la clase
TTabla, una nueva entrada en el switch-case.
protected string SeparadorSentencias()
{
string retorno ="NO SE HA DEFINIDO CORRECTAMENTE EL
LENGUAJE DE SALIDA";
switch(MotorBBDDSalida)
{
case MotorBBDD.SQLServer:
retorno = "GO";
break;
case MotorBBDD.ORACLE:
retorno = ";";
break;
default:break;
}
return(retorno);
}
3. Añadir un nuevo método de validacion de nombres de objeto para el
nuevo motor de base de datos soportado. Para ello hay que editar el metodo estatico
ValidateString de la clase estatica Validacion, añadiendole una nueva entrada en el
switch-case , asi como el nuevo método de validacion soportado
public static string ValidateString(string cadena, MotorBBDD ls)
{
string retorno ="";
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
186
switch(ls)
{
case(MotorBBDD.SQLServer):
retorno = ValidateStringSQLServer(cadena);
break;
case(MotorBBDD.ORACLE):
retorno = ValidateStringORACLE(cadena);
break;
default:break;
}
return(retorno);
}
* Este método se explicará mas en detalle en el punto “Modelo de validacion del
generador de código”
4. Añadir el tipo de datos por defecto de las columnas que haran de
clave principal en las tablas de la Base de Datos.Para ello basta con añadir una
nueva entrada en la estructura switch-case del método estatico
GetTipoDatosClavePrimaria de la clase estatica Validacion.
Por ejemplo, en SQL Server , las columnas que hacen de clave principal son
de tipo int, mientras que en ORACLE son de tipo number.
public static string GetTipoDatosClavePrimaria(MotorBBDD ls)
{
string retorno="";
switch(ls)
{
case(MotorBBDD.ORACLE):
retorno="number";
break;
case(MotorBBDD.SQLServer):
retorno="int";
break;
default:break;
}
return(retorno);
}
* Este método se explicará mas en detalle en el punto “Modelo de validacion del
generador de código”
5. Por ultimo solo falta añadir la conversion de tipos de datos específica
que se hará de acuerdo al estándar CWM para el nuevo motor de base de datos.
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
187
Por ejemplo, para Oracle el tipo de datos booleano se expresa como
boolean, mientras que para SQL Server se expresa como bit.
Para ello, basta con añadir los tipos de datos necesarios para cada uno de los
soportados por el modelo DSL que hemos diseñado y añadirlos a las sentencias
switch-case del método estático BuildSQLDataType de la clase estática Validacion.
public static string
BuildSQLDataType(Proyecto.Fincarrera.ObjectOrientedMultidimensionalModel.DomainModel.At
tribute a, MotorBBDD MotorBBDDSalida)
{
string retorno="";
bool mostrarWarningSobraTam = false;
bool mostrarWarningFaltaTam = false;
switch(a.dataType)
{
case(DataType.BOOLEAN):
switch(MotorBBDDSalida)
{
case(MotorBBDD.ORACLE):
retorno = "boolean";
break;
case(MotorBBDD.SQLServer):
retorno = "bit";
break;
default:break;
}
if(a.tam!="")
{
mostrarWarningSobraTam = true;
}
break;
…
… etc etc…
…
default:break;
}
return (retorno);
} * Este método se explicará mas en detalle en el punto “Modelo de validacion del
generador de código”
6. El último paso consiste unicamente en la adicion de los métodos
necesarios para construir el modelo Multidimensional de la nueva Base de Datos.
Por ejemplo para el modelo multidimensional de oracle warehouse builder, se
realizaron 3 métodos denominados: GenerarSentenciaCreateDimension,
GenerarCadenasLevel, GenerarCadenasAttribute y RecorrerJearquias, de
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
188
los cuales el unico expuesto con modificador “publico” es el de
GenerarSentenciaCreateDimension ya que es el que devuelve el string con la
sintaxis para generar la sentencia CREATE DIMENSION especifica de Oracle.
Este ultimo paso por tanto es el único que habria que implementar desde
cero, pero como se podrá comprobar viendo el código ( se vera mas adelante en el
punto “Generacion de código Oracle Warehouse builder” ) que tiene por debajo, no
es una tarea nada dificil.
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
189
Modelo de validación del generador de código
El modelo de validación del generador de código se realiza desde una clase estática
denominada Validacion. Esta clase ya esta explicada en detalle en el punto anterior por lo
que nos centraremos únicamente en explicar el por qué hemos realizado la validación de
forma estática y externa a las clases principales como son TTabla.
Hemos realizado esta separación para poder disponer en todo momento de una
validación independiente del objeto que estamos tratando ( tabla, columna, dimension,…)
y del lenguaje de generación de código. El hecho de estar definida como clase estática nos
asegura que cuando estemos generando los scripts de validación, todas las validaciones se
estarán realizando sobre el mismo Motor de Base de datos y además que la detección de
palabras reservadas se realizara de forma eficiente puesto que las palabras reservadas se
cargaran en la estructura de memoria interna de la clase validación una única vez, en su
constructor estático por lo que no necesitaremos entrar a leer a fichero tantas veces como
instancias de validación tengamos.
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
190
Generación de código
Una vez explicado todo lo necesario para poder entender la API de generación de
código que hemos implementado, vamos a explicar lo necesario para entender los scripts
de generación de código que hacen uso de ella.
Vamos a comenzar poniendo la descripción del lenguaje de programación de los
scripts, para luego explicar detenidamente y mas en profundidad su funcionamiento.
La generación de código mediante DSL Tools se realiza mediante scripts que son
procesados por el espacio de nombres Microsoft.VisualStudio.TextTemplating propio de
Visual Studio 2005 y que viene con el kit de Visual Studio 2005 SDK.
Tenemos que ver estos scripts como algo que se procesa de forma parecida a los
archivos .aspx de asp.net en los cuales hay una dll que se encarga de producir código html
a partir de los archivos .aspx y codebehind( .cs, .vb,…) que contenga. Esto quiere decir que
el objeto TextTemplatingFileGenerator propio del espacio de nombres citado, tomará el
siguiente código y generará el código de la forma en la que nosotros le digamos, y esto se
lo hemos dicho mediante el API de generación de código que hemos realizado para tal
ocasión.
Primeramente, lo que tenemos que hacer es añadir un nuevo ítem en el proyecto:
Proyect-> Add new ítem -> Template -> Text File. Una vez tengamos el archivo de texto
que va a ser nuestra futura plantilla o script de generación de código, lo renombramos
como queramos, por ejemplo "sqlPlantilla.ReportTemplate"
En la ventana de propiedades, hemos de poner en la celda de "Custom tool" la
palabra: "TextTemplatingFileGenerator", para que el generador de plantillas, utilice esta
plantilla para generar código.
Podemos tener tantas plantillas como queramos.
TAGS PARA LAS PLANTILLAS
Directivas:
Tienen la sintaxis <#@ directiva #> y a continuación pasaremos a describir las
utilizadas
o Directiva output extension: <#@ output extension=".sql" #>
Con esta directiva nos aseguramos que la plantilla, va a generar un archivo
nombrePlantilla.sql con el código generado.
o Directiva include: <#@ include
file="c:\test.txt" #>
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
191
Mediante la directiva include, podemos incluir en la generación de código,
una serie de información externa como copyright, encabezados, pies de pagina...
o Directivas de depuración:
<#@ template debug="true" #>
<#@ import namespace="System.Diagnostics" #>
Con estas dos directivas podemos hacer depuración en la generación del
código, justo mientras se encuentra el TextTemplatingFileGenerator en ejecución,
Para poner breakpoints, basta con poner la siguiente sentencia de código en
el script donde queramos:
<# Debugger.Break(); #>
Sentencias de Código:
Las sentencias de código tienen la siguiente sintaxis: <# SentenciaCodigo #>.
Estas sentencias se tienen que escribir en el lenguaje que hayamos decidido utilizar
para programarlas (por defecto C#) y han de compilarse correctamente, por lo que no
podemos cometer errores de sintaxis, como es de suponer ya que se compilaran antes de su
ejecución.
Bloques Expression:
Los bloques Expression tienen la sintaxis <#= Expression #> y sirven para añadir Strings
al código generado en función de variables y/o propiedades calculadas.
Por ejemplo, esto seria un trozo de código con una sentencia de código que no se
verá y un bloque Expression que si que se verá reflejado en el fichero de código que
generemos.
<#int result = 2*9#>
<#= result #>
Lo que hará esto será ejecutar el 2*9 mientras se encuentra el parser analizando,
compilando y ejecutando nuestro template y escribir el valor de result al fichero de
generación de código. Es decir que en el fichero generado obtendríamos "18" solamente.
Caracteres especiales en generación de código
Para que en nuestros templates de generación de código se nos generen caracteres
especiales como las comillas " o el signo menor que "<" (por ejemplo), hemos de hacer tal
y como hacíamos al programar en C/C++.Es decir con el carácter especial "\".
De esta forma, podremos hacer lo siguiente:
<# string texto = "\"Texto entre comillas"\" #>
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
192
<#= texto #>
Con lo que se nos generara: "Texto entre comillas" (con comillas incluidas) en el
fichero de salida
Crear funciones para el parser
También llamados "ClassFeatures", con su sintaxis <#+ ClassFeature #>, nos
permiten definirnos funciones para que se ejecuten en el parser antes de generar el código
y podamos hacer cambios en el mismo.
Un típico ejemplo es el de el nombre de una tabla de sql. Obviando características
especiales de SQL Server como poner los nombres entre corchetes; si hacemos "create
table Tabla 3", nos dará un error porque no sabe que el nombre de la tabla tiene un espacio.
Pues bien, podemos hacer para que o bien nos genere los corchetes ( quedando "create
table [Tabla 3]") o que nos quite el/los espacio/s en blanco (quedando "create Table
Tabla3").
Optando por la primera forma porque de paso veremos como utilizar la directiva
import y el uso de clases externas, lo que habría que hacer es lo siguiente:
1) Importar el namespace System.Text.RegularExpressions
Esto se hace añadiendo al principio del template la siguiente directiva:
<#@ import namespace = "System.Text.RegularExpressions" #>
2) Definirnos en cualquier parte la ClassFeature siguiente:
<#+
private string FixWhiteSpaces(string typeName)
{
//Usa el namespace System.Text.RegularExpressions importado antes
string checkName = Regex.Replace(typeName," ","");
return(checkName.ToString());
}
#>
3) Ahora solo falta usarlo, y se usa como cualquier función, pero desde
dentro de un bloque de sentencias de código <#...#>. Por ejemplo, este código es
válido.
<#
foreach(Table table in this.Schema.tables)
{
//Como vemos, cojemos el nombre de la tabla del diagrama
string nombreTabla = table.Name;
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
193
// Si el nombre de la columna contiene espacios en blanco, se los quito
nombreTabla = FixWhiteSpaces(nombreTabla);
#>
Create Table <#=nombreTabla#>
(
)
<#
//Cierro el bucle foreach
}
#>
Esto creara ficheros como el siguiente a partir de un diagrama con dos objetos table
que tengan nombres “Tabla 1” y “Ni NombreTa b l a” que como se puede apreciar, tienen
espacios en blanco:
Create Table Tabla1 (
)
Create Table NiNombreTabla (
)
Comentarios en los Templates
Para poner comentarios dentro de las plantillas de generación de código, es tan
simple como recordar que las Sentencias <#...#> son para delimitar código compilado en
.net y por tanto si creamos una directiva en la cual lo único que tenemos es un comentario,
esta no se vera representada en el fichero generado.
Aquí tenemos un ejemplo de comentario.
<# // Soy un comentario y no voy a verme reflejado en el fichero de salida
#>
El problema viene porque al estar en un estado temprano, el parser de DSLTools
tiene problemas con los comentarios y cree que hay Sentencias donde no las hay.
Por ejemplo:
<# /* Esto lo he comentado porque no consigo depurarlo
<#@ template debug="true" #>
<#@ import namespace="System.Diagnostics" #>
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
194
*/
#>
Aunque es sintacticamente correcto, nos da error el compilador porque el parser del
template se lía con los #> y cree que hemos cerrado la Sentencia cuando realmente tendría
que pasar de largo porque esta dentro de un comentario. Esto hace que lo que le de al
compilador de .net sea un código no valido y por eso nos saca warnings y errores.
La solución es obvia, para salir del paso le podemos decir que son caracteres
especiales; al estar dentro de un comentario no hará nada.
<# /* Esto lo he comentado porque no consigo depurarlo
<\#@ template debug="true" \#>
<\#@ import namespace="System.Diagnostics" \#>
*/
#>
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
195
Generación de código SQL Estrella
A continuación vamos a proceder con la explicación del script de generación SQL Estrella. En la parte izquierda hemos puesto una
serie de comentarios para ayudar aún más a la comprensión del mismo y hemos tenido que realizar algunas separaciones entre bloques de
código mas exageradas para que se puedan seguir los comentarios con mejor facilidad. Esto quiere decir que aunque el script que veamos sea
diferente en cuanto a tabulacion y distancia entre bloques de código y separación entre zonas de código, el script será el mismo que el que se
está usando para generar código, simplemente se ha retocado un poco para mejor comprensión.
Con esto le decimos que es un script a procesar por los objetos de transformación de texto de visual Studio; El poner la marca debug=”true” es para activar la depuración de los templates si se necesita. ---------------------------------------- Importamos los espacios de nombres que se van a usar internamente mediante sentencias <#@ import #> System.Diagnostics es necesario para poder depurar
<#@ template inherits = "Microsoft.VisualStudio.TextTemplating.VSHost.ModelingTextTransformation"
debug="true" #>
<#
// Necesario para poder realizar Breakpoints
#>
<#@ import namespace = "System.Diagnostics" #>
<#
// Este import es para poder usar la clase Regex para cambiar los nombres con espacio a sin espacio en blanco
#>
<#@ import namespace = "System.Text.RegularExpressions" #>
<#
// Ahora hago import del namespace para utilizar las colas FIFO que voy a usar para el algoritmo DFS
#>
<#@ import namespace = "System.Collections" #>
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
196
estos scripts mediante sentencias Debugger.Break(); ---------------------------------------- RegularExpressions es útil porque la clase validación utiliza expresiones regulares para la detección de caracteres extraños en el nombre de objetos de la BBDD y poder validarlos. --------------------------------------- Con la sentencia <#@ output #> lo que decimos es la extensión del archivo donde irá la salida del script que se va a procesar. ---------------------------------------- $_FICHERO_ENTRADA_$
<#
// Extension del fichero de salida que generará el template
#>
<#@ output extension=".sql" #>
<#
// Aqui es donde especificamos los archivos que va a leer este template y a partir de los cuales va a generar
código.
#>
<#@ Esquema processor="ObjectOrientedMultidimensionalModelDirectiveProcessor"
requires="fileName='$_FICHERO_ENTRADA_$'" provides="Esquema=Esquema" #>
<#
// Incluimos una cabecera que muestra información para que sepamos que se trata de codigo autogenerado.
#>
<#@ include file="cabecera.comentarios" #>
<#
// Incluyo el archivo con las clases de apoyo que uso para generar código
// Las separo del bucle principal para que quede limpio
#>
<#@ include file="RecursosApoyoGeneracionCodigo.t4" #>
<#@ include file="ClaseValidacion.t4" #>
<#@ include file="ClaseTTabla.t4" #>
<#@ include file="ClaseTColumna.t4" #>
<#@ include file="ClaseTClaveAjena.t4" #>
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
197
será el fichero .oomm del diseño realizado por el usuario y del que obtendremos el código. Se obtiene al hacer clic derecho sobre el diagrama y se realiza de forma transparente al usuario. ---------------------------------------- Con la sentencia <#@ include #> hacemos que se incluya el código del fichero que se indica, como si se hubiera escrito directamente aquí. Es como una substitución de macros en c, donde cuando encuentra esta sentencia, copia todo el texto del fichero indicado en el template, para procesarlo. Los ficheros incluidos pueden ser código de salida como el caso de cabecera.comentarios, que va a ser la cabecera de los
<#@ include file="ClaseTTablaMultidimensional.t4" #>
<#
/// Declaracion de variables que se van a usar
TTabla tablaFact;
TTabla tablaDimension;
TTabla tablaDegenerateFact;
/// Especificacion del motor de Base de Datos para el que va a ir destinado el script.
/// Por ahora: MotorBBDD.ORACLE
/// MotorBBDD.SQLServer
TTabla.MotorBBDDSalida = $_MOTOR_BASE_DATOS_$;
#>
-------------------------------------------------------------------------------------------------------
--
-- Script SQL Estrella autogenerado para el Motor de Base de Datos: <#=TTabla.StringMotorBBDD#>
--
-------------------------------------------------------------------------------------------------------
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
198
ficheros autogenerados, como código c#. En el caso de los ficheros .t4, es donde hemos programado la API de generación de código, que por supuesto al procesarse este template, se va a compilar con el compilador de c#2.0 ( mcs ) automáticamente, avisándonos y deteniendo el script en el caso de que el código que en ellos exista no compile. ---------------------------------------- Único lugar donde especificaremos el motor de BBDD destino. La variable
<#
// B U C L E P R I N C I P A L .
// Recuerda que para depurar has de poner un Debugger.Break();
//
foreach(Clase clase in this.Esquema.classRole)
{
if (clase is Fact)
{
Fact fact = (Fact)clase;
/// Creamos la TTabla tablaFact, generamos su clave primaria y sus columnas.
/// No podemos generar aun su sentencia create table, puesto que aun faltan las claves ajenas a las
dimensiones restantes y si lo hicieramos, el script fallaria al estar haciendo claves ajenas a tablas que
todavia no se han creado.
tablaFact = new TTabla(fact.Name,fact.GetType());
tablaFact.GenerarClavePrimaria(fact);
tablaFact.ObtenerColumnasRestantes(fact);
/// Estoy dentro de un "Fact" y voy a recorrer todas las dimensiones del "Fact"
foreach( Dimension dim in fact.dimension)
{
tablaDimension = new TTabla(dim.Name,dim.GetType());
tablaDimension.GenerarClavePrimaria(dim);
// Generamos las columnas que pertenecen a la tabla.
// Como es sql estrella, obtendremos las tablas desnormalizadas por la dimension que se le pase.
tablaDimension.ObtenerDimensionDesnormalizada(dim);
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
199
$_MOTOR_BASE_DATOS_$ es tomada en tiempo de ejecución de forma transparente cuando nos pregunta si queremos generar código para SQL Server o para Oracle en el formulario que nos sale al pinchar con el botón derecho sobre el diagrama. ---------------------------------------- Comentario que muestra el comienzo del código autogenerado y nos dice el motor de base de datos para el que este script va destinado. ----------------------------------------
/// Solo si no existe un DegenerateFact entre la dimension y la clase Fact, tendran clave ajena
desde la clase Fact a la Dimension.
if (!tablaFact.ExisteDegenerateFact(dim,fact))
{
// Una vez ya tenemos la tabla Dimension guardada como una estructura TTabla, podemos
añadirla a la cola de tablas ajenas de la clase Fact
tablaFact.AddTablaAjena(tablaDimension);
}
#>
<#=tablaDimension.GenerarSentenciaCreateTable()#>
<#
} // foreach( Dimension dim in fact.dimension)
// UNA VEZ CREADAS LAS TABLAS DE LAS DIMENSIONES, AHORA FALTA LA TABLA FACT
#>
<#=tablaFact.GenerarSentenciaCreateTable()#>
--
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
200
Aquí comienza el bucle principal, que empieza recorriendo con un bucle foreach todas y cada una de las clases de tipo Fact (vemos que recorre todas las clases pero solo entra en el if para las clases de tipo Fact) ---------------------------------------- ---------------------------------------- Ahora es cuando recorremos todas las dimensiones de un Fact con este bucle foreach. ----------------------------------------
-- Ahora van las asociaciones de DegenerateFact.
--
<#
// Ahora voy a crear las tablas formadas por las DegenerateFact.
foreach(DegenerateFact df in fact.factClassAssociatesDegenerateFact)
{
tablaDegenerateFact = new TTabla(df.Name,df.GetType());
tablaDegenerateFact.GenerarClavePrimaria(df);
tablaDegenerateFact.ObtenerColumnasRestantes(df);
#>
<#=tablaDegenerateFact.GenerarSentenciaCreateTable()#>
<#
}// foreach(DegenerateFact...
#>
---------------------------------------------------------------------
-- --
-- S E P A R A D O R D E D I A G R A M A S E S T R E L L A --
-- --
---------------------------------------------------------------------
<#
} // if (clase is Fact)
} // foreach(Clase clase in this.Esquema.classRole) <- B U C L E P R I N C I P A L
#>
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
201
Primero instanciamos la clase tablaDimension ---------------------------------------- Segundo comprobamos si hay DegenerateFact entre ella y la clase Fact, añadiéndola a la cola de tablas ajenas de la clase tablaFact ---------------------------------------- Por último, escribimos a fichero ( el .sql de salida ) el código necesario para crear la tabla de la dimensión que estamos analizando ahora. Esta tabla será de acuerdo al
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
202
código SQL Estrella por lo que será una dimensión desnormalizada. ---------------------------------------- Ahora , puesto que ya tenemos todas las sentencias de create table de las dimensiones escritas en el script, ya podemos generar la sentencia de create table de la tabla fact, que como tiene claves ajenas a las tablas dimensión, hemos de tenerlas creadas antes que la tabla fact para que no fallen las sentencias alter table…foreign key … ---------------------------------------- Para finalizar el diagrama estrella que parte de la clase Fact en la que estamos, faltan
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
203
por último las tablas que originan los DegenerateFact. Esto es así porque estas tablas tienen claves ajenas a dimensiones y a la propia clase Fact, por lo que han de ser incluidas al final del código para evitar problemas con las claves ajenas ---------------------------------------- Ahora para finalizar, ponemos un mensaje de separador de diagramas estrella, porque podemos tener varias clases fact en el mismo diagrama con lo que a continuación del primero, iría el segundo , quedando claramente diferenciados los diagramas y el código resultante mediante este separador
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
204
Generación de código SQL Snowflake
A continuación vamos a detallar mas en profundidad el template de generación de código para SQL Snowflake. Al igual que en el caso
anterior, el script es el mismo que el que esta siendo usado para generar código, solo que están mas abultadas las distancias entre bloques de
código para una mejor comprensión del template.
Dado que el API de generación de código ha sido pensado para facilitar la tarea de programación de este tipo de templates, vamos a
ver que el script es EXACTAMENTE IGUAL que el anterior, salvo que existe un if para distinguir la situación de dimensión normalizada o
no.
Con esto le decimos que es un script a procesar por los objetos de transformación de texto de visual Studio; El poner la marca debug=”true” es para activar la depuración de los templates si se necesita. ---------------------------------------- Importamos los espacios de nombres que se van a usar internamente mediante sentencias <#@ import #> System.Diagnostics es
<#@ template inherits = "Microsoft.VisualStudio.TextTemplating.VSHost.ModelingTextTransformation"
debug="true" #>
<#
// Necesario para poder realizar Breakpoint
#>
<#@ import namespace = "System.Diagnostics" #>
<#
// Este import es para poder usar la clase Regex para cambiar los nombres con espacio a sin espacio
en blanco
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
205
necesario para poder depurar estos scripts mediante sentencias Debugger.Break(); ---------------------------------------- RegularExpressions es útil porque la clase validación utiliza expresiones regulares para la detección de caracteres extraños en el nombre de objetos de la BBDD y poder validarlos. --------------------------------------- Con la sentencia <#@ output #> lo que decimos es la extensión del archivo donde irá la salida del script que se va a procesar. ----------------------------------------
#>
<#@ import namespace = "System.Text.RegularExpressions" #>
<#
// Ahora hago import del namespace para utilizar las colas FIFO que voy a usar para el algoritmo DFS
#>
<#@ import namespace = "System.Collections" #>
<#
// Extension del fichero de salida que generará el template
#>
<#@ output extension=".sql" #>
<#
// Aqui es donde especificamos los archivos que va a leer este template y a partir de los cuales va a
generar código.
#>
<#@ Esquema processor="ObjectOrientedMultidimensionalModelDirectiveProcessor"
requires="fileName='$_FICHERO_ENTRADA_$'" provides="Esquema=Esquema" #>
<#
// Incluimos una cabecera que muestra información para que sepamos que se trata de codigo
autogenerado.
#>
<#@ include file="cabecera.comentarios" #>
<#
// Incluyo el archivo con las clases de apoyo que uso para generar código
// Las separo del bucle principal para que quede mas limpio
#>
<#@ include file="RecursosApoyoGeneracionCodigo.t4" #>
<#@ include file="ClaseValidacion.t4" #>
<#@ include file="ClaseTTabla.t4" #>
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
206
$_FICHERO_ENTRADA_$ será el fichero .oomm del diseño realizado por el usuario y del que obtendremos el código. Se obtiene al hacer clic derecho sobre el diagrama y se realiza de forma transparente al usuario. ---------------------------------------- Con la sentencia <#@ include #> hacemos que se incluya el código del fichero que se indica, como si se hubiera escrito directamente aquí. Es como una substitución de macros en c, donde cuando encuentra esta sentencia, copia todo el texto del fichero indicado en el template, para procesarlo. Los ficheros incluidos pueden ser código de salida como el caso de cabecera.comentarios, que va a ser la cabecera de los
<#@ include file="ClaseTColumna.t4" #>
<#@ include file="ClaseTClaveAjena.t4" #>
<#@ include file="ClaseTTablaMultidimensional.t4" #>
<#
/// Declaracion de variables que se van a usar
TTabla tablaFact;
TTabla tablaDimension;
TTabla tablaDegenerateFact;
Queue tablasBase=null;
/// Especificacion del motor de Base de Datos para el que va a ir destinado el script.
/// Por ahora: MotorBBDD.ORACLE
/// MotorBBDD.SQLServer
TTabla.MotorBBDDSalida = $_MOTOR_BASE_DATOS_$;
#>
-----------------------------------------------------------------------------------------------------
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
207
ficheros autogenerados, como código c#. En el caso de los ficheros .t4, es donde hemos programado la API de generación de código, que por supuesto al procesarse este template, se va a compilar con el compilador de c#2.0 ( mcs ) automáticamente, avisándonos y deteniendo el script en el caso de que el código que en ellos exista no compile. ---------------------------------------- Único lugar donde especificaremos el motor de BBDD destino. La variable $_MOTOR_BASE_DATOS_$ es
--
--
-- Script SQL Snowflake autogenerado para el Motor de Base de Datos:
<#=TTabla.StringMotorBBDD#>
--
-----------------------------------------------------------------------------------------------------
--
<#
foreach(Clase clase in this.Esquema.classRole)
{
if (clase is Fact)
{
Fact fact = (Fact)clase;
/// Creamos la TTabla tablaFact, generamos su clave primaria y sus columnas.
/// No podemos generar aun su sentencia create table, puesto que aun faltan las claves
ajenas a las dimensiones restantes y si lo hicieramos, el script fallaria al estar haciendo claves
ajenas a tablas que todavia no se han creado.
tablaFact = new TTabla(fact.Name,fact.GetType());
tablaFact.GenerarClavePrimaria(fact);
tablaFact.ObtenerColumnasRestantes(fact);
/// Estoy dentro de un "Fact" y voy a recorrer todas las dimensiones del "Fact"
foreach( Dimension dim in fact.dimension)
{
tablaDimension = new TTabla(dim.Name,dim.GetType());
tablaDimension.GenerarClavePrimaria(dim);
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
208
tomada en tiempo de ejecución de forma transparente cuando nos pregunta si queremos generar código para SQL Server o para Oracle en el formulario que nos sale al pinchar con el botón derecho sobre el diagrama. ---------------------------------------- Comentario que muestra el comienzo del código autogenerado y nos dice el motor de base de datos para el que este script va destinado. ---------------------------------------- Aquí comienza el bucle principal, que empieza
///Caso de las dimensiones normalizadas
if($_CONDICION_DIMS_NORMALIZADAS_$)
{
// Generamos las columnas que pertenecen a la tabla.
tablasBase = tablaDimension.ObtenerDimensionNormalizada(dim);
if (tablasBase != null)
foreach(TTabla t in tablasBase)
{
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
209
recorriendo con un bucle foreach todas y cada una de las clases de tipo Fact (vemos que recorre todas las clases pero solo entra en el if para las clases de tipo Fact) ---------------------------------------- ---------------------------------------- Ahora es cuando recorremos todas las dimensiones de un Fact con este bucle foreach. ---------------------------------------- Primero instanciamos la clase
// Imprimo la sentencia create table, pero sin las claves ajenas,
para que no de error
#>
<#=t.GenerarSentenciaCreateTableSinClavesAjenas()#>
<#
} // (TTabla t in tablasBase)
}
else
{
tablaDimension.ObtenerDimensionDesnormalizada(dim);
}
/// Solo si no existe un DegenerateFact entre la dimension y la clase Fact, tendran
clave ajena desde la clase Fact a la Dimension.
if (!tablaFact.ExisteDegenerateFact(dim,fact))
{
// Una vez ya tenemos la tabla Dimension guardada como una estructura TTabla,
podemos añadirla a la cola de tablas ajenas de la clase Fact
tablaFact.AddTablaAjena(tablaDimension);
}
#>
<#=tablaDimension.GenerarSentenciaCreateTable()#>
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
210
tablaDimension ---------------------------------------- Segundo comprobamos si queremos normalizar la dimensión en cuestión. Esta condición se toma en base a los formularios que se piden por pantalla cuando pinchamos en generar SQL Snowflake desde el entorno de diseño. ---------------------------------------- Si queremos normalizar la dimensión, se llama al método de Normalización de la clase TTabla que nos devuelve por un lado en tablasBase, una cola de TTablas ya rellenadas y listas para generar código con todas las tablas Base que tiene
<#
////Caso de las dimensiones normalizadas
if ( (tablasBase != null) && ($_CONDICION_DIMS_NORMALIZADAS_$))
foreach(TTabla t in tablasBase)
{
// Imprimo la sentencia create table, pero sin las claves ajenas, para que
no de error
#>
<#=t.GenerarSentenciaAlterTableForeignKey()#>
<#
} // (TTabla t in tablasBase)
} // foreach( Dimension dim in fact.dimension)
// UNA VEZ CREADAS LAS TABLAS DE LAS DIMENSIONES, AHORA FALTA LA TABLA FACT
#>
<#=tablaFact.GenerarSentenciaCreateTable()#>
--
-- Ahora van las asociaciones de DegenerateFact.
--
<#
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
211
por debajo la dimensión que estamos tratando; y por otro lado nos rellena la estructura TTabla de la instancia tablaDimension en la que estamos. ---------------------------------------- Ahora lo que vamos haciendo es recorrer todas y cada una de las TTabla de tablasBase e imprimiendo las sentencias de creación de dichas tablas al fichero de salida, pero esto se hace sin las claves ajenas para que no tengamos problemas de dependencias entre ellas puesto que aún no tenemos la tabla dimensión creada. ----------------------------------------
// Ahora voy a crear las tablas formadas por las DegenerateFact.
foreach(DegenerateFact df in fact.factClassAssociatesDegenerateFact)
{
tablaDegenerateFact = new TTabla(df.Name,df.GetType());
tablaDegenerateFact.GenerarClavePrimaria(df);
tablaDegenerateFact.ObtenerColumnasRestantes(df);
#>
<#=tablaDegenerateFact.GenerarSentenciaCreateTable()#>
<#
}// foreach(DegenerateFact...
#>
---------------------------------------------------------------------
-- --
-- S E P A R A D O R D E D I A G R A M A S --
-- --
---------------------------------------------------------------------
<#
} // if (clase is Fact)
} // foreach(Clase clase in this.Esquema.classRole) <- B U C L E P R I N C I P A L
#>
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
212
Si por el contrario, la dimensión que estamos analizando no queremos normalizarla, llamaremos al método de desnormalizacion de la clase TTabla y obtendremos la instancia tablaDimension, como una TTabla desnormalizada y lista para imprimir a fichero. ---------------------------------------- Ahora comprobamos si hay DegenerateFact entre ella y la clase Fact, añadiéndola a la cola de tablas ajenas de la clase tablaFact ----------------------------------------
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
213
Por último, escribimos a fichero ( el .sql de salida ) el código necesario para crear la tabla de la dimensión que estamos analizando ahora. ---------------------------------------- Una vez realizado todo lo anterior solo faltan las claves ajenas de las tablas normalizadas de tablasBase en el caso de que se haya normalizado dicha dimensión ya que sino, no haría falta. ---------------------------------------- Ahora , puesto que ya tenemos
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
214
todas las sentencias de create table de las dimensiones escritas en el script, ya podemos generar la sentencia de create table de la tabla fact, que como tiene claves ajenas a las tablas dimensión, hemos de tenerlas creadas antes que la tabla fact para que no fallen las sentencias alter table…foreign key … ---------------------------------------- Para finalizar el diagrama estrella que parte de la clase Fact en la que estamos, faltan por último las tablas que originan los DegenerateFact. Esto es así porque estas tablas tienen claves ajenas a dimensiones y a la propia clase Fact, por lo que han de ser incluidas al final del código para evitar problemas con las claves ajenas
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
215
---------------------------------------- Ahora para finalizar, ponemos un mensaje de separador de diagramas estrella, porque podemos tener varias clases fact en el mismo diagrama con lo que a continuación del primero, iría el segundo , quedando claramente diferenciados los diagramas y el código resultante mediante este separador
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
216
Generación de código Oracle Warehouse Builder
Ahora es el turno de la plantilla de generación de código para Oracle Multidimensional usando Oracle Warehouse Builder. En este
caso volvemos a tener un script muy similar a los anteriores, salvo que ahora se utilizan objetos TTablaMultidimensional que internamente
funcionan específicamente para Bases de datos multidimensionales.
En este caso no vamos a volver a explicar porciones de código que ya han sido explicadas en las dos plantillas anteriores y vamos a ir
directamente a explicar los bloques de código puramente importantes de esta plantilla.
<#@ template inherits = "Microsoft.VisualStudio.TextTemplating.VSHost.ModelingTextTransformation"
debug="true" #>
<#
// Necesario para poder realizar Breakpoint
#>
<#@ import namespace = "System.Diagnostics" #>
<#
// Este import es para poder usar la clase Regex para cambiar los nombres con espacio a sin espacio
en blanco
#>
<#@ import namespace = "System.Text.RegularExpressions" #>
<#
// Ahora hago import del namespace para utilizar las colas FIFO que voy a usar para el algoritmo DFS,
propiedades de las TTabla,...
#>
<#@ import namespace = "System.Collections" #>
<#
// Extension del fichero de salida que generará el template
#>
<#@ output extension=".sql" #>
<#
// Aqui es donde especificamos los archivos que va a leer este template y a partir de los cuales va a
generar código.
#>
<#@ Esquema processor="ObjectOrientedMultidimensionalModelDirectiveProcessor"
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
217
requires="fileName='$_FICHERO_ENTRADA_$'" provides="Esquema=Esquema" #>
<#
// Incluimos una cabecera que muestra información para que sepamos que se trata de codigo
autogenerado.
#>
<#@ include file="cabecera.comentarios" #>
<#
// Incluyo el archivo con las clases de apoyo que uso para generar código
// Las separo del bucle principal para que quede algo limpio
#>
<#@ include file="RecursosApoyoGeneracionCodigo.t4" #>
<#@ include file="ClaseValidacion.t4" #>
<#@ include file="ClaseTTabla.t4" #>
<#@ include file="ClaseTColumna.t4" #>
<#@ include file="ClaseTClaveAjena.t4" #>
<#@ include file="ClaseTTablaMultidimensional.t4" #>
<#
/// Declaracion de variables que se van a usar
TTabla tablaFact;
TTablaMultidimensional tablaDimension;
TTabla tablaDegenerateFact;
/// Especificacion del motor de Base de Datos para el que va a ir destinado el script.
/// Por ahora: MotorBBDD.ORACLE
/// MotorBBDD.SQLServer
TTabla.MotorBBDDSalida = $_MOTOR_BASE_DATOS_$;
#>
-----------------------------------------------------------------------------------------------------
--
--
-- Script SQL Multidimensional autogenerado para el Motor de Base de Datos:
<#=TTabla.StringMotorBBDD#>
--
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
218
---------------------------------------- En este caso vamos a generar oracle multidimensional, es por ello que para las dimensiones vamos a utilizar TTablaMultidimensional
-----------------------------------------------------------------------------------------------------
--
<#
foreach(Clase clase in this.Esquema.classRole)
{
if (clase is Fact)
{
Fact fact = (Fact)clase;
/// Creamos la TTabla tablaFact, generamos su clave primaria y sus columnas.
/// No podemos generar aun su sentencia create table, puesto que aun faltan las claves
ajenas a las dimensiones restantes y si lo hicieramos, el script fallaria al estar haciendo claves
ajenas a tablas que todavia no se han creado.
tablaFact = new TTabla(fact.Name,fact.GetType());
tablaFact.GenerarClavePrimaria(fact);
tablaFact.ObtenerColumnasRestantes(fact);
/// Estoy dentro de un "Fact" y voy a recorrer todas las dimensiones del "Fact"
foreach( Dimension dim in fact.dimension)
{
tablaDimension = new TTablaMultidimensional(dim.Name,dim.GetType());
tablaDimension.GenerarClavePrimaria(dim);
// Generamos las columnas que pertenecen a la tabla.
// Como es sql estrella, obtendremos las tablas desnormalizadas por la dimension que se
le pase.
tablaDimension.ObtenerDimensionDesnormalizada(dim);
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
219
/// Solo si no existe un DegenerateFact entre la dimension y la clase Fact, tendran
clave ajena desde la clase Fact a la Dimension.
if (!tablaFact.ExisteDegenerateFact(dim,fact))
{
// Una vez ya tenemos la tabla Dimension guardada como una estructura TTabla,
podemos añadirla a la cola de tablas ajenas de la clase Fact
tablaFact.AddTablaAjena(tablaDimension);
}
#>
<#=tablaDimension.GenerarSentenciaCreateTable()#>
<#=tablaDimension.GenerarSentenciaCreateDimension() #>
<#
} // foreach( Dimension dim in fact.dimension)
// UNA VEZ CREADAS LAS TABLAS DE LAS DIMENSIONES, AHORA FALTA LA TABLA FACT
#>
<#=tablaFact.GenerarSentenciaCreateTable()#>
--
-- Ahora van las asociaciones de DegenerateFact.
--
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
220
---------------------------------------- Aquí es donde instanciamos la tablaDimension como TTablaMultidimensional y generamos su clave primaria, que en realidad no va a obtener claves primarias, sino claves con el modificador unique ---------------------------------------- Puesto que vamos a hacer multidimensional, las dimensiones van a ser desnormalizadas y sobre ellas vamos a obtener los create dimensión ----------------------------------------
<#
// Ahora voy a crear las tablas formadas por las DegenerateFact.
foreach(DegenerateFact df in fact.factClassAssociatesDegenerateFact)
{
tablaDegenerateFact = new TTabla(df.Name,df.GetType());
tablaDegenerateFact.GenerarClavePrimaria(df);
tablaDegenerateFact.ObtenerColumnasRestantes(df);
#>
<#=tablaDegenerateFact.GenerarSentenciaCreateTable()#>
<#
}// foreach(DegenerateFact...
#>
---------------------------------------------------------------------
-- --
-- S E P A R A D O R D E D I A G R A M A S --
-- --
---------------------------------------------------------------------
<#
} // if (clase is Fact)
} // foreach(Clase clase in this.Esquema.classRole) <- B U C L E P R I N C I P A L
#>
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
221
---------------------------------------- Ahora escribimos a fichero la sentencia de create table de la dimensión, así como su sentencia de CREATE DIMENSION. ----------------------------------------
Lenguaje de Especificación del dominio para un Modelo Multidimensional Orientado a Objetos Proyecto de fin de carrera de Enrique Catalá Bañuls y Vicente Soriano Claver
Año académico 2005-2006
222
Una vez ya tenemos todas las dimensiones generadas, generamos la sentencia create table de la tabla Fact ----------------------------------------
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
223
Detector de palabras reservadas
La detección de palabras reservadas la hemos clasificado como una parte de la
validación de nombres de objetos en los motores de base de datos y por ello lo hemos
introducido como parte fundamental del modelo de validación. Esto quiere decir que
cuando validamos nombres de objetos mediante la clase estática “Validacion”, no solo
validamos que el nombre de objeto sea correcto, sino que validamos que no sea un nombre
de objeto reservado por el motor de base de datos, evitando así que los scripts fallen al ser
lanzados.
El proceso de detección de palabras reservadas se realiza en 2 pasos:
1. El primero se realiza en el constructor de la clase Validacion, donde
iremos leyendo el fichero de PalabrasReservadas.txt de forma que será guardado
como estructura Queue ( cola fifo ) estática para consulta en tiempo de ejecución.
2. El segundo se realiza en los métodos de ValidateStringSQLServer
o ValidateStringORACLE ( de momento solo se soportan esos dos motores )
donde realmente se consulta dicha estructura FIFO para detectar si el nombre de
objeto en cuestión es una palabra reservada y actuar en consecuencia. Esto se
realiza mediante el método Contains de la clase Queue que nos dice si un
determinado objeto pertenece a la cola FIFO.
private static string ValidateStringANSI(string cadena)
{
string retorno = "";
string expresionRegular = @"(\s|-)";
Regex regex = new Regex(expresionRegular);
if (regex.IsMatch(cadena) ||
palabrasReservadas.Contains(cadena.ToUpper()))
{
…
…
…
}
}
Si quisiéramos añadir palabras reservadas de otros motores de bases de datos
distintos tendríamos que seguir estos dos únicos pasos:
1. En el primero lo único que tenemos que hacer es identificar aquellos
nombres de objetos ( palabras reservadas ) del motor de base de datos que no
podemos utilizar para definirnos nuevos objetos. Para ello no tendremos mas
remedio que irnos a los Books On Line o ayudas del motor de base de datos en
cuestión y quedarnos con los nombres de objetos reservados.
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
224
2. En el segundo paso lo que tendremos que hacer es añadirlos al
fichero PalabrasReservadas.txt que tendremos añadido como recurso en el proyecto
ObjectOrientedMultidimensionalModel y añadir aquellas palabras en una nueva
línea.
Hemos de añadir que el fichero PalabrasReservadas.txt que podemos encontrar
como recurso dentro del proyecto ObjectOrientedMultidimensionalModel y que es la base
del proceso de detección de palabras reservadas de nuestra aplicación, detecta el 100% de
las palabras reservadas de SQL Server 2005.
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
225
Abstracción de tipos de datos
La abstracción de tipos de datos consiste en aplicar la especificación CWM 1.1 en
la cual mediante la selección de una serie de tipos de datos simples de cara al usuario, los
tipos de datos se adapten al modelo de base de datos destino para el que se ejecutaran los
scripts. De esta forma podemos especificar tipos de datos genéricos que se adapten mejor a
la base de datos destino.
La selección de los tipos de datos se realiza desde el propio IDE del diseñador
gráfico y se exportan 2 zonas claramente diferenciadas para el diseñador.
En la primera se especifica claramente el tipo de datos que deseamos almacenar en
esa futura columna de una tabla de la Base de datos y en la otra se especificaran aquellas
restricciones inherentes a la elección, de forma que si queremos por ejemplo almacenar un
tipo decimal con 3 posiciones a la derecha de la coma, podremos hacerlo fácilmente.
De forma visual, aquí podemos ver los lugares donde especificamos dicha
información.
En un primer momento especificamos en la propiedad “dataType”, el tipo de datos
destino que queremos seleccionar entre la lista que aparece en la figura:
Donde podemos ver y en este orden:
o Booleano
o Carácter fijo ( normalmente char(x) )
o Carácter variable ( normalmente varchar(x) )
o Fechas
o Decimales
o Punto flotante
o Enteros
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
226
o Reales
Mas tarde, y una vez seleccionado el tipo de datos que queremos, podemos añadirle
las restricciones que consideremos oportunas en la propiedad “Tam”. Hemos de destacar
que estas restricciones no son obligatorias y por tanto son solo necesarias en casos
excepcionales donde requiramos más precisión.
En este caso podemos ver como hemos optado por un tipo de datos de punto
flotante con una precisión 11,3 que queda especificada tal y como se haría en la Base de
datos destino diciendo que queremos una precisión de 11 números, 3 de los cuales van a
ser decimales.
Esta abstracción de tipos se ha realizado en las clases de tipo Attribute ( recordemos
el diseñador gráfico ) de forma que es aplicada a todas las clases que heredan de el.
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
227
(*)Aquí podemos ver la especificación de la propiedad dataType para la clase Attribute y todas las clases que quedan afectadas por el.
DataType queda definido como un tipo de datos Enum dentro del diseñador, que ha
sido definido de la siguiente forma:
Para terminar, simplemente recordar que se hace uso de esto desde el método
BuildSQLDataType de la clase Validación, que es donde realmente se aplican los tipos de
datos específicos al motor de base de datos destino en base a este estándar.
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
228
BIBLIOGRAFÍA
A UML Profile for Multidimensional Modeling in Data Warehouses, Sergio Lujan-Mora, Juan Trujillo del Departamento de Lenguajes y Sistemas Informáticos de la Universidad de Alicante.
Utilizado para adaptar el profile de UML a nuestro lenguaje de especificación de dominio.
Blog creado por nosotros mientras desarrollábamos la aplicación:
http://dsltools.blogspot.com
En el hemos ido plasmando durante el proceso de desarrollo del proyecto todos los problemas que nos íbamos encontrando, así como las soluciones adoptadas ante los mismos para salvarlos. Ha tenido bastante aceptación e incluso ha sido publicado en la pagina oficial de INETA-latam (Internet .NET Association – latinoamericano). Podéis acceder a través de http://www.ineta.org/Latam en la sección “nuestros blogs”, aunque nos hacen mención directa en su página principal también..
Common Warehouse Metamodel (CWM) Specification version 1.1 Volumen 1 de marzo de 2003
Esta referencia la hemos empleado para tener en cuenta el apartado referente a los tipos de datos SQL que soporta nuestro modelo. Gracias a esta especificación hemos podido definir unos tipos de datos genéricos que se adaptaran a cada tipo de sintaxis (SQL Server y Oracle en nuestro caso).
Página oficial de las DSLTools de Microsoft:
http://msdn.microsoft.com/vstudio/DSLTools/default.aspx
Se ha participado activamente en el foro que tienen sobre las DSL, debido aspectos en los que hemos tenido alguna dificultad, tales como el tema de los conectores bidireccionales en las referencias (http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=315160&SiteID=1) o a la hora de acceder a los conectores de las Shapes por código (http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=337873&SiteID=1).
Wiley & Sons - The Data Warehouse Toolkit. Second Edition, Ralph Kimball y Margy Ross.
Utilizado sobre todo el capitulo 1 para conceptos de modelado multidimensional.
Data Warehousing Fundamentals: A Comprehensive Guide for IT Professionals. Paulraj Ponía.
Utilizados como consulta para almacenes de datos los capítulos 10 y 11.
Lenguaje de Especificacion del dominio para un Modelo Multidimensional Orientado a Objetos
Proyecto de fin de carrera de Enrique Catala Bañuls y Vicente Soriano Claver
Año académico 2005-2006
229