HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

229
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

Transcript of HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

Page 1: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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

Page 2: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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

Page 3: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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

Page 4: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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.

Page 5: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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:

Page 6: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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.

Page 7: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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.

Page 8: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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.

Page 9: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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.

Page 10: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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”.

Page 11: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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.

Page 12: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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”.

Page 13: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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.

Page 14: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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.

Page 15: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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

)

Page 16: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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.

Page 17: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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:

Page 18: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

18

Page 19: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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.

Page 20: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

20

Page 21: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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.

Page 22: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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.

Page 23: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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.

Page 24: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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.

Page 25: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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.

Page 26: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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.

Page 27: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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.

Page 28: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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.

Page 29: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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.

Page 30: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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

Page 31: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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

Page 32: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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í:

Page 33: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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

Page 34: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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.

Page 35: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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:

Page 36: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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:

Page 37: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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

Page 38: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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:

Page 39: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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.

Page 40: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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

Page 41: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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.

Page 42: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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:

Page 43: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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”:

Page 44: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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):

Page 45: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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

Page 46: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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:

Page 47: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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

Page 48: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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

Page 49: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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:

Page 50: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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):

Page 51: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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

Page 52: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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.

Page 53: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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:

Page 54: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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

Page 55: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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:

Page 56: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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

Page 57: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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)

Page 58: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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).

Page 59: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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.

Page 60: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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.

Page 61: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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:

Page 62: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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.

Page 63: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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.

Page 64: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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.

Page 65: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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.

Page 66: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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.

Page 67: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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:

Page 68: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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).

Page 69: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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);

}

}

}

}

}

Page 70: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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;

Page 71: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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

Page 72: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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);

}

Page 73: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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:

Page 74: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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);

}

}

}

Page 75: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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.

Page 76: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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)]

Page 77: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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.

Page 78: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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

Page 79: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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 =

Page 80: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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.

Page 81: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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);

Page 82: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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).

Page 83: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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.

Page 84: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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;

}

Page 85: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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.

Page 86: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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;

Page 87: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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.

Page 88: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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];

Page 89: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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:

Page 90: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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)

Page 91: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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();

Page 92: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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;

}

Page 93: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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"

Page 94: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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"

Page 95: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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"

Page 96: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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

Page 97: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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:

Page 98: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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

Page 99: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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

Page 100: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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

Page 101: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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

Page 102: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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

Page 103: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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ú.

Page 104: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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;

Page 105: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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.

Page 106: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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

Page 107: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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);

Page 108: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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);

}

Page 109: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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.

Page 110: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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.

Page 111: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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

Page 112: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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

Page 113: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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();

}

Page 114: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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)

{

Page 115: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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

Page 116: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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.

Page 117: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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

Page 118: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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;

}

//

Page 119: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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);

Page 120: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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.

Page 121: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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.

Page 122: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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 ).

Page 123: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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:

Page 124: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

124

*Diagrama de ejemplo que contiene el 100% de funcionalidad.

Page 125: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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),

Page 126: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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

Page 127: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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

Page 128: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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

Page 129: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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:

Page 130: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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.

Page 131: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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”

Page 132: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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.

Page 133: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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.

Page 134: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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.

Page 135: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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” ).

Page 136: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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.

Page 137: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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);

}

Page 138: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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.

Page 139: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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

Page 140: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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);

}

}

}

Page 141: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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

Page 142: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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;

Page 143: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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";

Page 144: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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;

Page 145: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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)

Page 146: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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

Page 147: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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)

{

Page 148: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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

Page 149: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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}\"

Page 150: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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.

Page 151: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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);

}

Page 152: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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.

Page 153: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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

Page 154: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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

Page 155: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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

Page 156: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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

Page 157: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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));

Page 158: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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);

Page 159: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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);

Page 160: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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.

Page 161: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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);

Page 162: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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);

}

Page 163: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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

Page 164: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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 =

Page 165: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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);

}

Page 166: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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;

Page 167: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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);

Page 168: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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));

}

Page 169: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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());

Page 170: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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);

Page 171: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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);

Page 172: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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

Page 173: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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);

}

Page 174: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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

Page 175: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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);

}

Page 176: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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.

Page 177: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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";

Page 178: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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);

Page 179: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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

Page 180: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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

Page 181: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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

Page 182: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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++;

Page 183: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

183

contenido += stringJerarquias;

}

}

Page 184: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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;

Page 185: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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 ="";

Page 186: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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.

Page 187: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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

Page 188: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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.

Page 189: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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.

Page 190: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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" #>

Page 191: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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"\" #>

Page 192: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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;

Page 193: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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" #>

Page 194: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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" \#>

*/

#>

Page 195: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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" #>

Page 196: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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" #>

Page 197: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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#>

--

-------------------------------------------------------------------------------------------------------

Page 198: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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);

Page 199: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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()#>

--

Page 200: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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

#>

Page 201: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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

Page 202: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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

Page 203: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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

Page 204: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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

Page 205: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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" #>

Page 206: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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_$;

#>

-----------------------------------------------------------------------------------------------------

Page 207: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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);

Page 208: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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)

{

Page 209: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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()#>

Page 210: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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.

--

<#

Page 211: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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

#>

Page 212: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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 ----------------------------------------

Page 213: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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

Page 214: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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

Page 215: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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

Page 216: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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"

Page 217: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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#>

--

Page 218: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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);

Page 219: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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.

--

Page 220: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

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

#>

Page 221: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

221

---------------------------------------- Ahora escribimos a fichero la sentencia de create table de la dimensión, así como su sentencia de CREATE DIMENSION. ----------------------------------------

Page 222: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS 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

222

Una vez ya tenemos todas las dimensiones generadas, generamos la sentencia create table de la tabla Fact ----------------------------------------

Page 223: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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.

Page 224: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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.

Page 225: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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

Page 226: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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.

Page 227: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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.

Page 228: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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.

Page 229: HERRAMIENTA CASE PARA MODELADO DE ALMACENES DE DATOS BASADA EN LENGUAJES ESPECÍFICOS DE DOMINIO

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