Download Curso DSDM Sesión 4 - Genereración de código
Document related concepts
no text concepts found
Transcript
Curso DSDM Sesión 4 - Genereración de código (MOFScript) Grupo Modelum Universidad de Murcia 10 de marzo de 2009 Resumen En esta sesión se abordará la fase de generación de código utilizando el lenguaje 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. Durante este guión se continuará desarrollando el ejemplo tratado durante los guiones anteriores. En esta sesión el objetivo será generar código a partir de los modelos que han resultado de la transformación modelo a modelo de la sesión 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 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. Sesión guiada 2.1. Organización del proyecto para el DSL Se parte del proyecto menuprofile desarrollado durante el guión de la sesión anterior. Para el trabajo que se realizará en este guión, 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. En un principio se trabajará con una sola definición de transformación. 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. 2.1.1. Tareas 1. Crear una carpeta llamada m2c dentro de la carpeta transformations. 2. La estructura del proyecto se muestra en la Figura 1. 3. Crear un fichero MOFScript con el nombre de menu2swing.m2t. 4. Crear un proyecto Java llamado menuprofile.gen. 2.1.2. Resumen de comandos Click der. (proyecto) → New folder → Nombre de carpeta Crear nueva carpeta Click der. (Carpeta m2c) → New. . . → Other → MOFScript → MOFScript File Crear nuevo fichero MOFScript 2 Figura 1: Estructura de las carpetas del proyecto menuprofiles 1 File → New. . . → Java Project Crear nuevo proyecto Java 2.2. 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 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 3 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. Otra forma de configurar la ejecución de la transformación MOFScript es haciendo uso de la tarea Ant encargada de dicha finalidad. Para ello, se debe crear un fichero build.xml (o reutilizar el ya existente en el proyecto) y añadir un elemento de tipo target que configure la tarea MOFScript para llevar a cabo la ejecución. Figura 3: Propiedades MOFScript para un fichero m2t 2.2.1. Tareas 1. Abrir la hoja de configuración del plugin MOFScript. 2. Comprobar que los datos de los campos son correctos. 3. Configurar la propiedad Select Root Output Directory de la hoja de propiedades MOFScript para el fichero menu2swing.m2t. Esta propiedad debe indicar la ruta hacia el directorio src del proyecto menuprofile.gen. 2.2.2. Resumen de comandos Windows → Preferences → MOFScript Preferences Abrir la hoja de configuración del plugin MOFScript Click der. (fichero Ecore) → Register metamodel Registrar metamodelo Click der. (fichero m2t) → Properties → MOFScript properties Configurar propiedades MOFScript para un fichero m2t 4 2.3. Conocimiento del dominio de la arquitectura destino La transformación MOFScript que se desarrollará en este guión 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); Durante el desarrollo de este guión, 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. El código de ejemplo para esta clase se muestra en el Anexo C. 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. 5 A continuación, se abordará la generación de estas clases utilizando el lenguaje MOFScript. 2.4. 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 metamodelo generado por la transformación modelo-a-modelo definida en la sesión anterior. La finalidad principal de este metamodelo 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. 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); } } 6 2.4.1. Tareas 1. Crear una regla main en la transformación main2swing que genere el código Java anterior. Se debe tener en cuenta que la clase Application debe estar contenida en el paquete centic. 2. Probar la generación de código y comprobar que la clase Application funciona correctamente 2.5. 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 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. La implementación de dicha transformación se incluye en el Anexo E. 2.5.1. Tareas 1. Enriquecer la regla main para que contemple la generación de código para los botones. 2. Probar la generación de código y comprobar que la clase Application funciona correctamente. 2.6. 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. 7 b) Constructor de la clase. 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: 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: 8 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.newFile.addActionListener(new centic.listeners.newfileAction()); 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 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. La implementación de dicha transformación se incluye en el Anexo F. 2.6.1. Tareas 1. Enriquecer la regla main para que contemple la generación de código para la clase MenuManager. A ser posible, seguir la nomenclatura mostrada anteriormente. 9 Se debe generar el nombre del paquete. Generar las variables de instancia para los elementos del menú. Generar el método encargado de construir los elementos del menú. Generar el método encargado de establecer las acciones de los elementos del menú. Generar los constructores para las barras de menú asociadas a cada perfil. 2. Probar la generación de código y comprobar que la clase Application funciona correctamente. 3. Sesión no guiada Definir una transformación MOFScript para generar automáticamente los ficheros de recursos necesarios para la internacionalización. Los ficheros de recursos deberán ser generados en la carpeta src del proyecto menuprofile.lib. Modificar la definición de transformación MOFScript para internacionalizarla. Para ello, deberá utilizar ficheros de recursos para recuperar las cadenas de texto que se muestren en la aplicación. 10 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. 11 B. Metamodelo de la arquitectura MenuManager profiles * statements * ProfileMethod name : String root 1 menus * Statement child 1 MenuItem variableId : String text : String AddMenu parent 1 1 menu MenuStatement SetStringProperty Action name : String 1 action SingleMenuItem TreeMenuItem SetToolTip CheckboxMenuItem defaultValue : Boolean MenuBar SetMnemonic SetShortcut key : String character : String DisableMenu Figura 5: Metamodelo de la arquitectura generada. 12 C. Código fuente para Application package centic; import import import import import import import java.awt.event.ActionEvent; java.awt.event.ActionListener; java.io.IOException; 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() { ApplicationExample theInstance = new ApplicationExample(); ((ApplicationExample) 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); JButton ProfileAButton = new JButton("ProfileA"); ProfileAButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { frame.setJMenuBar(MenuManagerExample.INSTANCE.getProfileProfileA() ); frame.pack(); } }); mainPane.add(ProfileAButton); JButton ProfileBButton = new JButton("ProfileB"); ProfileBButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { frame.setJMenuBar(MenuManagerExample.INSTANCE.getProfileProfileB() ); frame.pack(); } }); mainPane.add(ProfileBButton); // Show the window. frame.pack(); frame.setVisible(true); } } 13 D. Código fuente para la clase MenuManager package centic; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; class MenuManager { private JMenu file; private JMenuItem newFile; private JMenuItem close; private JMenuItem exit; private JMenuBar profileAProfileMenu; private JMenuBar profileBProfileMenu; public final static MenuManager INSTANCE = new MenuManager(); private MenuManager() { createSingleMenus(); attachListeners(); } public void createSingleMenus() { this.file = new JMenu("File"); this.newFile = new JMenuItem("New File"); this.close = new JMenuItem("Close"); this.exit = new JMenuItem("Exit"); this.profileAProfileMenu = new JMenuBar(); this.profileBProfileMenu = new JMenuBar(); } public void attachListeners() { this.newFile.addActionListener(new centic.listeners.newfileAction()); this.close.addActionListener(new centic.listeners.closeAction()); this.exit.addActionListener(new centic.listeners.exitAction()); } public JMenuBar getProfileProfileA() { this.profileAProfileMenu.add(this.file); this.file.add(this.exit); this.exit.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_E, ActionEvent.CTRL_MASK)); this.exit.setMnemonic(KeyEvent.VK_S); return profileAProfileMenu; } public JMenuBar getProfileProfileB() { this.profileBProfileMenu.add(this.file); this.file.add(this.newFile); this.file.add(this.close); this.file.add(this.exit); return profileBProfileMenu; } } 14 E. Definición de transformación MOFScript para la clase Application menu . MenuManager :: main () { file ( pkgDir + ’ Application . java ’ ) ’ package ’ package ’; import java . awt . event . ActionEvent ; import java . awt . event . Act ionListe ner ; 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 . Swi ngUtilit ies . invokeLater ( new Runnable () { public void run () { Application theInstance = new Application () ; (( Application ) theInstance ) . c r e a t e A n d S h o w G U I () ; } }) ; } public void c r e a t e A n d S h o w G U I () { // Create and set up the window . final JFrame frame = new JFrame (" Application ") ; frame . s e t D e f a u l t C l o s e O p e r a t i o n ( JFrame . EXIT_ON_CLOSE ) ; JPanel mainPane = new JPanel () ; BoxLayout layout = new BoxLayout ( mainPane , BoxLayout . Y_AXIS ) ; mainPane . setLayout ( layout ) ; frame . setCo ntentPan e ( mainPane ) ; ’ ’ ’ self . profiles - > forEach ( c : menu . ProfileMethod ) { JButton ’ c . name ’ Button = new JButton (" ’ c . name ’ ") ; ’c . name ’ Button . a d d A c t i o n L i s t e n e r ( new Acti onListe ner () { public void ac ti o nP er fo r me d ( ActionEvent e ) { frame . setJMenuBar ( MenuManager . INSTANCE . ’ c . p r o f i l e M e t h o d N a m e () ,→ ’ () ) ; frame . pack () ; } }) ; mainPane . add ( ’ c . name ’ Button ) ; ’ newline (1) } // Show the window . frame . pack () ; frame . setVisible ( true ) ; } } ’ } menu . ProfileMethod :: p r o f i l e M e t h o d N a m e () : String { result = ’ getProfile ’ + self . name ; } 15 F. Definición MOFScript para generar la clase MenuManager // ( c o n t i n u a la d e f i n i c i n de la regla main ) file ( pkgDir + ’ MenuManager . java ’ ) ’ package ’ package ’; import java . awt . event . ActionEvent ; import java . awt . event . KeyEvent ; import import import import import javax . swing . J C h e c k B o x M e n u I t e m ; javax . swing . JMenu ; javax . swing . JMenuBar ; javax . swing . JMenuItem ; javax . swing . KeyStroke ; class MenuManager { ’ newline (1) self . menus - > forEach ( m : menu . MenuItem ) { m . m a p I n s t a n c e V a r i a b l e () } ’ public final static MenuManager INSTANCE = new MenuManager () ; private MenuManager () { c r e a t e S i n g l e M e n u s () ; a tt ac h Li st en e rs () ; } public void c r e a t e S i n g l e M e n u s () { ’ newline (1) self . menus - > forEach ( m : menu . MenuItem ) { m . m a p I n i t i a l i z e I n s t a n c e V a r i a b l e () ; } ’ } ’ newline (2) ’ public void at t ac hL is t en er s () { ’ newline (1) self . menus - > forEach ( m : menu . Sing leMenuIt em ) { this . ’ m . variableId ’. a d d A c t i o n L i s t e n e r ( new ’ li s te ne rP a ck ag e ’. ’ m . ,→ action . name ’ () ) ; ’ newline (1) } } ’ newline (2) ’ ’ self . profiles - > forEach ( p : menu . ProfileMethod ) { public JMenuBar ’ p . p r o f i l e M e t h o d N a m e () ’ () { ’ newline (1) p . statements - > forEach ( s ) { s . mapStatement () } return ’ p . root . variableId ’; } ’ newline (2) } ’ ’ ’} ’ } // C r e a t i n g i n s t a n c e v a r i a b l e s abstract menu . MenuItem :: m a p I n s t a n c e V a r i a b l e () ; menu . Si ngleMenu Item :: m a p I n s t a n c e V a r i a b l e () { ’ private JMenuItem ’ self . variableId ’; ’ newline (1) } menu . TreeMenuItem :: m a p I n s t a n c e V a r i a b l e () { ’ private JMenu ’ self . variableId ’; ’ newline (1) } menu . MenuBar :: m a p I n s t a n c e V a r i a b l e () { ’ private JMenuBar ’ self . variableId ’; ’ newline (1) } 16 menu . C h e c k b o x M e n u I t e m :: m a p I n s t a n c e V a r i a b l e () { ’ private J C h e c k B o x M e n u I t e m ’ self . variableId ’; ’ newline (1) } // I n i t i a l i z i n g i n s t a n c e v a r i a b l e s menu . Si ngleMenu Item :: m a p I n i t i a l i z e I n s t a n c e V a r i a b l e () { ’ this . ’ self . variableId ’ = new JMenuItem ( I n t e r n a t i o n a l i z a t i o n . ,→ getInstance () . getString (" ’ self . variableId ’ ") ) ; ’ newline (1) } menu . TreeMenuItem :: m a p I n i t i a l i z e I n s t a n c e V a r i a b l e () { ’ this . ’ self . variableId ’ = new JMenu ( I n t e r n a t i o n a l i z a t i o n . ,→ getInstance () . getString (" ’ self . variableId ’ ") ) ; ’ newline (1) } menu . MenuBar :: m a p I n i t i a l i z e I n s t a n c e V a r i a b l e () { ’ this . ’ self . variableId ’ = new JMenuBar () ; ’ newline (1) } menu . C h e c k b o x M e n u I t e m :: m a p I n i t i a l i z e I n s t a n c e V a r i a b l e () { ’ this . ’ self . variableId ’ = new J C h e c k B o x M e n u I t e m ( ,→ I n t e r n a t i o n a l i z a t i o n . getInstance () . getString (" ’ self . variableId ’ ") ) ; ’ ,→ newline (1) if ( self . defaultValue = true ) { ’ this . ’ self . variableId ’. setSelected ( true ) ; ’ newline (1) } } // M a p p i n g s s t a t e m e n t s menu . AddMenu :: mapStatement () { ’ this . ’ + self . parent . variableId ’. add ( this . ’ self . child . variableId ’) ; ,→ ’ newline (1) } ’ ’ menu . SetToolTip :: mapStatement () { this . ’ + self . menu . variableId ’. setT oolTipTe xt (" ’ self . value ’ ") ; ’ ,→ newline (1) } menu . DisableMenu :: mapStatement () { this . ’ self . menu . variableId ’. setEnabled ( false ) ; ’ newline (1) } menu . SetShortcut :: mapStatement () { if ( not self . menu . oclIsKindOf ( menu . TreeMenuItem ) ) { ’ this . ’ + self . menu . variableId ’. setA ccelerat or ( KeyStroke . getKeyStroke ( ,→ KeyEvent . VK_ ’ self . character ’ , ActionEvent . ’ self . key ’ _MASK ) ) ; ’ newline (1) } } ’ menu . SetMnemonic :: mapStatement () { this . ’ + self . menu . variableId ’. setMnemonic ( KeyEvent . VK_ ’ self . value . ,→ toUpper () ’) ; ’ newline (1) } 17