Download Máster MNTI Desarrollo Dirigido por Modelos Seminario 4

Document related concepts
no text concepts found
Transcript
Máster MNTI
Desarrollo Dirigido por Modelos
Seminario 4 - Transformaciones modelo a código
Grupo Modelum
Universidad de Murcia
19 de enero de 2011
Resumen
En esta sesión se abordará la fase de generación de código utilizando transformaciones
modelo a código definidas en MOFScript. MOFScript es un lenguaje de plantillas basado
en reglas que permite generar código a partir de modelos conformes a metamodelos. Su
simplificidad y facilidad para definir plantillas lo convierte en un lenguaje comprensible
y muy potente.
Los ejercicios de este seminario tendrán como objetivo generar código a partir de los
modelos que han resultado de la transformación modelo a modelo de los ejercicios del
seminario anterior.
1
1.
Introducción
En la sesión anterior se aplicaron una serie de transformaciones modelo a modelo para
obtener un modelo intermedio que representa una pequeña arquitectura para gestionar menús
a partir del modelo de sintaxis abstracta del DSL. El objetivo de esta sesión es generar el
código necesario para la incorporación de un menú en una aplicación Swing a partir del
modelo intermedio de arquitectura generado.
Para llevar a cabo la generación de código, se utilizará el lenguaje de transformación
MOFScript, el cual es un lenguaje basado en reglas integrado en la plataforma Eclipse. Una
transformación MOFScript está compuesta por un conjunto de reglas que definen las secciones
de código a generar a partir de los elementos del modelo origen. Estas secciones a generar
actúan como plantillas donde se inserta determinada información para completarlas, por este
motivo, MOFScript también recibe el nombre de lenguaje de plantillas. Durante el desarrollo
de este guión se presentará el entorno de programación en MOFScript y se desarrollará la
transformación necesaria. En el Anexo A puede encontrarse un resumen de las principales
sentencias de MOFScript.
2.
Organización del proyecto para el DSL
Dado que anteriormente hemos utilizado transformaciones modelo a modelo, se dispone
de un proyecto en Eclipse con la estructura de un proyecto tı́pico de la plataforma AGE. De
esta forma se creará una carpeta llamada m2c dentro de la carpeta transformations (si no
existe ya). Esta será nuestra carpeta de trabajo, todas las transformaciones MOFScript que
se realicen se guardarán en su interior.
Durante el desarrollo de este guión se generará una aplicación Java Swing que será la que
incorporará el menú correspondiente a cada perfil. Por ello es necesario crear un proyecto
Java donde se almacenarán los ficheros generados como resultado de la ejecución de la transformación MOFScript. Al integrar la generación del código en un proyecto Java de Eclipse
se puede utilizar el sistema de compilación automática de la plataforma ası́ como su entorno
de edición y depuración.
Figura 1: Estructura de las carpetas del proyecto
3.
Configuración del entorno
MOFScript está incorporado a la plataforma Eclipse como un plugin que ofrece un editor
para transformaciones MOFScript, un motor para la ejecución de transformaciones y una hoja
de configuración en el menú de preferencias de Eclipse. Antes de comenzar a definir y ejecutar
transformaciones MOFScript, es conveniente comprobar que la información contenida en la
hoja de configuración es correcta.
La hoja de configuración que ofrece MOFScript puede observarse en la Figura 2. El campo
principal a comprobar es el primero, el cual indica la localización de los metamodelos que se
2
utilizarán en la definición de las transformaciones. De todas formas, es conveniente comprobar
que los demás campos contienen información correcta para evitar problemas colaterales. Para
ello, se debe hacer click en cada campo y comprobar si existe mensaje de error.
Figura 2: Hoja de configuración de MOFScript
Una cuestión importante a destacar es el hecho de que si trabajamos con metamodelos
almacenados en diferentes partes del disco deberı́amos cambiar este campo de configuración
continuamente. La solución a este problema es utilizar el registro de metamodelos ecore en la
plataforma Eclipse. Con este método, podemos registrar un metamodelo y las transformaciones que utilicen dicho metamodelo funcionarán correctamente sin la necesidad de configurar
el plugin en la hoja de configuración.
La configuración del directorio donde se almacenarán los ficheros generados como resultado de una transformación MOFScript se debe realizar individualmente para cada fichero
m2t. La Figura 3 muestra la hoja de propiedades asociada a cada fichero m2t.
Figura 3: Propiedades MOFScript para un fichero m2t
3.0.1.
Resumen de comandos
Windows → Preferences → MOFScript Preferences
Abrir la hoja de configuración del plugin MOFScript
Click der. (fichero Ecore) → Register metamodel
Registrar metamodelo
3
Click der. (fichero m2t) → Properties → MOFScript properties
Configurar propiedades MOFScript para un fichero m2t
3.1.
Conocimiento del dominio de la arquitectura destino
La transformación MOFScript que se desarrollará en este seminario generará el código
necesario para incluir un menú en una aplicación Swing a partir de una instancia del metamodelo de la arquitectura. En primer lugar, se debe conocer el dominio donde se generará el
código.
Swing es una librerı́a gráfica en Java que forma parte de Java Foundation Classes (JFC).
Incluye un conjunto de elementos gráficos (o widgets) tales como cajas de texto, botones,
menús o tablas para llevar a cabo el diseño y creación de aplicaciones de interfaz gráfica en
Java.
Una aplicación Swing puede tener incluido un menú que contiene un conjunto de opciones
para interacturar con la aplicación. La clase principal encargada de representar el menú en una
aplicación es JMenuBar, la cual puede contener un conjunto de elementos de tipo JMenuItem.
En la Figura 4 se muestra la jerarquı́a de estos elementos de menú.
Figura 4: Jerarquı́a de clases para los menús en Swing
El código Java que incluye un menú File con las opciones New y Close en una aplicación
App serı́a el siguiente:
// Creacion de menus
JMenu fileMenu = new JMenu("File");
fileMenu.add(new JMenuItem("New"));
fileMenu.add(new JMenuItem("Close"));
// Creacion de barra de menu
JMenuBar menuBar = new JMenuBar();
menuBar.add(fileMenu);
// Creacion de ventana e inclusin de barra de menu
JFrame app = new JFrame("App");
app.setJMenuBar(menuBar);
En este seminario se construirá una aplicación Java Swing que incorpore una barra de
menú. La organización de los elementos contenidos en dicha barra de menú será dependiente
del perfil que esté activo. Por lo tanto, debe existe una forma para realizar el cambio de
perfiles. Para llevar a cabo la creación de la aplicación se ha optado por crear dos clases:
Clase Application. Esta clase es una aplicación Swing que incluye la barra de menú y
permite cambiar entre los diferentes perfiles. El cambio de perfiles se realiza por medio
de la pulsación del botón correspondiente incluido en dicha aplicación.
4
Clase MenuManager. Es la clase encargada de construir los menús correspondientes a
los perfiles. Es utilizada por Application para recuperar el menú asociado a un perfiles
al pulsar el botón correspondiente. El código de ejemplo para esta clase se muestra en
el Anexo D.
Además, dado que el DSL de menús soporta la internacionalización de los menús, se
utilizará la clase Internationalization (mostrada en el Anexo E. Esta clase será utilizada
por la clase MenuManager para la construcción de los menús de la aplicación conforme a un
idioma determinado. Los ficheros de internacionalización utilizados por esta clase deberán
ser generados automáticamente para cada idioma. MenuManager obtendrá la tranducción de
cada uno de los menús llamando al método getString de la clase Internationalization.
La clase Internationalization utilizada en este caso práctico está configurada para utilizar
siempre el lenguaje español, pero un diseño más realista deberı́a contemplar la posibilidad de
cambiar el idioma arbitrariamente.
A continuación, se abordará la generación de los ficheros necesarios utilizando el lenguaje
MOFScript.
3.2.
Generar los ficheros de internacionalización
La generación de los ficheros de internacionalización toma como entrada instancias de
modelos conformes al metamodelo i18n. Los ficheros generados tienen el formato tı́pico de un
fichero de propiedades, donde cada lı́na define una propiedad con el formato key = value.
Este podrı́a ser un ejemplo:
fileMenu.name="Fichero"
fileMenu.tooltip="Menu Fichero"
fileMenu.mnemonic=f
fileMenu.shortcut=ctrl+f
Cada fichero corresponde a un lenguaje y contiene las propiedades necesarias para la traducción de cada menú. De esta forma, por cada Bundle del metamodelo i18n se generará un
fichero de internacionalización que contendrá tantas propiedades como elementos Entry contenga dicha metaclase Bundle.
El fichero generado seguirá el formato menu [language] [variant].properties, donde language y variant corresponden a los valores de dichos atributos en la instancia de la
metaclase Bundle. Cada propiedad del fichero seguirá el formado [key]=[value] donde key
y value serán precisamente los valores de dichos atributos de la instancia de la metaclases
Entry. En el Anexo C se muestra un ejemplo de fichero de internacionalización.
3.3.
Generar el esqueleto de Application
Al igual que se harı́a para crear la aplicación de manera tradicional, el primer paso consistirı́a en generar una aplicación Java Swing de prueba que incorpore el menú o menús
correspondientes a cada perfil. De esta forma, el proceso a seguir para definir la transformación MOFScript consistirá primero en generar la clase Java encargada de mostrar la ventana
(llamada Application) y a continuación generar la clase encargada de gestionar los menús
(llamada MenuManager).
El primer paso para definir una transformación MOFScript es especificar su nombre y el
metamodelo que utilizará para definir las reglas de transformación. Se parte del modelo de
arquitectura generado por la transformación modelo-a-modelo definida en la sesión anterior.
La finalidad principal del metamodelo al que conforma este modelo es acercarse al dominio de
la arquitectura destino y facilitar con ello la generación de código. El metamodelo se muestra
en el Anexo B.
5
A continuación se debe definir la regla main de la transformación. Como en la mayorı́a de
los casos, la regla main de la transformación tendrá como contexto el elemento raı́z del metamodelo, que es MenuManager, el cual representa a la entidad encargada de crear y gestionar
los menús. A partir de dicho elemento se puede acceder a los menus y los profiles definidos.
La regla main se encargará de crear el fichero que contendrá a la clase Application, la
cual se encargará de visualizar una ventana en pantalla. El código esqueleto a generar para
mostrar una ventana en Java Swing serı́a el siguiente:
package centic;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import
import
import
import
javax.swing.BoxLayout;
javax.swing.JButton;
javax.swing.JFrame;
javax.swing.JPanel;
public class Application {
public static void main(String[] args) throws IOException {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
Application theInstance = new Application();
((Application) theInstance).createAndShowGUI();
}
});
}
public void createAndShowGUI() {
// Create and set up the window.
final JFrame frame = new JFrame("Application");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel mainPane = new JPanel();
BoxLayout layout = new BoxLayout(mainPane, BoxLayout.Y_AXIS);
mainPane.setLayout(layout);
frame.setContentPane(mainPane);
// Show the window.
frame.pack();
frame.setVisible(true);
}
}
3.4.
Generar los botones de cambio de menú en Application
El siguiente paso consistirı́a en enriquecer el código Java de la clase Application para que
muestre un botón por cada perfil de menú que esté definido en el metamodelo. La pulsación
de cada uno de ellos provocarı́a que la aplicación Java Swing cambie la disposición de los
menús.
El código especı́fico para la inclusión de menús en la aplicación está asociado a las acciones
asociadas a los botones que incluirá la aplicación Swing. En concreto, el código Java que
realizarı́a estas acciones serı́a el siguiente
JButton ProfileButton = new JButton("Profile");
ProfileButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
frame.setJMenuBar(MenuManager.INSTANCE.getProfile());
frame.pack();
}
});
mainPane.add(ProfileButton);
Como se puede observar, el código anterior debe repetirse para cada tipo de perfil que se
haya definido. Además, el punto de variabilidad del código se encuentra en el nombre que se
6
da a la variable JButton, el texto que incluye el botón, ası́ como la llamada al método get
de la clase MenuManager.
Con estas modificaciones, la definición de transformación MOFScript genera el código
necesario para la clase Application.
3.5.
Generar el código para MenuManager
Al igual que para la clase Application, la regla main se encargará también de generar
la clase MenuManager, la cual se encargará de crear los menús. El código que debe generar
la regla main debe ser estudiado para conocer cuales son los puntos que dependen de la
información del modelo de arquitectura. La clase MenuManager (un ejemplo de dicha clase
puede encontrarse en D) se descompone en las siguientes secciones:
1. Declaración del paquete en el que está incluida la clase.
2. Sección de imports.
3. Declaración de la clase.
a) Variables de instancia.
b) Metódo de inicialización de los widgets y establecimiento de listeners
c) Constructor de los elementos del menú.
d ) Establecimiento de las acciones para los elementos del menú.
e) Constructores de las barras de menú para los perfiles.
De las secciones anteriores, las únicas que dependen de la información contenida en los
modelos de la arquitectura las siguientes:
Sección 1. El nombre del paquete debe poder ser cambiado.
Sección 3a. Las variables de instancia dependen de los elementos de menú que contenga
el modelo.
Sección 3c. El constructor para los elementos del menú depende del número y tipo de
elementos que contenga el modelo.
Sección 3d. La acción asociada a cada elemento del menú depente de la información del
modelo.
Sección 3e. Se debe generar un constructor de barra de menú para cada perfil.
Una vez identificadas las partes del código dependientes de la información del modelo,
se puede pasar a la definición de la regla main encargada de la generación del código para
la clase MenuManager. A continuación se estudiará el código a incluir en cada una de las
secciones dependiente de la información del modelo.
Sección 1. Generación del paquete. Esta sección es simple y simplemente debe
hacerse uso de una propiedad de la definición de transformación para generar el codigo.
Sección 3a. Generación de las variables de instancia. Esta sección de la clase
MenuManager contiene tantas variables de instancia como elementos del menú contenga la instancia de la metaclase MenuManager, en concreto, la referencia menus. Esta
referencia es de tipo MenuItem, que realmente es una jerarquı́a de metaclases que representan a diferentes tipos de menú, por lo que el código a generar para cada una de
ellas difiere. Las correspondencias entre cada metaclase y el objeto Java Swing que lo
representa como menú son las siguientes:
7
Metaclase arquitectura
SingleMenuItem
TreeMenu
MenuBar
CheckboxMenuItem
Objeto Java Swing
JMenuItem
JMenu
JMenuBar
JCheckBoxMenuItem
Para generar el código correspondiente a cada variable de instancia se deberá recorrer
los elementos de la referencia menus de la metaclase MenuManager (que es el contexto
de la regla main). Para cada elemento se llamará a la regla mapInstanceVariable,
que será una regla definida para cada una de las metaclases de la jerarquı́a comentada
anteriormente. El código de cada una de las reglas se debe encargar de definir la variable
de instancia correspondiente al elemento.
Sección 3c. Generación del código para construir los elementos del menú.
Esta sección de código, al igual que en el caso anterior, depende de los elementos de la
referencia menus de la instancia de la metaclase MenuManager. Para cada elemento que se
ha añadido como variable de instancia se debe llamar al constructor. Los constructores
de los anteriores objetos Java son:
Metaclase arquitectura
SingleMenuItem
TreeMenu
MenuBar
CheckboxMenuItem
Objeto Java Swing
new JMenuItem(String menuText)
new JMenu(String menuText)
new JMenuBar()
new JCheckBoxMenuItem(String menuText)
Al igual que en el caso anterior, en la regla main se deberá recorrer los elementos de la referencia menus y para cada elemento llamar la regla llamada mapInitializeInstanceVa
riable, que será la encargada de generar el código correspondiente.
Sección 3d. Generar las acciones para los elementos del menú. Esta sección
de código genera un método encargado de asignar las acciones a cada elemento del
menú. Dado que solamente los elementos del menú de tipo SingleMenuItem tendrán
establecida una acción (metaclase Action en el metamodelo de arquitectura), se debe
recorrer la referencia menus para trabajar solamente con elementos de este tipo. Para
cada uno de ellos se añadirá un nuevo listener que apuntará a la clase encargada de
realizar la acción. Dicha clase está especificada en el atributo name de la clase Action
a la que SingleMenuItem hace referencia. Un ejemplo de generación de código para
añadir un listener a un botón serı́a el siguiente:
this.fileMenu.addActionListener(new mnti2010.listeners.fileMenuAction());
Sección 3e. Generar los constructores de las barras de menú. Esta sección
de la regla main se encarga de generar el código necesario para incluir un método
cuya finalidad es construir la barra de menú asociada a un perfil. Se generarán tantos
métodos como perfiles tenga la instancia de la metaclase MenuManager. Para ello, se
deberá recorrer la referencia profiles y generar el código necesario para cada entrada.
Los perfiles están compuestos por un conjunto de sentencias que establecen qué menús
debe contener dicho perfil ası́ como sus propiedades básicas. De esta forma, se deberá recorrer la referencia statements para configurar la barra de menú del perfil. Esta
referencia contiene un conjunto de elementos de la jerarquı́a Statement del metamodelo de la arquitectura. Para cada metaclase de la jerarquı́a se creará una regla llamada
mapStatement que se encargará de generar el código asociado al comportamiento de
8
la sentencia. La siguiente tabla muestra las correspondencias para cada metaclase y la
forma de llevar a cabo el comportamiento en Swing.
Metaclase arquitectura
AddMenu
SetToolTip
DisableMenu
SetShortcut
SetMnemonic
Java Swing
método add(JMenu menu)
método setToolTipText(String text)
método setEnabled(Boolean isEnabled)
método setAccelerator(KeyStroke keyStroke)
método setMnemonic(KeyEvent keyEvent)
Con estas modificaciones, la definición de transformación MOFScript genera el código
necesario para la clase MenuManager.
4.
Ejercicios
Para realizar estos ejercicios, descarga el proyecto esqueleto que en la web de la asignatura.
El fichero seminario4-esqueleto.zip de la web contiene el proyecto esqueleto. Se facilita
una primera versión para disponer de un ejemplo de lo que deberı́a generarse.
Ejercicio 1. Completa la transformación MOFScript i18n2Text.m2t para que genere
los ficheros de internacionalización. Estos ficheros deberán generarse en el directorio
src del proyecto seminario4 ejercicios.gen. Los ficheros deben seguir el formato
descrito en 3.2.
Ejercicio 2. Completa la transformación MOFScript menu2swign.m2t para que genere
las clases Application y MenuManager. La generación de estas clases deberá realizarse
en el directorio src/imaci del proyecto seminario4 ejercicios.gen. Se deberá establecer el nombre de los menús utilizando la clase Internationalization y al menos
su tooltip. Para su elaboración sigue las indicaciones dadas a lo largo de este guión.
En el directorio models del proyecto seminario4 ejercicios se facilita un ejemplo de modelo conforme al metamodelo i18n (example-i18n.xmi) y otro ejemplo de modelo conforme
al metamodelo de arquitectura (example.xmi) para probar las transformaciones definidas.
El resultado de las transformaciones deberá estar contenido en un proyecto Java llamado
seminario4 ejercicios.gen.
Se enviarán los proyectos seminario4 ejercicios y seminario4 ejercicios.gen en un
fichero zip con los ejercicios resueltos a [email protected].
9
A.
Resumen sentencias MOFScript
Tipos primitivos. Los tipos primitivos ofrecidos por MOFScript son los siguientes:
String, Integer, Real, Boolean, Hastable, List y Object. El tipo Object permite
representar cualquier tipo.
Trabajo con ficheros. La sentencia file permite establecer el fichero a crear donde
se generará el código indicado en la transformación. El nombre del fichero se indica
como parámetro y puede incluir una ruta relativa o absoluta. Algunos ejemplos de uso
son:
file (" myfile . java ")
file f1 (" myfile2 . java ")
Impresión. Estas sentencias permiten imprimir cadenas de texto en un fichero o en
la pantalla. Las sentencias principales son print y println. Por defecto, se imprime
en el último file indicado pero puede indicarse la salida estándar para imprimir por
pantalla. Algunos ejemplos de uso son:
stdout . println ( ‘ ‘ mensaje ’ ’)
file f1 (" myfile2 . java ")
f1 . print ( ‘ ‘ codigo ’ ’
Iteradores. Los iteradores permiten recorrer colecciones de elementos. La sentencia
forEach define un iterador sobre una colección que puede ser una colección de elementos
del modelo, una lista/hash o una colección de String/Integer. Algunos ejemplos de uso
son:
collection - > forEach ( element )
collection - > forEach ( c : someType )
collection - > forEach ( a : String | a . size () = 2)
Sentencia select. La sentencia select permite realizar una consulta sobre una colección de elementos y devuelve una lista con los elementos encontrados. Actualmente
solamente puede ser utilizada en asignaciones y su sintaxis es muy similar a la utilizada
en la sentencia forEach. Un ejemplo de uso serı́a:
var result : List = self . states - > select ( st : stateMM . State | st . name . startswith ("
,→ ST ") )
Sentencias condicionales. MOFScript ofrece estructuras de tipo if y while. A continuación se muestran ejemplos de cada una de ellas:
if ( c . states > 10) {
// s t a t e m e n t s
} else if ( c . states > 5) {
// s t a t e m e n t s
} else {
// s t a t e m e n t s
}
while ( c . states > 1) {
// s t a t e m e n t s
}
Expresiones lógicas. MOFScript ofrece soporte a expresiones lógicas estándar al igual
que cualquier otro lenguaje de programación.
Para más información acerca del
http://www.eclipse.org/gmt/mofscript
10
lenguaje,
se
puede
visitar
la
web
B.
Metamodelo de la arquitectura
Figura 5: Metamodelo de la arquitectura generada.
11
C.
Ejemplo de fichero de internacionalización
fileMenu.name=Fichero
fileMenu.tooltip=Menu Fichero
fileMenu.mnemonic=f
fileMenu.shortcut=ctrl+f
newMenu.name=Nuevo
newMenu.tooltip=Crea un fichero
newMenu.mnemonic=n
newMenu.shortcut=ctrl+n
saveAsMenu.name=Salvar como
saveAsMenu.tooltip=Salvar con otro nombre
saveAsMenu.mnemonic=s
saveAsMenu.shortcut=ctrl+s
autosaveMenu.name=Autosalvar
autosaveMenu.tooltip=Autosalvar
autosaveMenu.mnemonic=a
autosaveMenu.shortcut=ctrl+a
closeMenu.name=Cerrar
closeMenu.tooltip=Cierra la aplicacion
closeMenu.mnemonic=c
closeMenu.shortcut=ctrl+c
editMenu.name=Edicion
editMenu.tooltip=Menu Edicion
editMenu.mnemonic=e
editMenu.shortcut=ctrl+e
undoMenu.name=Deshacer
undoMenu.tooltip=Deshace el ultimo cambio
undoMenu.mnemonic=u
undoMenu.shortcut=ctrl+u
redoMenu.name=Rehacer
redoMenu.tooltip=Rehace el ultimo cambio deshecho
redoMenu.mnemonic=r
redoMenu.shortcut=ctrl+r
...
12
D.
Código fuente para la clase MenuManager
package mnti;
import
import
import
import
javax.swing.JCheckBoxMenuItem;
javax.swing.JMenu;
javax.swing.JMenuBar;
javax.swing.JMenuItem;
class MenuManager {
public final static MenuManager INSTANCE = new MenuManager();
// Instance variables
private JMenu fileMenu;
...
private void initWidgets() {
createSingleMenus();
attachListeners();
}
public void createSingleMenus() {
this.fileMenu = new JMenu(Internationalization.getInstance().getString("fileMenu.name"));
...
}
public void attachListeners() {
//this.newMenu.addActionListener(new mnti.listeners.newMenuAction());
...
}
public JMenuBar getProfileAdvancedUser() {
initWidgets();
this.AdvancedUser.add(this.fileMenu);
...
return AdvancedUser;
}
...
}
13
E.
Código fuente para Internationalization
package mnti;
import java.util.Locale;
import java.util.ResourceBundle;
public class Internationalization {
private static ResourceBundle messages;
private static Internationalization instance = null;
private Internationalization() {
Locale currentLocale = new Locale( "es" ,"ES" ) ;
messages = ResourceBundle.getBundle("menu", currentLocale ) ;
}
public static Internationalization getInstance() {
if(instance == null) instance = new Internationalization();
return instance;
}
public static String getString(String key) {
String sol = null;
try {
sol = messages.getString(key);
} catch(Exception e) {
sol = key;
}
return sol;
}
}
14