DAVI AZEVEDO DE QUEIROZ SANTOS TRANSFORMAÇÃO DE MODELOS INDEPENDENTE DE METAMODELOS USANDO REFLEXÃO Dissertação apresentada como requisito parcial à obtenção do grau de Mestre. Programa de Pós-Graduação em Informática, Setor de Ciências Exatas, Universidade Federal do Paraná. Orientador: Prof. Dr. Marcos Didonet del Fabro CURITIBA 2014 S237t Santos, Davi Azevedo de Queiroz Transformação de modelos independente de metamodelos usando reflexão / Davi Azevedo de Queiroz Santos. – Curitiba, 2014. 74f. : il. color. ; 30 cm. Dissertação (mestrado) - Universidade Federal do Paraná, Setor de Ciências Exatas, Programa de Pós-graduação em Informática, 2014. Orientador: Marcos Didonet del Fabro. Bibliografia: p. 60-65. 1. Engenharia de software. 2. Projeto de sistemas. 3. Simulação (Computadores). I. Universidade Federal do Paraná. II. Fabro, Marcos Didonet del. III. Título. CDD: 005.12 DAVI AZEVEDO DE QUEIROZ SANTOS TRANSFORMAÇÃO DE MODELOS INDEPENDENTE DE METAMODELOS USANDO REFLEXÃO Dissertação aprovada como requisito parcial à obtenção do grau de Mestre no Programa de Pós-Graduação em Informática da Universidade Federal do Paraná, pela Comisso formada pelos professores: Orientador: Prof. Dr. Marcos Didonet del Fabro DINF/UFPR Prof. Dr. Marco Aurélio Wehrmeister UTFPR - membro externo Prof. Dr. Fabiano Silva DINF/UFPR - membro interno Curitiba, 03 de abril de 2014 i AGRADECIMENTOS Ao professor e orientador Marcos Didonet del Fabro, pela orientação e suporte. Aos meus professores de graduação, Gustavo Augusto Lima de Campos e Jerffeson Texeira de Souza. À CAPES (Coordenação de Aperfeiçoamento de Pessoal de Nı́vel Superior) pela concessão da bolsa. Finalmente agradeço à minha famı́lia por tudo. ii SUMÁRIO LISTA DE FIGURAS v LISTA DE TABELAS vi RESUMO vii ABSTRACT viii 1 INTRODUÇÃO 1 1.1 Motivação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 1.2 Objetivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 1.3 Organização do texto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 2 REVISÃO BIBLIOGRÁFICA 7 2.1 Engenharia Dirigida Por Modelos . . . . . . . . . . . . . . . . . . . . . . . 7 2.1.1 Transformação de Modelos . . . . . . . . . . . . . . . . . . . . . . . 11 2.1.2 Classificação para Problemas de Transformação de Modelos . . . . . 14 2.1.3 Classificação para Linguagens de Transformação de Modelos . . . . 16 2.2 Linguagens e Ferramentas para a Transformação de Modelos . . . . . . . . 18 2.3 Eclipse Modeling Framework . . . . . . . . . . . . . . . . . . . . . . . . . . 21 2.4 Programação por Restrições . . . . . . . . . . . . . . . . . . . . . . . . . . 23 2.5 Framework Alloy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 3 MODELAGEM COMO BUSCA 27 3.1 Modelagem como Busca . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 3.2 Transformação como Busca . . . . . . . . . . . . . . . . . . . . . . . . . . 31 3.3 Trabalhos Relacionados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 iii 4 TRANSFORMAÇÃO DE MODELOS INDEPENDENTE DE METAMODELOS USANDO REFLEXÃO 39 4.1 Abordagem Independente de Resolvedor . . . . . . . . . . . . . . . . . . . 39 4.2 Instanciação com o Framework Alloy . . . . . . . . . . . . . . . . . . . . . 41 4.3 Transformação de Modelos Independente de Metamodelos usando Reflexão 5 EXPERIMENTOS 47 52 5.1 Linhas de Produto de Software . . . . . . . . . . . . . . . . . . . . . . . . 52 5.2 Transformação Classe para Relacional . . . . . . . . . . . . . . . . . . . . . 54 6 CONCLUSÕES E TRABALHOS FUTUROS 57 BIBLIOGRAFIA 60 A CÓDIGO DA TRANSFORMAÇÃO 4 66 iv LISTA DE FIGURAS 2.1 Hierarquia das siglas MBE, MDE, MDA e MDD [12] . . . . . . . . . . . . 7 2.2 Arquitetura de três nı́veis [19] . . . . . . . . . . . . . . . . . . . . . . . . . 9 2.3 Exemplos da arquitetura de três nı́veis [19] . . . . . . . . . . . . . . . . . . 10 2.4 Esquema base da transformação de modelos . . . . . . . . . . . . . . . . . 11 2.5 Exemplos de transformações de modelos [22] . . . . . . . . . . . . . . . . . 12 2.6 Metamodelos de entrada e saı́da do exemplo Livro para Publicações [21] . . 13 2.7 Exemplo de modelo de entrada da transformação e sua respectiva saı́da . . 13 2.8 Diagrama das principais classes do Ecore [56] . . . . . . . . . . . . . . . . 22 2.9 Exemplos de instâncias Alloy . . . . . . . . . . . . . . . . . . . . . . . . . 26 3.1 Modelagem como Busca [37] . . . . . . . . . . . . . . . . . . . . . . . . . . 28 3.2 Exemplo do metamodelo Livro . . . . . . . . . . . . . . . . . . . . . . . . . 29 3.3 Exemplo de instância de Livro . . . . . . . . . . . . . . . . . . . . . . . . . 29 3.4 Integração da modelagem como busca no espaço tecnológico MDE [37] . . 30 3.5 Transformação como busca . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 3.6 Metamodelo de transformacão para a transformacão como busca [38] . . . 34 3.7 Resultado da transformação do diagrama de classe para relacional . . . . . 35 3.8 Diagrama com as classes do SAPos . . . . . . . . . . . . . . . . . . . . . . 36 4.1 Abordagem Independente de Resolvedor . . . . . . . . . . . . . . . . . . . 40 4.2 Transformação da Solução . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 4.3 Metamodelo Alloy [37] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43 4.4 Metamodelo XML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 4.5 Metamodelo de Instâncias Alloy . . . . . . . . . . . . . . . . . . . . . . . . 45 4.6 Metamodelo ECore produzido pela transformação 3. . . . . . . . . . . . . . 47 4.7 Saı́da da Transformação 4 . . . . . . . . . . . . . . . . . . . . . . . . . . . 50 5.1 Metamodelo de Classe do estudo de caso SPL . . . . . . . . . . . . . . . . 53 v 5.2 Modelo produzido pela TIMeR para o estudo de caso SPL . . . . . . . . . 53 5.3 Metamodelo de Classe usado como entrada . . . . . . . . . . . . . . . . . . 54 5.4 Saı́da do estudo de caso Class2Relational . . . . . . . . . . . . . . . . . . . 55 5.5 Metamodelo relacional [22] . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 5.6 Saı́da do estudo de caso Class2Relational com a operação cut . . . . . . . . 56 vi LISTA DE TABELAS 2.1 Comparativo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 2.2 Comparativo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 3.1 Mapeamentos entre os elementos . . . . . . . . . . . . . . . . . . . . . . . 33 3.2 Tempos de Execução . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36 4.1 Mapeamento entre o metamodelo Alloy e ECore . . . . . . . . . . . . . . . 47 4.2 Principais classes e métodos do ECore usados na implementação . . . . . . 47 5.1 Tempo médio em segundos de cada etapa do estudo de caso SPL . . . . . . 53 5.2 Tamanho da entrada de cada etapa do estudo de caso SPL . . . . . . . . . 54 5.3 Tempo médio em segundos de cada etapa da cadeia para o estudo de caso Class2Relational . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56 5.4 Tamanho da entrada de cada etapa do estudo de caso Class2Relational . . 56 vii RESUMO A integração de técnicas de programação por restrições em plataformas de engenharia dirigida por modelos (MDE) tem como objetivo principal usar resolvedores de programação por restrições para execução de operações sobre modelos. As operações são chamadas operações de busca, pois buscam instâncias válidas de modelos baseada em um conjunto de restrições. Entretanto, as plataformas MDE existentes possuem formatos incompatı́veis com os formatos das soluções dos resolvedores atuais. Por isso é necessário desenvolver mecanismos de interoperabilidade. Existem diferentes abordagens para automatizar esta integração, normalmente baseada na execução de uma cadeia de transformações, sendo uma das mais conhecidas chamada “modelagem como busca”. Esta abordagem especifica quais operações são necessárias para, a partir de um modelo e metamodelo de entrada, produzir uma especificação no formato do resolvedor, e como transformar o resultado produzido por um resolvedor em um modelo e metamodelo. Entretanto, esta e as demais abordagens são baseadas em transformações de modelo que são implementadas sobre os elementos de metamodelos. Com isso, para cada metamodelo de saı́da, é necessário implementar uma transformação do formato do resolvedor para um modelo conforme a este metamodelo, tornando a abordagem inviável. Este trabalho propõe um conjunto de transformações para automatizar a cadeia de operações da abordagem de modelagem como busca. Dentre as diferentes transformações, damos destaque a transformação do modelo do formato do resolvedor para o metamodelo de saı́da. Esta transformação será desenvolvida sem conhecimento prévio dos elementos do metamodelo, estes serão interpretados durante a execução, sendo assim chamada de ”transformação independente de metamodelos”. Apresentaremos a formalização da cadeia de operações, e esta será implementada em dois casos de uso, mostrando sua factibilidade. Palavras-chave: engenharia dirigida por modelos, transformação de modelos viii ABSTRACT The integration of constraint programming techniques with model-driven engineering (MDE) platforms has as main objective to use constraint programming solvers to perform operations on models. These operations are called search operations, because they search valid model instances based on a set of constraints. However, the existing MDE platforms have incompatible formats with the formats of the solutions produced by the current solvers. Therefore it is necessary to develop mechanisms for interoperability. There are different approaches to automate that integration, usually based on the execution of a chain of transformations, one of the most known approach is called “model search”. This approach specifies what operations are needed to produce, from an input model and metamodel, an specification in the solver’s format, and how to turn the result produced by a solver into a model and metamodel. Yet, this and the other approaches are based on model transformations that are implemented over the elements of metamodels. Thus, for each output metamodel, it is necessary to implement a transformation of the solver’s format to a model conforming to this metamodel, making the approach difficult to implement. This work proposes a set of transformations to automate the chain of operations of the model search approach, focusing on the interoperability part. Among the different transformations, we highlight the model transformation from the solver’s format to the output metamodel. This transformation will be developed without prior knowledge of the metamodel elements, these are interpreted at runtime, thus being called ”metamodel independent transformation.”We will present the formalization of the chain of operations, and this chain will be implemented in two use cases, showing its feasibility. Keywords: model-driven engineering, model transformation 1 CAPÍTULO 1 INTRODUÇÃO A Engenharia Dirigida por Modelos (Model-Driven Engineering, MDE) [12] é uma abordagem de desenvolvimento de software onde modelos são artefatos de primeira ordem [37], e não são usados apenas como entrada para atividades de geração de código ou documentação. Isto diferencia esta abordagem de métodos como o RUP, onde modelos são usados essencialmente para documentação [40]. Um modelo é uma representação de um sistema, utilizado para um determinado objetivo, que tenta extrair algumas de suas caracterı́sticas de forma simplificada [12]. Temos como exemplo um modelo representando um sistema de gestão de controle acadêmico, onde um aluno é um elemento deste modelo que possui atributos como nome, idade, e relaciona-se com outros elementos como disciplina e histórico. Estes relacionamentos podem indicar que um aluno está matriculado em uma disciplina e possui um histórico com as notas das disciplinas cursadas. Os tipos dos elementos pertencentes a um modelo e seus relacionamentos são definidos por um modelo chamado metamodelo [19]. A relação de tipagem entre um modelo e seu metamodelo é chamada de conformidade [19]. Entretanto, metamodelos não possuem sempre capacidade de expressão adequada, pois definem informações essencialmente estruturais e é necessário incluir restrições mais especı́ficas [37]. Por exemplo, a idade de um aluno não pode ser negativa. Searchbased MDE (SBMDE) é uma área recente que estuda a integração de técnicas de MDE com programação por restrições [32]. As aplicações mais comuns de SBMDE são a verificação automática e manual de modelos UML [13, 27], Linhas de Produto de Software [15], e mais recentemente abordagens como JTL [14], Modelagem como Busca [37] e Transformação como Busca [38]. As abordagens de programação por restrições possuem resolvedores que, a partir de um conjunto de variáveis, restrições e valores de 2 entrada, tentam encontrar uma solução (conjunto de valores atribuı́dos às variáveis) que satisfaça todas as restrições especificadas [37]. As soluções de programação por restrições atuais não possuem integração completa com o MDE [14, 27], pois as soluções de cada resolvedor possuem formatos de representação e ferramentas especı́ficos e incompatı́veis. Isto dificulta a integração de ambas abordagens. Dizemos que a abordagem MDE e programação por restrições são definidas em espaços tecnológicos diferentes. Espaços tecnológicos representam o contexto para a especificação e representação dos modelos [12], como por exemplo: XML [58], Ecore [56], SQL [26], MOF [49] e soluções de programação por restrições. Cada espaço tecnológico possui sua própria terminologia, formalismo, capacidades e conjunto de ferramentas de suporte [42]. Por exemplo, o que é chamado de metamodelo no espaço tecnológico MDE corresponde a um esquema no espaço tecnológico do XML. É importante possuir técnicas para converter modelos de um espaço tecnológico para outro, para ser possı́vel aproveitar as capacidades de diferentes espaços tecnológicos. Nesta dissertação, usaremos o MDE como espaço tecnológico base. Para realizar operações de conversão entre modelos, usamos transformações de modelos [12]. Transformações de modelos recebem um ou mais modelos de entrada e produzem um ou mais modelos de saı́da [11]. Quando transformações de modelo são desenvolvidas, é necessário conhecer o metamodelo de entrada e o metamodelo de saı́da. Por exemplo, para transformar diagramas de classes em tabelas [22], devemos conhecer os elementos do metamodelo de classes e os elementos do metamodelo de tabelas. Transformações que convertem modelos entre diferentes espaços tecnológicos são chamadas de injetores ou extratores. Se considerarmos o MDE como espaço tecnológico de referência, um injetor é uma transformação que converte uma representação de um espaço tecnológico X para o espaço tecnológico MDE. Um extrator é a transformação inversa, do espaço tecnológico MDE para o espaço tecnológico X [12]. 3 1.1 Motivação Existem diferentes abordagens que permitem fazer a conexão entre o espaço tecnológico MDE e o espaço tecnológico de programação por restrições de forma automatizada, como por exemplo, a Modelagem como Busca [37]. A Modelagem como Busca (Model Search) é uma abordagem que usa programação por restrições para aplicar operações de busca de modelos válidos, isto é, dado um conjunto de elementos de um modelo, não conectados, como entrada, um resolvedor é utilizado para encontrar um ou vários modelos de saı́da válidos. Uma aplicação de exemplo são as linhas de produto de software. Uma extensão da modelagem como busca chamada Transformação como Busca (Transformation as Search, TAS) [38] é aplicada especificamente para a operação de transformação de modelos. O funcionamento da modelagem como busca consiste em encontrar um modelo de saı́da a partir do metamodelo alvo, suas restrições e um modelo parcial. Este modelo parcial é composto por atribuições iniciais de elementos e relacionamentos do modelo, que não precisam, necessariamente, satisfazer todas as restrições do metamodelo. Em outras palavras, um conjunto de elementos não conectados e não conforme ao metamodelo. O resolvedor, quando executado, pode produzir uma ou várias soluções, dependendo do modelo parcial e das restrições do metamodelo. Essa capacidade de produzir várias soluções para um mesmo conjunto de entrada é útil em diversos cenários, como em aplicações de linha de produto de software. A abordagem da modelagem como busca é formada por uma cadeia com cinco etapas [37]: 1. Transformação do metamodelo do problema em um modelo de programa de busca. 2. Transformação do modelo parcial em dados do programa de busca. Ao término destas duas etapas, temos o modelo do programa de busca, que contém os elementos do metamodelo, suas restrições e o modelo com as atribuições iniciais. 3. Extração do modelo do programa de busca do espaço tecnológico MDE para o espaço tecnológico do resolvedor. Por exemplo, um arquivo texto com o programa a ser 4 executado. 4. Execução do programa pelo resolvedor com a obtenção da sua solução. Esta fase é a execução da operação de busca. 5. Injeção desta solução no espaço tecnológico MDE, de forma a gerar um modelo em conformidade com o metamodelo de saı́da. Este metamodelo não precisa ser conhecido durante a especificação desta cadeia. Entretanto, não há abordagens atualmente que implementem toda esta cadeia de forma genérica, em especial a última etapa. Isto porque as ferramentas existentes de injeção e extração precisam conhecer o metamodelo de saı́da para que a transformação seja codificada. Isto é, é preciso escrever uma transformação do formato de saı́da do resolvedor para cada metamodelo de saı́da diferente. Porém, na modelagem como busca e na transformação como busca, isto deveria ser evitado, escrevendo apenas uma operação de extração de um espaço tecnológico para outro, sem ser dependente do metamodelo de saı́da para a sua especificação. Para evitar essa limitação, nesta dissertação apresentaremos uma solução de transformação de modelos independente de metamodelo para injeção da solução de um resolvedor no espaço tecnológico MDE. 1.2 Objetivos O objetivo deste trabalho é propor e implementar uma abordagem de injeção do espaço tecnológico do resolvedor para o espaço tecnológico MDE sem conhecer o metamodelo de saı́da durante sua implementação. O metamodelo de saı́da será um parâmetro de entrada que será interpretado durante a execução usando técnicas de reflexão. Esta transformação será escrita usando os elementos do metametamodelo. Por isso, sua implementação será independente do metamodelo de saı́da. A transformação será chamada de transformação independente de metamodelo. Implementaremos também transformações de suporte, no total de 4, para levar em conta a diferença de formatos entre o espaço tecnológico dos resolvedores e o espaço tecnológico MDE. 5 Para cumprir tal objetivo, foi proposta uma cadeia composta com as quatro transformações de modelos descritas abaixo: 1. Injeção do programa do resolvedor para um modelo de programa do resolvedor no espaço tecnológico MDE. O modelo do programa do resolvedor contém informações do metamodelo de saı́da e suas restrições; 2. Injeção da solução do programa do resolvedor no espaço tecnológico MDE. Esta solução contém os elementos do modelo de saı́da; 3. Transformação do modelo produzido pela primeira transformação no metamodelo de saı́da. Vale salientar que o metamodelo e o modelo do resolvedor são sempre os mesmos em uma execução. Os elementos variáveis são os metamodelos e modelos da solução (saı́da); 4. Transformação do modelo da solução do resolvedor em um modelo em conformidade com o metamodelo de saı́da. A implementação desta transformação será independente do metamodelo, isto é, não será necessário conhecer o metamodelo de saı́da (produzido por 3) durante sua implementação. Este será apenas interpretado durante a execução. Assim, quando a cadeia de modelagem como busca for executada, as soluções do resolvedor poderão ser injetadas no espaço tecnológico MDE de forma automática e com a especificação de uma única transformação na etapa 4, para qualquer metamodelo ou modelo de saı́da. Após esta implementação, o processo de validação e análise dos resultados foi realizado utilizando dois estudos de caso: linhas de produto de software [15] para a modelagem como busca e a transformação Class2Relational [22] para a transformação como busca. A validação foi feita comparando os elementos gerados com os esperados e com verificação do formato correto do modelo através da ferramenta do Eclipse. O resolvedor utilizado foi o Alloy Analyser [30]. Algumas ferramentas já existentes foram utilizadas como o injetor do programa Alloy de [37]. Para as outras transformações utilizou-se o ATL [2] e Java com o framework EMF [56] para suas implementações. 6 1.3 Organização do texto Este trabalho está organizado da seguinte forma: No próximo capı́tulo é realizada uma sı́ntese dos principais conceitos utilizados neste trabalho, como a engenharia dirigida por modelos, as ferramentas de transformação de modelos e programação por restrições. No capı́tulo três é apresentado a modelagem como busca e a transformação como busca, abordagens às quais este trabalho visa contribuir. No capı́tulo quatro é apresentado a solução proposta através de uma cadeia de transformações, onde a última é denominada transformação independente de metamodelos usado reflexão. No capı́tulo cinco são apresentados os resultados obtidos. Finalmente, no capı́tulo seis temos as conclusões e sugestões de trabalhos futuros. 7 CAPÍTULO 2 REVISÃO BIBLIOGRÁFICA Neste capı́tulo será realizada uma revisão bibliográfica dos conceitos usados nesta dissertação. Esta dissertação abrange conceitos de duas áreas, engenharia dirigida por modelos e programação por restrições, além da integração de ambas. Estes são os conceitos e ferramentas em que este trabalho se baseia. 2.1 Engenharia Dirigida Por Modelos A Engenharia Dirigida por Modelos (Model-Driven Engineering, MDE) [35] é uma abordagem que usa modelos como artefatos de primeira classe em todas as fases do ciclo de vida do processo de desenvolvimento de software. Recentemente em [12], tem-se utilizado o termo engenharia de software dirigida por modelos (Model-Driven Software Engineering - MDSE) para os usos do MDE dentro da Engenharia de Software. Há diferentes abordagens baseadas no MDE como Model-Driven Development (MDD) [12]; Model-Driven Architecture (MDA) [48], que é uma visão particular da MDD proposta pela Object Management Group (OMG); e Model-Based Engineering (MBE) [12]. A figura 2.1 ilustra a hierarquia dessas siglas. Como exemplos de MDE temos o Eclipse Modeling Framework [56], o Meta-Object Facility [49]. Um modelo pode ser definido como a representação de um sistema que possui duas Figura 2.1: Hierarquia das siglas MBE, MDE, MDA e MDD [12] 8 utilidades [12]: refletir uma parte das propriedades e entidades relevantes do sistema e representar o sistema de forma abstrata e generalizada. Um sistema é um grupo de elementos que interagem, relacionam-se, ou são independentes e que formam um todo [19]. Podemos usar um modelo para representar um sistema para um determinado objetivo. Os modelos podem ter diferentes propósitos [12]: podem ser descritivos (descrevem a realidade de um contexto ou sistema, como um esquema de dados), prescritivos (determinam o escopo e detalhes, como os diagramas UML) ou definem como um sistema será implementado. Os modelos podem ser classificados como estruturais (ou estáticos), que focam no aspecto da representação da estrutura e dados do sistema com o diagrama de classe da UML [53], ou comportamentais (ou dinâmicos), que enfatizam a sequência de ações e algoritmos, além de mostrar a colaboração entre componentes e mudanças de seus estados internos [12], como por exemplo os diagramas de estados e sequência da UML [53]. Um passo natural para a definição de modelos é especificar estes modelos como instâncias de outros modelos mais abstratos [12]. Estes modelos, chamados de metamodelos, definem as informações de tipagem e os relacionamentos possı́veis entre os elementos de um modelo [19], ou de acordo com [42], um metamodelo é um modelo de uma linguagem de modelagem. A relação entre o modelo e seu metamodelo é chamada de conformidade, isto é, um modelo está em conformidade com seu metamodelo. Esta relação é frequentemente indicada pela sigla c2 (conformsTo). Tomando como exemplo um documento XML [58] (modelo) e seu respectivo esquema XSD [59] (metamodelo), o esquema XSD especifica quais elementos e atributos o documento XML deve possuir. Da mesma forma que existe a relação entre um modelo e seu metamodelo, podemos ter a relação entre o metamodelo e outro modelo que especifica quais são os tipos e relacionamentos do metamodelo. Este modelo é chamado de metametamodelo. Um metametamodelo é um modelo que especifica a representação base para todos os modelos e metamodelos de um dado contexto. Um metamodelo está em conformidade consigo mesmo [19]. A seguir temos as definições destes termos de acordo com [19] e [38]: Definição 2.1 Um sistema é um grupo de elementos que se interagem, relacionam-se ou 9 conforme M3 Metametamodelo conforme M2 Metamodelo conforme Modelo M1 representa Sistema M0 Figura 2.2: Arquitetura de três nı́veis [19] são independentes, e que formam um todo. Definição 2.2 Um modelo é um artefato que representa um sistema. Um modelo M é uma tripla < G, ω, µ > onde G é um multigrafo rotulado acı́clico; ω (modelo de referência de M) é um outro modelo ou o mesmo modelo (uma auto-referência); µ é uma função que associa vértices e arestas do grafo G para os nós do grafo Gω (o grafo associado a seu modelo de referência ω). Definição 2.3 Um metamodelo é um modelo que define o tipo dos elementos e relacionamentos dos elementos de um modelo. Definição 2.4 Um metametamodelo é um modelo que especifica a representação base para todos os modelos e metamodelos de um dado contexto. Um metamodelo está em conformidade consigo mesmo. Definição 2.5 A relação entre um modelo e seu metamodelo é chamada de conformidade e é notada como conformsTo (ou abreviado como c2). A figura 2.2 mostra as relações entre sistema, modelo, metamodelo e metametamodelo. M3 é o metametamodelo. M2 é o metamodelo. M1 é o modelo que representa o sistema M0. Como o sistema está fora da fronteira dos modelos, temos apenas três nı́veis. Esta arquitetura de três nı́veis é suficiente para ser aplicada em diferentes contextos. Na figura 2.3 temos a ilustração de três exemplos onde esta arquitetura pode ser aplicada. 10 conforme M3 MOF conforme M2 Metamodelo UML conforme M1 Modelo UML conforme Definição XML conforme Esquema XSD conforme Documento XML conforme EBNF conforme Gramática Java conforme Código Java Figura 2.3: Exemplos da arquitetura de três nı́veis [19] O MOF (Meta-Object Facility) da OMG [49] é o metametamodelo que fornece uma base independente de plataforma para a MDA. Podemos definir um metamodelo de classe que está em conformidade com o MOF. Este metamodelo define os tipos e relacionamentos dos elementos de um diagrama de classe, como associações e herança. Em seguida temos o exemplo do XML [58], que também pode ser especificado em três nı́veis. O documento XML (modelo) está em conformidade com um esquema XSD [59] (metamodelo). Este esquema define a validade da forma de um documento XML. Por sua vez, o esquema XSD está em conformidade com a definição de um arquivo XML (metametamodelo). Também podemos representar um código de uma linguagem de programação qualquer. O código de um programa (modelo) está em conformidade com a gramática de uma linguagem de programação (metamodelo), e esta linguagem é definida pela notação EBNF [55] (metametamodelo). Todos os modelos e metamodelos do MDE são projetados seguindo essa arquitetura de três nı́veis [19]. Como temos diferentes formatos de representação, dizemos que cada forma de representação delimita um espaço tecnológico [12]. Um espaço tecnológico representa o contexto para a especificação e implementação de aplicações [12]. Eles possuem uma linguagem para especificar os metamodelos e formatos para armazenar estes modelos, além de um conjunto de ferramentas de suporte. Por exemplo, o espaço tecnológico do EMF [56] possui como metametamodelo o Ecore e usa XMI [50] como formato para armazenar os modelos. O MOF [49] possui um metametamodelo diferente, apesar de usar também 11 transforma Figura 2.4: Esquema base da transformação de modelos XMI como formato de armazenamento de seus modelos. Ligar estes diferentes espaços tecnológicos de forma uniforme é necessário [42], porque os espaços tecnológicos podem possuir capacidades e ferramentas diferentes. É preciso de uma operação que realize a integração e manipule estes modelos em espaços tecnológicos diferentes ou até mesmo dentro do mesmo espaço tecnológico. Estas operações são chamadas de transformações de modelos. 2.1.1 Transformação de Modelos Transformação de modelos é a principal operação do MDE e é definida usando modelos de transformação. Isto é, a própria transformação é definida por um modelo. Estes modelos definem o mapeamento entre os metamodelos de entrada e saı́da. Elas possuem diversas aplicações [18]: gerar modelos de baixo nı́vel (código), mapear, sincronizar e evoluir modelos, engenharia reversa, entre outras. A figura 2.4 mostra o esquema básico de uma operação de transformação de modelos. Neste exemplo, Ma é o modelo de entrada que é transformado para o modelo de saı́da Mb . Ma está em conformidade com MMa (metamodelo de Ma ) e Mb com MMb (metamodelo de Mb ), isso é indicado pelas setas c2. O modelo da transformação Mt está em conformidade com MMt (metamodelo de Mt ) e define as regras de transformação entre os metamodelos de entrada e saı́da. MMt é o modelo da linguagem de transformação. Mt contém as 12 operações que são executadas para transformar Ma em Mb . Todos os metamodelos estão em conformidade com o mesmo metametamodelo. A seguir temos uma definição para transformação de modelos conforme [19]: Definição 2.6 Transformação de modelo é uma operação que recebe um conjunto de modelos como entrada, visita os elementos destes modelos e produz um conjunto de modelos de saı́da. transforma Figura 2.5: Exemplos de transformações de modelos [22] Na figura 2.5, temos dois exemplos de transformações. A primeira consiste em transformar um diagrama de classe em um modelo relacional [22]. A segunda consiste na transformação de um modelo de livros para um modelo de publicações [21]. Temos na figura 2.6(a) o metamodelo de livros e na figura 2.6(b) o de publicações. O metamodelo de livros determina que a classe Livro possui os atributos tı́tulo e um conjunto de capı́tulos, e a classe Capı́tulo possui os atributos tı́tulo e número de páginas deste capı́tulo. O metamodelo de publicações contém uma única classe Publicação, que possui os atributos tı́tulo da publicação e número de páginas totais. A transformação consiste em transformar cada instância da classe Livro em uma instância da classe Publicação, sendo que o tı́tulo da publicação é o tı́tulo do livro e o número de páginas da publicação é a soma do número de páginas de todos os capı́tulos deste livro. Na figura 2.7 temos um exemplo de 13 (a) Metamodelo de Livros (b) Metamodelo de Publicações Figura 2.6: Metamodelos de entrada e saı́da do exemplo Livro para Publicações [21] (a) Modelo de Livros (entrada) (b) Modelo (saı́da) de Publicações Figura 2.7: Exemplo de modelo de entrada da transformação e sua respectiva saı́da modelo a ser transformado (figura 2.7(a)) e o resultado da transformação em um modelo de Publicações (figura 2.7(b)). Quando os modelos de entrada e saı́da estão em espaços tecnológicos diferentes, é possı́vel transformar estes modelos de um espaço tecnológico para outro [12]. Esta operação é denominada injeção ou extração [37]. • A injeção é uma transformação de um espaço tecnológico para o espaço tecnológico base (MDE). • A extração é uma transformação do espaço tecnológico base (MDE) para outro espaço tecnológico. Injetores e extratores podem ser implementados de várias formas, código nativo [2], Textual Concrete Syntax [34], ou usando ferramentas de transformação que realizem transformação modelo-para-texto [12]. 14 A seguir, temos um resumo da classificação dos problemas que envolvem as transformações e das ferramentas utilizadas para realizá-las feito por [11]. Czarnecki el al. [18], Mens et al. [43] e Huber [28] realizaram a classificação das principais linguagens e ferramentas utilizadas para a transformação de modelos. Biehl [11] sintetiza estas abordagens em dois tipos: classificação para problemas de transformação de modelos e classificação para Linguagens de Transformação de Modelos. 2.1.2 Classificação para Problemas de Transformação de Modelos Um problema de transformação de modelos é definido como um problema que deve ser resolvido usando transformação de modelos [11]. Logo, é preciso uma classificação da relação entre os modelos de entrada e saı́da. Esta relação possui as seguintes propriedades segundo [11]: • Mudança de Abstração: – O nı́vel de abstração é a medida da quantidade de detalhes de um modelo[11]. A transformação de modelos pode mudar o nı́vel de abstração inserindo ou removendo detalhes, ou mesmo não realizar nenhuma alteração. Esta propriedade é independente da mudança do metamodelo, isto é, os metamodelos de entrada e saı́da podem ser o mesmo ou diferentes. Há dois tipos: transformação vertical e transformação horizontal. – Uma transformação vertical muda o nı́vel de abstração. Se houver a inserção de detalhes no modelo, a transformação é chamada de transformação de refinamento [11]. – Uma transformação horizontal muda a representação do modelo, mas não muda seu nı́vel de abstração. Este tipo de transformação produz o modelo de saı́da em um metamodelo diferente e mantendo o mesmo nı́vel de abstração [43]. • Mudança de Metamodelos: 15 – Se os metamodelos de entrada e saı́da são os mesmos, temos uma transformação endógena. Se são diferentes, a transformação é chamada exógena [18]. • Espaços Técnológicos Suportados: – Como foi visto, modelos podem estar em espaços tecnológicos diferentes. Os espaços tecnológicos limitam quais ferramentas de transformação podem ser usadas. • Número de Modelos Suportados: – O número de modelos de entrada e saı́da também é uma caracterı́stica importante de uma transformação. A maioria das transformações envolve dois modelos diferentes, um de entrada e outro de saı́da. Também é possı́vel ter vários modelos de entrada e saı́da. – Quando o modelo de entrada e saı́da é o mesmo, a transformação é chamada transformação in-place. Neste caso, o modelo de saı́da é criado modificando algumas partes do modelo de entrada. • Tipo do Modelo de Saı́da Suportado: – Podemos diferenciar a transformação de modelos de acordo com o tipo do modelo de saı́da. Uma transformação modelo-para-modelo (model-to-model, M2M) cria um novo modelo a partir do modelo de entrada. Elementos do modelo de entrada são mapeados em elementos do modelo de saı́da. Uma transformação modelo-para-texto (model-to-text, M2T) cria texto ou código. Logo, os elementos do modelo de entrada são mapeados em fragmentos de texto ou código. • Preservação de Propriedades: – A transformação pode ou não preservar algumas propriedades. Há três tipos: ∗ Preservação Semântica: os modelos de entrada e saı́da são os idênticos, mesmo estando em espaços tecnológicos diferentes. As computações podem 16 ser diferentes, mas os resultados produzidos devem ser iguais [11]. Por exemplo, a refatoração de um modelo. ∗ Preservação Comportamental: As restrições (implı́citas e explı́citas) do modelo de entrada são mantidas no modelo de saı́da [11]. ∗ Preservação de Sintaxe: Os modelos de entrada e saı́da possuem a mesma sintaxe [11]. Este tipo é comum em uma transformação endógena horizontal. 2.1.3 Classificação para Linguagens de Transformação de Modelos Biehl [11], baseado nos trabalhos [18] e [28], propôs as seguintes caracterı́sticas para as linguagens de transformação de modelos: • Paradigma: – Há diferentes paradigmas para as linguagens de transformação de modelos [11] e [18]: ∗ Imperativo/Operacional: Especificam o fluxo sequencial e descrevem como a transformação deve ser executada, isto é, a transformação é descrita como uma sequência de ações em uma linguagem imperativa [18], como Java ou C#. ∗ Declarativo/Relacional: Elas não explicitam o controle de fluxo. Apenas indicam como a transformação deve ser executada, focando em o quê deve ser mapeado pela transformação. Como descrevem o relacionamento entre os metamodelos de entrada e saı́da, este relacionamento pode ser bidirecional [11]. Como exemplo, temos a ATL [33]. ∗ Hı́brido: Oferece as abordagens imperativa e declarativa, deixando a escolha para o usuário [11]. ∗ Transformação de Grafos: Triple Graph Grammars (TGG) [54] são uma forma de descrever transformações de grafos. É um subconjunto do tipo 17 declarativo, onde modelos de entrada e saı́da são interpretados como grafos e há um terceiro grafo que descreve o mapeamento entre os elementos da entrada e saı́da. ∗ Baseado em Templates: É usada em transformações modelo-para-texto. Para cada elemento do metamodelo de saı́da, há um fragmento de texto correspondente [18]. ∗ Manipulação Direta: É a aplicação de linguagem de programação de propósito geral para implementar a transformação [18]. Por exemplo, usar Java para produzir um documento XML. • Agendamento das Regras: – O agendamento das regras determina a ordem em que cada regra é a aplicada [18]. Uma linguagem de transformação pode fornecer mecanismos para determinar de forma explı́cita quando e onde uma determinada regra deve ser aplicada [11]. • Organização das Regras: – Em uma transformação com muitas regras, pode ser interessante a sua organização, isto é, agrupar estas regras de forma que possam ser compostas e/ou reutilizáveis (modularizadas). Assim, com o reuso é possı́vel combinar regras simples para construir regras mais complexas [11]. • Rastreabilidade: – A execução de uma transformação pode gerar um registro do mapeamento realizado entre os elementos. Esta funcionalidade pode estar embutida na ferramenta ou implementada como parte da descrição da transformação [11]. • Direção: – O mapeamento entre os modelos de entrada e saı́da por ser unidirecional, isto é, as regras de transformação só podem ser aplicadas no modelo de entrada; 18 ou multidirecional, quando as regras podem ser aplicadas tanto do modelo de entrada para o modelo de saı́da, como também do modelo de saı́da para o modelo de entrada. Neste caso, se há apenas um modelo de entrada e outro de saı́da, a transformação é bidirecional [18]. • Transformação Incremental: – Uma transformação é incremental quando as mudanças no modelo de entrada se propagam para o modelo de saı́da. Por outro lado, uma transformação não-incremental recria o modelo de saı́da [11]. • Representação da Transformação: – As transformações podem ser representadas como texto ou como um modelo. Se são modelos, elas podem ser manipuladas por transformações de modelos [11]. 2.2 Linguagens e Ferramentas para a Transformação de Modelos A seguir apresentamos a listagem das principais ferramentas e linguagens usadas para a transformação de modelos. EMF Henshin [9] é uma linguagem que suporta transformações modelo-para-modelo endógenas e exógenas. É baseada no Eclipse Modeling Framework (EMF) [56] e a transformação é representada por Triple Graph Grammars (TGG). Contudo, não há suporte para rastreabilidade, multidirecionalidade ou transformação incremental. Atlas Transformation Language (ATL) [33] é uma linguagem hı́brida (imperativa e declarativa, esta última a preferı́vel) para a transformações modelo-para-modelo. Suporta apenas transformações unidirecionais e não incrementais. É integrada com a IDE Eclipse, oferece suporte dedicado a rastreamento, a ordem de execução das regras é determinada automaticamente na maioria dos casos. OpenArchitectureWare (OAW) [24] é um conjunto de boas práticas e processos de MDE integrados no Eclipse. Possui uma linguagem imperativa e baseada em templates 19 chamada Xpand [20] para transformações modelo-para-texto. Query/View/Transform (QVT) [51, 41] é uma especificação da OMG (Object Managment Group) para uma linguagem capaz de expressar consultas, visões e transformações sobre modelos. É divida em três sub-linguagens que são imperativas e declarativas para transformações modelo-para-modelo: • QVT Relational é uma linguagem declarativa de alto nı́vel. Suporta a especificação de transformações bidirecionais; modelos de rastreabilidade são criados implicitamente. • QVT Core é uma linguagem declarativa de baixo nı́vel e serve como base para a QVT Relational. • QVT Operational é uma linguagem imperativa que estende a QVT Relational. As transformações são unidirecionais e usam modelos de rastreamento implı́citos. SmartQVT [5] é uma implementação de um motor de transformação para a linguagem QVT Operational. É uma linguagem imperativa para transformações modelo-para-modelo para modelos do EMF. Não suporta transformações incrementais nem multidirecionalidade. Kermeta [45, 52] é uma linguagem de modelagem imperativa e propósito geral capaz de realizar transformações modelo-para-modelo. Não há suporte para multidirecionalidade, rastreabilidade, nem para transformações incrementais. Epsilon Transformation Language (ETL) [39] é uma linguagem hı́brida de transformação modelo-para-modelo, e pode tratar vários modelos de entrada e saı́da. Oferece a funcionalidade do agendamento da aplicação das regras e as regras podem ser reutilizadas e herdadas. VIATRA2 [17] é um framework com uma linguagem hı́brida baseada em transformação de grafos que suporta transformações modelo-para-modelo e modelo-para-texto. Também suporta transformações incrementais. Janus Transformation Language (JTL) [14] é uma linguagem de transformação declarativa modelo-para-modelo que realiza transformações bidirecionais. Possui suporte a 20 transformações incrementais e rastreabilidade. A Transformação como Busca (Transformation as Search, TAS) [38] é uma extensão da Busca de modelos [37] que realiza transformações exógenas modelo-para-modelo usando uma linguagem declarativa. Possui suporte a rastreabilidade, incrementabilidade e transformações bidirecionais. Nas tabelas 2.1 e 2.2 temos o resumo das caracterı́sticas mais importantes de cada linguagem. Ferramenta EMF Henshin OpenArchitectureWare ATL QVT SmartQVT Kermeta ETL VIATRA2 JTL TAS Tipo do Modelo de Saı́da Modelo-para-modelo Modelo-para-texto Modelo-para-modelo Modelo-para-modelo Modelo-para-modelo Modelo-para-modelo Modelo-para-modelo Modelo-para-modelo Modelo-para-texto Modelo-para-modelo Modelo-para-modelo Paradigma Transformação de Grafo Imperativa e Baseada em Templates Hı́brida Hı́brida Imperativa Imperativa Hı́brida Hı́brida Declarativa Declarativa Tabela 2.1: Comparativo Ferramenta Direcionalidade EMF Henshin Unidirecional OpenArchitectureWare Unidirecional ATL Unidirecional QVT Unidirecional e Bidirecional SmartQVT Unidirecional Kermeta Unidirecional ETL Multidirecional VIATRA2 Unidirecional JTL Bidirecional TAS Bidirecional Rastreabilidade Não Sim Sim Sim Sim Não Sim Sim Sim Sim Incremental Não Não Não Sim Não Não Não Sim Sim Sim Tabela 2.2: Comparativo Podemos ver que a maioria das abordagens são modelo-para-modelo, usam linguagens imperativas e são unidirecionais. Poucas possuem suporte a incrementabilidade. Ainda é preciso evoluir bastante nessa área, já que há a necessidade de transformações bidirecionais e incrementais, porque durante o desenvolvimento os modelos de entrada e saı́da 21 são modificados continuadamente [14]. Logo, nenhumas das abordagens atuais sozinhas podem ser utilizadas para todos os tipos de problemas, elas sempre são combinadas entre si para obter o resultado desejado. Neste trabalho, onde é proposto uma cadeia de transformações para realizar última etapa da cadeia da busca de modelos (e consequentemente da transformação como busca), a ATL foi escolhida como ferramenta auxiliar a definição e execução de transformações entre modelos, devido a sua fácil integração com a IDE do Eclipse e com framework de modelagem do Eclipse (Eclipse Modeling Framework, EMF), além de ser possı́vel executála diretamente dentro de um código java. 2.3 Eclipse Modeling Framework O Eclipse Modeling Framework (EMF) [56] é um framework de modelagem e geração de código para construir ferramentas e outros aplicativos baseados em modelo. Este framework permite manipular o metametamodelo Ecore usado nesta dissertação. O EMF consiste de dois frameworks básicos: Core e EMF.Edit. O Core fornece uma API para a geração e manipulação de modelos, além de fornecer ferramentas para produzir código Java a partir de um modelo. O EMF.Edit estende o Core, adicionando suporte para gerar classes adaptadoras, visualizadores e editores de modelos. O EMF foi inicialmente uma implementação do Meta-Object Facility (MOF) [49], que é o padrão da Object Management Group (OMG) para o MDE, mas acabou ganhando importância própria, e pode ser visto uma implementação Java de um subconjunto da API MOF [56]. O EMF é fornecido como um plugin para a IDE Eclipse e usa XMI (XML Metadata Interchange) para armazenar (serializar) os modelos. Ecore é o metametamodelo fornecido pelo EMF. Ele tem quatro tipos de classes como principais elementos [56] (figura 2.8): • EClass: é usado para modelar as classes. São identificadas por nome e possuem atributos, referências. Também há suporte para herança. 22 Figura 2.8: Diagrama das principais classes do Ecore [56] • EAttribute: modela atributos. São identificados por nome e possuem um tipo (um EDataType). • EDataType: é usado para representar tipos simples, cujos detalhes não são modelados em classes. Também identificados por um nome e são associados a tipos primitivos das linguagens de programação. • EReference: é usado para modelar associações entre classes. Também são identificadas por um nome e também possuem um tipo (uma EClass). Uma referência também possui multiplicidade. Estes tipos são usados para modelar as caracterı́sticas estruturais dos modelos. Para modelar as caracterı́sticas comportamentais, a classe EOperation é usada. No entanto, o Ecore apenas modela a interface destas operações comportamentais, porém é possı́vel especificar implementações em outras linguagens usando anotações [56]. Além de fornecer ferramentas para criar e manipular os modelos Ecore dentro da IDE do Eclipse, é possı́vel fazer a manipulação direta destes modelos utilizando a linguagem Java. Entre as vantagens de realizar esta manipulação em nı́vel de metametamodelo estão: maior flexibilidade, capacidade de manipulação dos modelos e metamodelos em tempo execução e utilização da API reflexiva [56]. Esta API reflexiva permite que programas examinem as informações sobre a estrutura dos modelos e metamodelos que estão sendo manipulados em tempo de execução, com é isso possı́vel criar modelos dinamicamente e criar novas ferramentas. 23 2.4 Programação por Restrições Nesta seção apresentaremos os conceitos de programação por restrições usados nesta dissertação. Programação por restrições (Constraint Programming , CP) [32] é um paradigma de programação declarativo usado para resolver problemas combinatórios [37]. A programação por restrições consiste na especificação de um conjunto de restrições sobre um conjunto de variáveis com um tipo de dado. Em seguida, um programa chamado de resolvedor analisa esta especificação e tenta encontrar uma atribuição de valores válida (valoração) para estas variáveis, satisfazendo as restrições de entrada. O resolvedor pode encontrar zero ou mais valorações (soluções de um problema de programação por restrições). Cada variável possui um tipo e um domı́nio, isto é, um conjunto de valores possı́veis para uma variável [37]. As restrições sobre um conjunto de variáveis são um subconjunto do produto cartesiano dos domı́nios destas variáveis [7]. O conjunto de valores de todas as variáveis formam a solução, e este conjunto de valores é encontrado por resolvedores. Um exemplo de problema resolvido por programação por restrições é determinar se uma fórmula booleana é satisfeita ou não, também conhecido como problema SAT [16]. Uma fórmula booleana é a ligação de um conjunto variáveis com domı́nio binário (verdadeiro ou falso) por operadores lógicos (conjunção, disjunção e negação) [8]. Por exemplo, tendo as variáveis A e B, uma fórmula para a conjunção da negação destas variáveis pode ser escrita como ¬A ∧ ¬B. A programação por restrições não apenas verifica a satisfabilidade, como também encontra os valores possı́veis que tornam a fórmula verdadeira. O paradigma SAT possui algumas limitações: o domı́nio das variáveis possui apenas dois valores e fornece uma linguagem de baixo nı́vel para predicados (apenas negação, conjunção e disjunção). Há uma grande variedade de resolvedores SAT eficientes: MiniSat [23], SAT4J [10], Chaff [46] e outros. Estendendo o problema SAT para variáveis com domı́nios não apenas binários, chegamos aos problemas de satisfação de restrições (Constraint Satisfaction Problems, CSP) [37]. 24 Há diversos resolvedores para CSP, a maioria são baseados em Prolog [57], uma linguagem de programação lógica ou realizam redução ao SAT. A seguir temos uma lista de alguns resolvedores utilizados no contexto do MDE: • ECLiPSe [8] é uma ferramenta de código aberto baseada em Prolog, logo as restrições são expressas como predicados lógicos. Possui uma API para ser integrada em aplicações java. R R é uma ferramenta proprietária para proCPLEX CP Optimizer [1] • IBM ILOG gramação por restrições. Possui uma linguagem de modelagem e resolvedores bastante robustos. • Alloy [30] foi desenvolvido pelo MIT e, apesar de usar resolvedores SAT, possui uma linguagem relacional bastante expressiva. Esta linguagem permite a expressão de predicados usando conjuntos, relações e quantificadores. O Alloy pode utilizar diferentes resolvedores SAT, além de poder ser facilmente integrada em aplicações Java. Entre estes resolvedores, o ILOG CPLEX CP Optimizer possui a desvantagem de ser uma ferramenta proprietária e paga. O ECLiPSe e o Alloy já foram utilizados no contexto do MDE em [13], [38]. Neste trabalho o Alloy foi escolhido, pois uma implementação atual da modelagem como busca já o utiliza, está disponı́vel em código aberto, tem ferramentas e documentação de suporte, e sua especificação pode ser traduzida para diferentes resolvedores. 2.5 Framework Alloy O framework Alloy [30] é uma ferramenta de código aberto desenvolvida pelo MIT, e seu propósito é analisar programas escritos pela linguagem de modelagem Alloy utilizando o Alloy Analyzer. A linguagem Alloy é baseada na lógica de predicados, e pode ser usada para representar modelos e suas restrições [44]. Os programas escritos na linguagem Alloy são modelos for- 25 mados por estruturas chamadas assinaturas (signatures), que são declarações de conjunto de átomos (atoms), e fatos (facts). Átomos são entidades primitivas, indivisı́veis, imutáveis e não possuem propriedades [31]. A linguagem permite a especificar propriedades através da definição de relações entre átomos [31]. Estas relações são especificadas na linguagem por meio de campos (fields) dentro das assinaturas. Fatos são declarações de restrições sobre átomos ou assinaturas. Esta restrições podem ser predicados, limite de cardinalidade, ou operações sobre conjuntos [37]. Código 2.1: Exemplo de código Alloy 1 2 3 4 5 6 7 8 9 sig E s t o q u e { itens : set Item } sig Item { c o d i g o P r o d u t o : one Int , n o m e P r o d u t o : one String , q u a n t i d a d e : one Int } fact { all y : Item | y . q u a n t i d a d e >= 0 } Estes conceitos são exemplificados na listagem de código 2.1 com um sistema de controle de estoque. Um estoque é formado por um conjunto de itens. As entidades são descritas por assinaturas. Cada item é descrito por uma assinatura que contém a relação de três átomos. O primeiro átomo é o código do produto, o segundo é nome do produto e o terceiro é quantidade deste produto no estoque. Na linha 9 é definida uma restrição que exclui os valores negativos do domı́nio do campo quantidade. O Alloy Analyzer funciona como um resolvedor de programação por restrições [44]. Ele recebe como entrada um programa Alloy, e produz uma instância. Esta instância é uma valoração das variáveis do programa que satisfazem as restrições, ou seja, é a solução encontrada pelo resolvedor SAT [29]. Se o programa permite mais de uma solução, o Alloy Analyzer é capaz de encontrá-la. A figura 2.9 ilustra duas instâncias para o exemplo do sistema de controle de estoque. Na figura 2.9(a), temos o átomo ’Estoque’ relacionado com dois átomos do tipo Item (’Item1’ e ’Item2’) através do campo itens. Cada uma destas relações forma uma tupla. 26 (a) Instância com dois itens (b) Instância com três itens Figura 2.9: Exemplos de instâncias Alloy Cada átomo do tipo Item possui três campos: código, nome do produto e quantidade. Na figura 2.9(b), temos uma instância similar com três átomos do tipo Item. Internamente, o Alloy Analyzer traduz o programa Alloy em uma fórmula booleana, e um resolvedor SAT é utilizado para encontrar uma ou mais valorações desta fórmula. Cada valoração é uma solução, que é traduzida automaticamente para a linguagem do programa Alloy. Uma caracterı́stica do Alloy Analyazer é restringir o espaço da busca através de um escopo, isto é, uma restrição no espaço de busca. Como esse escopo é limitado, o resolvedor nem sempre encontra uma instância, mas isso significa apenas que não foi possı́vel encontrar a solução dentro daquele escopo. Porém, as instâncias encontradas são seguramente válidas [44]. Também é oferecido um suporte a rastreamento, assim é possı́vel realizar análises e simulações podem ser aplicadas as instâncias encontradas [31]. Finalmente, as instâncias encontradas pelo Alloy Analyzer são armazenadas como documentos XML [29]. Para as instâncias serem utilizada no espaço tecnológico MDE, é preciso injetar este documento XML num modelo. 27 CAPÍTULO 3 MODELAGEM COMO BUSCA Neste capı́tulo apresentaremos primeiramente a abordagem chamada “modelagem como busca”, que integra técnicas de programação por restrições com execução de operações MDE. Em seguida, apresentaremos uma extensão do MAS (Model as Seach), especı́fica para execução de transformação de modelos. Finalmente, apresentaremos uma implementação que realizamos para avaliar a cadeia de TAS. 3.1 Modelagem como Busca A Modelagem como Busca (Model Search) [37] é uma abordagem que usa programação por restrições para aplicar operações sobre modelos. A natureza dos resolvedores de programação por restrições permite, dado um conjunto de elementos do modelo não conectados como entrada, encontrar um ou mais modelos de saı́da que satisfazem a estrutura do metamodelo e um conjunto de restrições. Esta possibilidade de encontrar múltiplas soluções é um diferencial em relação as abordagens de transformação apresentadas na seção anterior. Os metamodelos podem conter restrições embutidas (metamodelos com restrições). Um metamodelo com restrições é definido por um metamodelo e um conjunto de restrições que um determinado modelo deve satisfazer. Quando este modelo satisfaz a todas as restrições dizemos que está em conformidade com as restrições. A figura 3.1 ilustra uma operação de modelagem como busca. Esta operação consiste em encontrar um modelo (M) a partir de outro modelo entrada Mr que está parcialmente em conformidade com um metamodelo (CMM). Este modelo Mr é chamado de modelo parcial, e está em conformidade com o relaxamento das restrições do metamodelo CMM. Este relaxamento consiste em remover restrições e elementos (cardinalidade, opcionais, etc) do metamodelo original, criando assim o chamado metamodelo relaxado 28 CMMr . Quando este modelo parcial Mr está em conformidade com o metamodelo relaxado CMMr , dizemos que o modelo parcial está em conformidade parcial (p-conformsTo) com o metamodelo com restrições CMM. Finalmente, tendo o modelo parcial, o metamodelo relaxado e o metamodelo com restrições, a modelagem como busca é a operação que procura encontrar um modelo (finito) a partir do modelo parcial que esteja em conformidade com o metamodelo com restrições. O conceito de metamodelo parcial foi introduzido nesta abordagem para poder definir o conjunto de elementos não conectados que uma operação de modelagem como busca recebe como entrada. Isto se deve ao fato de que esta abordagem precisa de um “pool” de elementos de entrada, isto é, não é uma abordagem generativa. Figura 3.1: Modelagem como Busca [37] A modelagem como busca é considerada uma transformação de modelos onde o modelo de entrada (modelo parcial Mr e o metamodelo com restrições CMM) é uma instância de um problema combinatório, e o modelo de saı́da M é uma solução deste problema (se existir). Do ponto de vista da programação por restrições, o metamodelo CMM age como uma fórmula, enquanto que o modelo parcial é uma atribuição parcial que precisa ser completada para a fórmula ser verdadeira [37]. Para exemplificar a modelagem como busca, utilizaremos novamente o metamodelo de Livros [21] (figura 3.2(b)). Um modelo está em conformidade com este metamodelo com restrições quando um Livro possui zero ou mais capı́tulos. Ao relaxar este meta- 29 (a) Metamodelo de Livros relaxado (b) Metamodelo de Livros com restrições Figura 3.2: Exemplo do metamodelo Livro (a) Modelo parcial (b) Modelo encontrado Figura 3.3: Exemplo de instância de Livro modelo, podemos excluir a referência entre Livro e Capı́tulo, de forma que os elementos do modelo parcial sejam desconexos. Com isso, temos o metamodelo relaxado na figura 3.2(a). O modelo parcial pode ser qualquer modelo que esteja em conformidade com este metamodelo relaxado. Pode ser um modelo vazio ou um modelo que possui apenas uma instância da classe Livro e duas instâncias da classe Capı́tulo que não estão associadas a nenhuma instância da classe Livro (figura 3.3(a)). Quando um desses modelos parciais é usado como entrada pela modelagem como busca, o resolvedor realiza atribuições que tornem esse modelo parcial em conformidade com as restrições e com o metamodelo nãorelaxado [37]. No segundo exemplo de modelo parcial, o resolvedor adiciona as instâncias das classes Capı́tulo à instância da classe Livro 3.3(b). Em [37] é apresentado um cadeia para a automatização e integração da modelagem como busca ao espaço tecnológico MDE. A figura 3.4 ilustra essa cadeia e a divide em cinco tarefas. Os metamodelos CMM, CMMr , Search Engine, SE Solution e XML estão no 30 ECore Figura 3.4: Integração da modelagem como busca no espaço tecnológico MDE [37] espaço tecnológico MDE. Os metamodelos Search Engine e SE Solution são os metamodelo usados para representar o programa e a solução do resolvedor no espaço tecnológico MDE. O metamodelo XML é o metamodelo para documentos XML dentro do espaço tecnológico MDE. Os outros metamodelos estão fora do espaço tecnológico MDE. SE.g representa a gramática do programa do resolvedor e XML Format é o esquema do documento XML onde está a solução do resolvedor. A seguir temos uma descrição de cada etapa da cadeia: 1. Geração do Problema de Busca (CMM2SP): esta tarefa gera o modelo do problema de programação por restrições a partir do metamodelo de entrada e suas restrições (CMM). 2. Geração dos Dados de Busca (M2SP): gera os dados de entrada do programa de programação por restrições a partir do modelo parcial Mr . Esta transformação é especı́fica para cada metamodelo de entrada. 3. Extração do Programa: converte o modelo do problema de programação por restrições para um formato executável pelo resolvedor usado. 4. Busca da Programação por Restrições: o programa gerado nos passos anteriores é executado, e uma ou mais soluções são encontradas no formato da saı́da do resolvedor. 31 5. Injeção da Solução (XML2SS, SS2M): Injeta a solução encontrada pelo resolvedor em um modelo em conformidade com o metamodelo CMM. Esta etapa consiste em escrever uma transformação para injetar a solução encontrada no formato da saı́da do resolvedor em um modelo do espaço tecnológico base. A modelagem como busca também pode utilizar um metamodelo de saı́da diferente do de entrada, neste caso é usada para transformações exógenas, e esta abordagem recebe o nome de transformação como busca, que será apresentada a seguir. 3.2 Transformação como Busca A Transformação como Busca (Transformation As Search, TAS) [38] é uma abordagem, baseada na modelagem como busca, para definir e executar transformações de modelo bidirecionais. A diferença principal em relação a modelagem como busca é a execução de transformações exógenas. Isto impacta na especificação das restrições sobre o metamodelo da solução, pois teremos um metamodelo fonte e um metamodelo destino, e as restrições deverão ser escritas entre os dois metamodelos. Estas restrições podem ser consideradas correspondências entre metamodelos. Como se baseia na modelagem como busca (é independente de resolvedores), não é preciso definir uma linguagem especı́fica, assim qualquer ferramenta de programação por restrições pode ser usada. A abordagem fornece suporte para transformações modelo-para-modelo, verticais e horizontais, endógenas e exógenas, bidirecionais, incrementais, rastreáveis, mas sem suporte a agendamento e controle de aplicação das regras. Estas caracterı́sticas são possı́veis devido às expressividade das linguagens declarativas usadas pelos resolvedores. Porém algumas delas, como a bidirecionalidade, dependem de como é realizada a implementação da transformação. A figura 3.5 ilustra o funcionamento da cadeia de transformação como busca, numa transformação exógena unidirecional. M A é o modelo de entrada que está em conformidade com o metamodelo de entrada CMM A , M B é o modelo de saı́da em conformidade com o metamodelo CMM B . MrT é o modelo parcial para a modelagem como busca e consiste numa cópia do modelo de entrada. M T é a solução encontrada pela modelagem 32 ECore c2 Figura 3.5: Transformação como busca como busca, CMM T é metamodelo de transformação (unificação dos metamodelos de origem e saı́da e seus elos) e CMMrT é o metamodelo relaxado de CMM T . Depois de encontrada a solução pela modelagem como busca, há um último passo chamado ’cut’ que extrai o modelo desejado. Este passo é necessário porque o resultado da modelagem como busca possui tanto o modelo de entrada como o de saı́da. A seguir, apresentaremos um caso de uso de transformação como busca para aplicação e teste da cadeia completa 1 . Foi implementada a transformação de modelos ClassToRelational [22], que consiste em transformar um modelo de classes em um relacional. Esta transformação e usada em diversos trabalhos como ilustração de problemas de transformações. A figura 3.6 ilustra o metamodelo da transformação usado em [38]. Este metamodelo ilustra a correspondência (weaving links) entre os metamodelos de entrada e saı́da. Nesta implementação foi utilizado como resolvedor o framework Alloy/SAT. A tabela 3.1 ilustra o mapeamento entre os elementos dos metamodelos. Não há um mapeamento 1:1 entre o elemento Table do metamodelo relacional e um elemento do metamodelo de classe e sim um mapeamento disjunto entre tabelas, atributos multivalorados e classes. Isto é, há uma tabela para uma classe ou um atributo multivalorado. Isso é realizado pela associação wnamed entre AttributeClassAndTable e NamedElt. Porém, existe uma restrição que especifica que os elementos dessa associação só podem ser classes 1 Publicado em [38] 33 ou atributos multivalorados. Há uma equivalência entre atributos e colunas, eles possuem estruturas muito semelhantes, mas possuem algumas propriedades diferentes (como a indicação de chave de uma tabela para o modelo relacional). Um caso diferente são os atributos multivalorados de uma classe, pois eles estão associados a uma tabela e a uma coluna. Já os tipos de dados do metamodelo relacional e de classe possuem a mesma estrutura, então seu mapeamento é direto. Há também casos onde um elemento de um metamodelo não possui correspondência com algum elemento do outro metamodelo. Neste caso, o metamodelo relacional possui chaves, porém o metamodelo de classes não possui esta informação, e não há correspondência e essa informação fica contida apenas no modelo relacional. Uma implementação da transformação como busca pode ser dividida em dois passos: 1. Criação do metamodelo de transformação: identificar as correspondências, com suas ligações e elementos; 2. Adição de restrições: adicionar restrições que tornem os modelos corretos. No primeiro passo nem sempre este mapeamento é bijetor (1:1). É preciso identificar os casos onde há uma relação 1:N, N:1 e M:N entre os elementos dos metamodelos. No segundo passo é preciso adicionar as restrições que tornem corretos o mapeamento e os modelos de entrada e saı́da que serão produzidos. Estes dois passos correspondem a unificação dos metamodelos da transformação como busca. No exemplo ClassToRelational, os nomes de atributos, classes, tabelas, colunas, tipos devem se corresponder de forma adequada. Elementos do Metamodelo de Classe Class Attribute (monovalorado) Attribute (multivalorado) DataType Elementos do Metamodelo Relacional Table Column Column e Table Type Tabela 3.1: Mapeamentos entre os elementos A seguir temos a listagem de algumas restrições da transformação usando a OCL+ [3] 34 Figura 3.6: Metamodelo de transformacão para a transformacão como busca [38] (uma extensão da linguagem OCL [47]). Estas restrições estabelecem a correspondência entre os nomes de tipos, classes, atributos multivalorados e tabelas. A injeção destes metamodelos e suas restrições num programa de busca representam o final da tarefa Extração do Programa. Nesse ponto, o resolvedor é executado (isto é, é executada a busca da programação por restrições), e uma solução é encontrada. A figura 3.7 apresenta a entrada e o resultado da transformação de um modelo de classe para o modelo relacional. O modelo utilizado consiste em duas classes (Family e Person) com atributos monovalorados e mutivalorados. Os atributos multivalorados e as classes foram mapeados corretamente para tabelas. Da mesma forma, os atributos monovalorados foram mapeados corretamente. Como podemos ver na tabela 3.2, o um ponto negativo da implementação foi o tempo de execução. Os modelos utilizados possuem poucos elementos (apenas duas classes e cinco atributos). Foi realizado um experimento com uma entrada maior e extraı́da de um sistema real (Sistema de Apoio da Pós-graduacão - SAPos) [4] (figura 3.8). Esta entrada possui 18 classes. Os experimentos mostraram que para essa entrada o framework Alloy traduziu o problema para um problema booleano com uma explosão de variáveis. O 35 Restrições OCL+ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 1 - c o n t e x t dtt : D a t a T y p e A n d T y p e inv : dtt . w d a t a t y p e . name = dtt . wtype . name 2 - c o n t e x t act : A t t r i b u t e C l a s s A n d T a b l e inv : act . wnamed . name = act . w t a b l e N T. name 3 - c o n t e x t act : A t t r i b u t e C l a s s A n d T a b l e inv : act . wnamed . i n c l u d e s( named : N a m e d E l t | named . o c l I s T y p e O f ( Class ) or ( named . o c l I s T y p e O f ( A t t r i b u t e) and named . m u l t i V a l u e d ) ) ) 4 - c o n t e x t mvac : M o n o V a l u e d A t t r i b u t e s A n d C o l u m n inv : mvac . m o n o V a l A t t r . forAll ( attr : A t t r i b u t e | not attr . m u l t i V a l u e d iff mvac . m o n o V a l A t t r. i n c l u d i n g( attr ) and mvac . m o n o V a l C o l. one ( col | col . name = attr . name and col . keyOf . o c l I s U n d e f i n e d () and col . towner . name = attr . cowner . name and if D a t a T y p e. one ( dt | attr . ctype . name = dt . name ) then col . ttype . name = attr . ctype . name else col . ttype . name = ’ Integer ’) Figura 3.7: Resultado da transformação do diagrama de classe para relacional 36 Cenário-versão Classe para Relacional Relacional para Classe Incremental Número de Variáveis Booleanas 7179 5725 5448 Tempo de Execução (ms) 2483 1655 666 Tabela 3.2: Tempos de Execução resolvedor minisat foi utilizado nestes experimentos. Figura 3.8: Diagrama com as classes do SAPos Após esta etapa, a solução encontrada deve ser injetada no espaço tecnológico MDE. Numa abordagem clássica é preciso escrever uma transformação de modelos T1 que injete a solução encontrada pelo resolvedor em um modelo em conformidade com o metamodelo relacional. Porém esta transformação T1 não serve para injetar a solução num modelo em conformidade com o metamodelo de Classe ou com o metamodelo de Livros, por exemplo. Seria preciso escrever transformação T2 para o metamodelo de Classe e uma transformação T3 para o metamodelo de livros. Neste caso, não é desejável escrever uma injeção diferente para cada metamodelo de saı́da diferente. Automatizar esta injeção para 37 metamodelos de saı́da diferentes traria benefı́cios para o uso da modelagem como busca e transformação como busca. É preciso de uma implementação genérica para a injeção da solução da cadeia da modelagem como busca. As soluções encontradas pelo resolvedor estão em formato XML. A injeção da solução é crucial para a praticidade da modelagem como busca, e consequentemente da transformação como busca. 3.3 Trabalhos Relacionados Em [27] os autores apresentam uma ferramenta para verificação de modelos UML com restrições especificadas com a linguagem OCL [47]. Há suporte para os diagramas de classe, objeto e sequência da UML, mas é preciso escrever um código para realizar a validação. Este código consiste em consultas que são executadas por um resolvedor OCL. Este resolvedor é usado como um biblioteca que retorna o resultado da consulta em formato de texto, que é exibido na interface do programa. A injeção da solução do resolvedor desta abordagem é sempre para um formato já conhecido. A abordagem [13] tem o mesmo objetivo, mas com um escopo mais reduzido (apenas diagramas de classe). Fornece uma verificação automática. Neste caso o ECLIPSe é utilizado como resolvedor. Esta abordagem transforma o modelo UML de entrada e suas restrições em um programa de busca, e o resolvedor tenta encontrar uma atribuição (ou mais de uma) que satisfaça todas as restrições. Se todas as restrições são satisfeitas, um modelo que representa uma instância válida daquele diagrama de classe é encontrado. Se este modelo não é encontrado, significa que a especificação do modelo de entrada e restrições está errada. A solução encontrada pelo resolvedor está em um grafo armazenado em formato textual. A ferramenta GraphViz [25] é utilizada para gerar apenas uma visualização desta solução. Em nenhuma dessas duas abordagens, o objetivo é produzir um modelo usável a partir da solução do resolvedor. Os modelos encontrados estão em formato próprio, e não há uma transformação deles em modelo Ecore, por exemplo. Em [14] é apresentado uma abordagem chamada Janus Transformation Languagem 38 (JTL), que possui funcionamento similar à transformação como busca. É uma linguagem de transformação de modelos baseado em resolvedores de programação por restrições. Porém, o modelo e metamodelos de entrada precisam ser codificados usando uma linguagem própria da ferramenta, porque não há uma extração automática dos metamodelos ECore para a linguagem usada pelo motor de transformação. Nesta abordagem a injeção da solução é realizada por uma transformação que recebe o metamodelo de saı́da e gera uma outra transformação que transforma a saı́da do resolvedor em um modelo em conformidade com o metamodelo de saı́da. A modelagem como busca tem como objetivo realizar estas extrações e injeções de forma automática e transparente. Por exemplo no caso de linhas de produto de software, o metamodelo, restrições e modelo parcial estão no espaço tecnológico base. Estes modelos e restrições são extraı́dos para o espaço tecnológico do resolvedor de forma automática e a solução do resolvedor é injetada no espaço tecnológico base também de forma automática em conformidade com o metamodelo de entrada. No próximo capı́tulo é apresentada a abordagem utilizada para extrair a solução encontrada pelo resolvedor e injetá-la como um modelo usável no espaço tecnológico MDE escrevendo apenas uma única transformação. 39 CAPÍTULO 4 TRANSFORMAÇÃO DE MODELOS INDEPENDENTE DE METAMODELOS USANDO REFLEXÃO Neste capı́tulo, apresentaremos uma abordagem para a injeção da solução produzida pela operação de modelagemo como busca em um modelo no espaço tecnológico MDE, usando apenas uma transformação para todos os metamodelos de saı́da. Esta abordagem é composta por quatro transformações. As três primeiras operações são relativas a interoperabilidade entre espaços tecnológicos. A quarta transformação da abordagem é o ponto chave, denominada Transformação de Modelos Independente de Metamodelos usando Reflexão (TIMeR). Seu objetivo é implementar uma transformação que seja independente do metamodelo de saı́da, isto é, um modelo de entrada é transformado em um modelo de saı́da em conformidade com um metamodelo que é desconhecido na fase de implementação. Primeiramente será apresentada uma abordagem independente de resolvedor em seguida sua instanciação com o framework Alloy. 4.1 Abordagem Independente de Resolvedor Como foi apresentado, a primeira etapa da cadeia da modelagem como busca (CMM2SP) é transformar o metamodelo e suas restrições em um programa de busca, e a última etapa (SS2M) é transformar o modelo da solução encontrada pelo resolvedor em um modelo em conformidade com o metamodelo CMM. Portanto, para realizar a transformação do modelo da solução, precisamos de duas etapas (figura 4.1): 1. Consiste no passo reverso da CMM2SP, isto é, o metamodelo CMM é extraı́do do programa de busca em um metamodelo no espaço tecnológico MDE (transformação SP2MM). 40 2. Consiste na execução da TIMeR, que recebe como entrada o metamodelo CMM produzido em (1) e o modelo com a solução do resolvedor. c2 ECore Figura 4.1: Abordagem Independente de Resolvedor A transformação SP2MM pode ser implementada com qualquer linguagem de transformação. Na transformação SP2MM as restrições não precisam ser transformadas, basta os elementos do metamodelo, em seguida podemos apenas importar as restrições do problema original. As soluções encontradas pelo resolvedor já satisfazem as restrições originais. O programa de busca, que contém o modelo parcial, restrições e metamodelo de saı́da, está em outro espaço tecnológico, geralmente em formato de texto da linguagem do resolvedor. Por isso, dependendo do resolvedor utilizado é preciso realizar uma injeção prévia. A TIMeR possui duas entradas: a solução do resolvedor e o metamodelo de saı́da. Cada elemento da solução do resolvedor é processado, e então é criado um elemento no modelo de saı́da, se houver uma correspondência entre o elemento do metamodelo de saı́da e o elemento do programa de busca. A seguir temos a definição da Transformação de Modelos Independente de Metamodelos usando Reflexão: 41 Definição 4.1 Definição da Transformação de Modelos Independente de Metamodelos usando Reflexão: Seja, MI =< GI , MMI , µI >, MMI =< Ga , MMM, µa > e MMO =< Gb , MMM, µb >, onde MI é o modelo de entrada a ser transformado. MI está conforme MMI , e MMO é o metamodelo de saı́da. A Transformação de Modelos Independente de Metamodelos usando Reflexão cria um modelo MO =< GO , MMO , µO >, tal que MO está conforme MMO e ∃x∃y|((x ∈ GI ∧ y ∈ Gb ∧ y := µI (x)) ↔ (∃z|z ∈ MO ∧ µO (z) := µI (x))) Onde := é a operação reflexiva que retorna verdadeiro se os termos a direita e esquerda são correspondentes (na maioria dos casos, se possuem o mesmo nome). Analisando o último termo da definição, podemos ver que um elemento do modelo de saı́da (z) corresponde a um elemento do modelo de entrada (x), se e somente se, o elemento do metamodelo de entrada (que está atrelado a x) possui alguma correspondência com o metamodelo de saı́da. Um fator importante é a definição da operação “:=”. Nos exemplos testados considerouse que se os elementos possuem o mesmo nome (label), então eles são similares. Outras definições podem aumentar a expressividade desta operação, mas ao custo de aumentar sua complexidade de projeto e implementação. A seguir veremos mais detalhes de como isso é feito na implementação da Injeção da Solução usando o framework Alloy. 4.2 Instanciação com o Framework Alloy Como o programa Alloy gerado e suas respectivas soluções estão em outro espaço tecnológico, é preciso injetá-los no espaço tecnológico MDE, para que sejam compatı́veis com o ECore. Para realizar isso, temos duas transformações (injeções) adicionais às apresentadas na seção anterior. Para evitar a reimplementação de soluções já existentes explicitamos quais passos desta cadeia de transformações são reutilizados ou então implementados por este trabalho. Como podemos ver na figura 4.2 ao todo temos quatro transformações: 42 Solução Solução Alloy Alloy (XML) (XML) Figura 4.2: Transformação da Solução 1. Injeção do Programa Alloy em um Modelo Alloy, implementado em [37]; 2. Injeção da Instância Alloy em um Modelo de Instâncias Alloy; 3. Transformação do Modelo de Programa Alloy em um metamodelo ECore; 4. Transformação de Modelos Independente de Metamodelos usando Reflexão. Também se pode notar pelos retângulos triplos que podemos ter diferentes modelos e metamodelos de entrada mantendo as mesmas transformações. A primeira transformação é injeção do programa Alloy em um modelo de programa Alloy. Nesta etapa foi utilizado o metamodelo de programa Alloy (figura 4.3) e o TCS Parser Generator criados em [37], que permite a injeção/extração entre a versão textual e o modelo de programa Alloy. Este TCS foi disponibilizado publicamente como um plugin do eclipse em [36]. Para ilustrar o funcionamento de cada etapa, foi escolhido o estudo de caso de linhas de produto de software para a modelagem como busca utilizado em [37]. Este estudo de caso 43 Figura 4.3: Metamodelo Alloy [37] ilustra os possı́veis métodos que uma classe que simula o funcionamento de um relógio deve possuir. Esta classe pode possuir os métodos: Start, Stop, DisplayTime, StartAlarm, StartVisualAlarm, StopAlarm, StartSoundAlarm. As restrições limitam quais combinações de métodos são possı́veis. Por exemplo, um relógio deve possuir os métodos Start e DisplayTime e se existir o método StartAlarm, o método StopAlarm também deve estar presente. Abaixo, temos um programa Alloy produzindo pela terceira etapa da cadeia da modelagem como busca. Este programa Alloy contém o metamodelo de saı́da, restrições e o modelo parcial, e é usado como entrada na Transformação 1. Esta transformação injeta esse código num modelo de programa Alloy em conformidade com o metamodelo da figura 4.3. Código 4.1: Programa Alloy - Entrada para a Transformação 1 1 2 3 4 5 6 7 module watch // M e t a m o d e l o sig Root { c l a s s i f i e r s : set Class } a b s t r a c t sig Class { m e t h o d s : set Method 44 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 } a b s t r a c t sig Method { } // Modelo p a r c i a l sig Watch e x t e n d s Class {} one sig Start e x t e n d s Method {} one sig Stop e x t e n d s Method {} one sig S t a r t A l a r m e x t e n d s Method {} one sig D i s p l a y T i m e e x t e n d s Method {} one sig S t a r t V i s u a l A l a r m e x t e n d s Method {} one sig S t o p A l a r m e x t e n d s Method {} one sig S t a r t S o u n d A l a r m e x t e n d s Method {} // R e s t r i ç ~ oes fact { one Root , all r : Root | # r . c l a s s i f i e r s = 1 } fact { all c : Class | c in Watch } fact { all c : Class | one m : Start | m in c . m e t h o d s } fact { all c : Class | one m : D i s p l a y T i m e | m in c . m e t h o d s } fact { all c : Class | Start in c . m e t h o d s = > Stop in c . m e t h o d s } fact { all c : Class | S t a r t A l a r m in c . m e t h o d s <= > S t o p A l a r m in c .←֓ methods } fact { all c : Class | S t a r t S o u n d A l a r m in c . m e t h o d s = > S t a r t A l a r m in c←֓ . methods } fact { all c : Class | S t a r t V i s u a l A l a r m in c . m e t h o d s = > S t a r t A l a r m in ←֓ c. methods } Figura 4.4: Metamodelo XML O segundo passo é injetar a solução encontrada pelo framework Alloy em um modelo de instâncias Alloy. Como esta solução é um documento XML, este passo é subdivido em duas etapas: 1. Injeção em um modelo XML, usando um metamodelo XML já existente em [22] (Figura 4.4). Esta injeção é feita através do injetor XML do AtlanMod MegaModel Management (AM3) [6]. 2. Posteriormente, este modelo XML é transformado usando ATL para um modelo em conformidade com o metamodelo de instâncias Alloy (figura 4.5). O metamodelo 45 Figura 4.5: Metamodelo de Instâncias Alloy de instâncias Alloy foi criado neste trabalho baseado nas especificações do esquema XSD do XML da instância do Alloy, o que permite capturar as soluções. Esta transformação foi implementada usando ATL e associa cada elemento do documento XML com seu respectivo elemento do metamodelo de instâncias Alloy. A seguir temos um exemplo de instância (solução) gerada pelo Alloy que é utilizada como entrada desta transformação. Esta instância contém um relógio com três métodos: Start, Stop e DisplayTime (linhas 19-21). Código 4.2: Solução do Resolvedor - Entrada para a Transformação 2 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 < alloy b u i l d d a t e ="2012 -09 -25 15:54 EDT " > < i n s t a n c e b i t w i d t h ="4" maxseq ="4" c o m m a n d =" Run e x a m p l e for 1 Root , 1 ←֓ Class , 7 Method " f i l e n a m e =" C :\ U n t i t l e d 1. als " > < sig label =" seq / Int " ID ="0" p a r e n t I D ="1" b u i l t i n =" yes " > </ sig > < sig label =" Int " ID ="1" p a r e n t I D ="2" b u i l t i n =" yes " > </ sig > < sig label =" String " ID ="3" p a r e n t I D ="2" b u i l t i n =" yes " > </ sig > < sig label =" this / Root " ID ="4" p a r e n t I D ="2" > < atom label =" Root$0 "/ > </ sig > < field label =" c l a s s i f i e r s " ID ="5" p a r e n t I D ="4" > < tuple > < atom label =" Root$0 "/ > < atom label =" W a t c h $ 0"/ > </ tuple > < types > < type ID ="4"/ > < type ID ="6"/ > </ types > </ field > < sig label =" this / Watch " ID ="7" p a r e n t I D ="6" > < atom label =" W a t c h $ 0 "/ > </ sig > < sig label =" this / Class " ID ="6" p a r e n t I D ="2" a b s t r a c t =" yes " > </ sig > < field label =" m e t h o d s" ID ="8" p a r e n t I D ="6" > 46 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 < tuple > < atom label =" W a t c h $ 0"/ > < atom label =" S t a r t $ 0 "/ > </ tuple > < tuple > < atom label =" W a t c h $ 0"/ > < atom label =" Stop$0 "/ > </ tuple > < tuple > < atom label =" W a t c h $ 0"/ > < atom label =" D i s p l a y T i m e $ 0 "/ > </←֓ tuple > < types > < type ID ="6"/ > < type ID ="9"/ > </ types > </ field > < sig label =" this / Start " ID ="10" p a r e n t I D ="9" one =" yes " > < atom label =" S t a r t $ 0 "/ > </ sig > < sig label =" this / Stop " ID ="11" p a r e n t I D ="9" one =" yes " > < atom label =" Stop$0 "/ > </ sig > < sig label =" this / S t a r t A l a r m" ID ="12" p a r e n t I D ="9" one =" yes " > < atom label =" S t a r t A l a r m $ 0 "/ > </ sig > < sig label =" this / D i s p l a y T i m e " ID ="13" p a r e n t I D ="9" one =" yes " > < atom label =" D i s p l a y T i m e $ 0 "/ > </ sig > < sig label =" this / S t a r t V i s u a l A l a r m " ID ="14" p a r e n t I D ="9" one =" yes " > < atom label =" S t a r t V i s u a l A l a r m $ 0 "/ > </ sig > < sig label =" this / S t o p A l a r m" ID ="15" p a r e n t I D ="9" one =" yes " > < atom label =" S t o p A l a r m $ 0 "/ > </ sig > < sig label =" this / S t a r t S o u n d A l a r m " ID ="16" p a r e n t I D ="9" one =" yes " > < atom label =" S t a r t S o u n d A l a r m $ 0 "/ > </ sig > < sig label =" this / Method " ID ="9" p a r e n t I D ="2" a b s t r a c t =" yes " > </ sig > < sig label =" univ " ID ="2" b u i l t i n =" yes " > </ sig > </ instance > </ alloy > $ A terceira transformação utiliza como entrada o resultado da transformação 1 e cria um metamodelo ECore. Os mapeamentos desta transformação estão resumidos na tabela 4.1. Basicamente as assinaturas (signatures) do programa Alloy são mapeadas em EClass do ECore, e os campos (fields) são mapeados em EReference (quando uma assinatura faz referência a outra assinatura) ou EAttribute, quando as assinatura possui campos de tipos primitivos. As cardinalidades foram implementadas de acordo com a referência da linguagem Alloy [44]. Na figura 4.6 temos o resultado dessa transformação para o exemplo. Comparando com a entrada da Transformação 1 (Código 4.1), podemos ver que o resultado é correto e o eclipse verifica corretamente que o modelo é bem formado. Finalmente temos o quarto passo: a Transformação de Modelos Independente de Metamodelos usando Reflexão. 47 Elementos do Metamodelo de Alloy Module Signatures Field Field ExternalType Elementos do ECore EPackage EClass EReference EAttribute EDataType Tabela 4.1: Mapeamento entre o metamodelo Alloy e ECore Figura 4.6: Metamodelo ECore produzido pela transformação 3. 4.3 Transformação de Modelos Independente de Metamodelos usando Reflexão Nesta etapa os resultados das transformações 2 e 3 (modelo com a solução do resolvedor e o metamodelo de saı́da) são utilizados como entrada para produzir um modelo que está em conformidade com o metamodelo de saı́da. Esta transformação foi implementada em java devido a sua fácil integração com o framework Alloy, e utiliza a API do EMF. Em especial os métodos de reflexão, que permite conhecer e manipular os elementos dos modelos carregados em tempo de execução. Esta transformação (para o caso do framework Alloy) é genérica e produz um modelo para qualquer metamodelo de saı́da, pois utiliza a API reflexiva do EMF para obter as informações necessárias para criar o modelo de saı́da. A Tabela 4.2 lista as principais classes e métodos reflexivos utilizados na implementação. Classes EObject, EStructuralFeature, EClass, EReference Métodos getName(), eGet(), getEType() Tabela 4.2: Principais classes e métodos do ECore usados na implementação Esta transformação se baseia no fato de que os metamodelos de entrada e saı́da pos- 48 suem alguma relação entre eles, determinada pelo operador :=. A relação entre modelo e metamodelo permite identificar quais elementos do modelo estão ligados aos elementos do metamodelo de saı́da. Com isso, o metamodelo de instâncias Alloy serve como uma ponte entre as soluções encontradas e o metamodelo de saı́da gerado a partir do programa Alloy que gerou essa solução. As soluções são apenas átomos (atoms) (gerados a partir de assinaturas) e as relações entre eles (campos). Dada a definição de modelo M =< G, MM, µ >, os átomos são vértices do grafo G do modelo, os campos representam as arestas deste grafo. A relação entre átomo e assinatura é dada pela função µ (isto é, se A é um átomo e S é uma assinatura µ(A) = S), esta relação é dada pela capacidade de rastreamento fornecida pelo resolvedor e pela reflexão. Por exemplo, na figura 4.5 temos as classes Atom e Sig, que representam átomos e assinaturas, respectivamente. A associação ’type’ entre Atom e Sig fornece a informação de restreamento, indicado a qual assinatura (classe) pertence este átomo (instância da classe). Neste caso a função µ é definida como µ(A) = A.type. Com isso, podemos criar uma relação entre átomos e campos para obter qualquer modelo de saı́da desejado. Note que para outros resolvedores, basta usar esta informação de rastreamento para auxiliar a definição da ligação dos elementos da solução com as instâncias dos elementos do metamodelo do problema (isto é, o operador :=). Portanto a rastreabilidade da solução do resolvedor é um dos requisitos desta abordagem. O apêndice A lista o código java utilizado nessa transformação, e a seguir (algoritmo 4.1)temos o pseudocódigo do núcleo dessa transformação: Os atributos label, type, owner, ref, origin estão definidos no metamodelo de instâncias da figura 4.5, e correspondem, respectivamente, a: nome do elemento, assinatura a qual o átomo pertence, assinatura a qual o campo pertence, átomo de destino do campo e átomo de origem do campo. A linha 1 cria um modelo de saı́da vazio em conformidade com o metamodelo de saı́da apresentado à TIMeR. As linhas 2-3 indicam que para cada átomo da solução é criada uma instância da classe que possui o nome A.type.label. A.type representa a assinatura de origem deste 49 Algoritmo 4.1 Pseudocódigo da TIMeR TIMeR (Metamodelo De Saı́da MM, Modelo de Instâncias Alloy MI) 1: ModeloSaı́da = new ModeloComforme(MM); 2: for each Átomo A in MI do 3: C’ = criaInstânciaDaClasse(pegaClassePeloNome(A.type.label)); 4: ModeloSaida.AdicionaObjeto(C’); 5: end for each 6: for each Campo C in MI do 7: R = pegaReferênciaPeloNome(C.label); 8: if R != nulo then 9: ModeloSaı́da.pegaObjetoPeloNome(C.origin.label).criaInstânciaDaReferência(R, C.ref.label); 10: end if 11: T = pegaAtributoPeloNome(C.label); 12: if T != nulo then 13: ModeloSaı́da.pegaObjetoPeloNome(C.origin.label).criaInstânciaDoAtributo(T, C.ref); 14: end if 15: end for each 16: return ModeloSaı́da átomo, é uma informação de rastreamento da solução do resolvedor. Esta assinatura está atrelada a um elemento do metamodelo de saı́da porque, a função “:=” foi definida como a igualdade do atributo label, e este atributo é obtido através da API de reflexão do EMF. A linha 4 adiciona esta instância ao modelo. Tendo criados todas as instâncias necessárias, basta agora adicionar atributos e referências entre as classes. Na linha 5 cada campo da solução e iterado e é realizada a criação dos atributos e referências necessários. Nas linhas 7-8, a função pegaReferênciaPeloNome busca no conjunto de referências do metamodelo se existe uma que possui o nome C.label. Caso afirmativo, na linha 9, esta referência é adicionada ao objeto da solução que possui o nome C.origin.label para o objeto C.ref.label. Nas linhas 11-12, a função pegaAtributoPeloNome, de modo similar, retorna o atributo do metamodelo que possui o nome C.label. Caso exista este atributo, na linha 13, a instância do atributo é adicionada ao objeto da solução que possui o nome C.origin.label e valor C.ref. O tipo do atributo é inferido através da reflexão, onde a classe do metamodelo com nome C.owner.label possui o atributo de nome C.label. Para tratar casos onde classes 50 diferentes possuem atributos com o mesmo nome, é usado o valor de C.owner.label para obter a classe correta através da reflexão. Finalmente, na linha 16 o modelo completo é retornado. Na figura 4.7 temos o resultado da transformação 4. Um modelo que está em conformidade com o metamodelo produzido pela transformação 3. Mais uma vez, este modelo foi corretamente verificado pelo Eclipse. Nesta saı́da o relógio produzido possui os métodos Start, Stop e DisplayTime. Figura 4.7: Saı́da da Transformação 4 Finalmente, com estas quatro transformações combinadas, temos um único código para injetar a solução de qualquer programa Alloy no espaço tecnológico MDE. Porém, esta abordagem possui suposições que introduzem algumas restrições: (1) a implementação depende do resolvedor utilizado; (2) apenas os aspectos estruturais dos modelos foram considerados. Para utilizar outro resolvedor, como por exemplo o ECLiPSe, é preciso reimplementar as quatro etapas. Para a transformação 1 é preciso definir um novo metamodelo do programa ECLiPSe e implementar o seu respectivo injetor. Para a transformação 2 é preciso criar um metamodelo de instâncias e seu respectivo injetor. Para a transformação 3 é preciso reimplementar a transformação utilizando o metamodelo criado na transformação 1. Finalmente, código da TIMeR deve ser reescrito utilizando o metamodelo de instâncias utilizado na transformação 2. O metamodelo de instâncias do resolvedor é fixo porque depende do resolvedor utilizado. O código da transformação usa os elementos deste metamodelo para fazer a ponte entre o modelo da solução do resolvedor e o modelo de saı́da. Por exemplo, um campo da 51 solução Alloy possui um átomo de partida e outro de destino. A transformação usa essa informação para criar referências entre as classes correspondentes a esses átomos. No caso da injeção da solução, e onde apenas o metamodelo de saı́da é diferente, esta abordagem é muito boa, temos uma única transformação para diversos metamodelos de saı́da. A implementação atual considera apenas os aspectos estruturais dos modelos. Para suportar modelos comportamentais é preciso estender a abordagem atual e considerar o elemento EOperation do Ecore. Esta abordagem é diferente da JTL porque não é necessário criar uma transformação especı́fica em tempo de execução para cada metamodelo de saı́da. A seguir temos os resultados obtidos com um estudo de caso de linha de produto de software e a transformação de modelo de classe para relacional. 52 CAPÍTULO 5 EXPERIMENTOS Neste capı́tulo apresentaremos os resultados da implementação utilizando dois estudos de caso. O primeiro estudo de caso é a Linhas de Produto de Software (Software Product Lines, SPL) [15] para a modelagem como busca, e o segundo estudo de caso é a transformação Class2Relational [22] para a transformação como busca. Nestes dois estudos de caso, apenas os dois arquivos de entrada (programa Alloy e solução Alloy) foram modificados. O código das transformações usado em todas as fases da cadeia é o mesmo. Os arquivos com as soluções foram gerados pelo Alloy usando o resolvedor SAT4J [10]. As quatro transformações foram executadas em sequência. Em seguida, o modelo de saı́da produzido foi verificado empiricamente usando as ferramentas da IDE Eclipse. Esta verificação consiste em determinar se o modelo está em conformidade com o metamodelo e se a saı́da está de acordo com a solução produzida pelo resolvedor. O computador usado em todos os testes possui a seguinte configuração: Processador: Intel Core i5 2.8 GHz com 8GB de memória RAM com o sistema operacional Windows 7 64-bits. A versão utilizada do Java foi a 1.7.1 32-bits. 5.1 Linhas de Produto de Software As linhas de produto de software permitem criar um conjunto de sistemas de software similares a partir de uma especificação comum a todos estes sistemas [37]. O primeiro é definir um modelo para o domı́nio compartilhado (especificação do sistema, suas caracterı́sticas, componentes e pontos de variação). No contexto da modelagem como busca, este modelo de domı́nio compartilhado é o metamodelo com restrições. O exemplo do SPL utilizado é o mesmo de [37]. O metamodelo utilizado neste estudo de caso (figura 5.3) permite definir classes, e estas classes possuem métodos. O objetivo é gerar classes que simulam a execução de um relógio. No caso, há cinco possı́veis tipos 53 de relógios a serem produzidos: 1) simples; 2) com alarme; 3) com alarme sonoro; 4) com alarme sonoro e visual; e 5) com alarme visual. Estas combinações são determinadas pelas restrições do metamodelo. Estas restrições estão no código 4.1 do capı́tulo anterior. Figura 5.1: Metamodelo de Classe do estudo de caso SPL Abaixo temos o resultado da cadeia de transformações: um documento XML produzido corretamente utilizando a TIMeR. Graficamente este modelo é exibido na figura 5.2. A solução encontrada pelo Alloy possui os métodos Start, Stop e DisplayTime. Da mesma forma, podemos ver que no modelo gerado Watch possui referências aos métodos Start, Stop e DisplayTime. Para as outras soluções os resultados foram similares. Figura 5.2: Modelo produzido pela TIMeR para o estudo de caso SPL Etapa Transformação Transformação Transformação Transformação Total 1 2 3 4 Tempo Médio 0,0952 2,5007 0,9516 0,6792 4,2267 Tabela 5.1: Tempo médio em segundos de cada etapa do estudo de caso SPL Na tabela 5.1 temos os tempos de execução em segundos de cada etapa da cadeia. A 54 Entrada Programa Alloy Instância Alloy Tamanho 11 assinaturas, 2 campos 15 assinaturas, 9 átomos, 2 campos, 4 tuplas Tabela 5.2: Tamanho da entrada de cada etapa do estudo de caso SPL cadeia foi executada 20 vezes e inclui o tempo de serialização e deserialização dos modelos em cada etapa. O tamanho das entradas é dado na tabela 5.2. 5.2 Transformação Classe para Relacional O segundo estudo de caso testado foi o primeiro cenário da transformação como busca em [38], isto é, a transformação Class2Relational. O objetivo desta transformação é transformar um modelo de classe em um modelo relacional. A figura 4.1 ilustra o modelo de classe utilizado como entrada para a transformação como busca. Figura 5.3: Metamodelo de Classe usado como entrada Mais uma vez, o programa Alloy e sua respectiva solução são utilizados como entrada para a cadeia de transformações. Na figura 5.4 temos o modelo gerado e verificado pelo Eclipse. Neste caso a transformação como busca produz como saı́da os dois modelos, o de classe e o relacional, pois a TIMeR recebeu como entrada um metamodelo que continha a unificação dos metamodelos de entrada e saı́da. Como vimos anteriormente, na transformação como busca há um passo posterior chamado ’cut’ que extrai desta solução o modelo de interesse. Também é possı́vel obter apenas a solução do modelo relacional se o metamodelo de saı́da apresentado à TIMeR for apenas o metamodelo relacional. A figura 55 Figura 5.4: Saı́da do estudo de caso Class2Relational Figura 5.5: Metamodelo relacional [22] 5.6 ilustra este exemplo como o metamodelo de saı́da na figura 5.5 apresentado à TIMeR e o modelo produzido na figura 5.6. Neste caso o arquivo de entrada da solução foi o mesmo. Na tabela 5.3 temos os tempos médios de execução em segundos, e na tabela 5.4 temos o tamanho das entradas da cadeia. Podemos ver mais uma vez que o tempo de execução da transformação 2 foi responsável por grande parte do tempo de processamento (98%). Apesar do tamanho da 56 Figura 5.6: Saı́da do estudo de caso Class2Relational com a operação cut Etapa Transformação Transformação Transformação Transformação Total 1 2 3 4 Tempo Médio 0,1266 185,084 1,2015 0,7901 187,202 Tabela 5.3: Tempo médio em segundos de cada etapa da cadeia para o estudo de caso Class2Relational Entrada Programa Alloy Instância Alloy Tamanho 39 assinaturas, 23 campos 43 assinaturas, 55 átomos, 23 campos, 116 tuplas Tabela 5.4: Tamanho da entrada de cada etapa do estudo de caso Class2Relational solução do estudo de caso Class2Relational conter mais elementos, o tempo das outras três transformações não aumentaram proporcionalmente. No próximo capı́tulo será apresentado as conclusões e sugestões para trabalhos futuros. 57 CAPÍTULO 6 CONCLUSÕES E TRABALHOS FUTUROS Nesta dissertação foi apresentada uma abordagem que possibilita a interoperabilidade de modelos escritos no espaço tecnológico da engenharia dirigida por modelos (MDE) com programas escritos no espaço tecnológico de resolvedores de busca. Esta interoperabilidade permite que aplicações MDE tirem proveito das capacidades de resolvedores de busca para executar operações complexas. Para permitir esta interoperabilidade, foi desenvolvida uma cadeia com quatro transformações de modelos. Estas transformações realizam uma injeção dos dados do programa do resolvedor de busca em um modelo e metamodelos no formato de uma plataforma MDE. Destas quatro transformações, uma tem a particularidade de poder ser implementada sem o conhecimento do metamodelo de saı́da. Este será interpretado no momento da execução. Por isso a abordagem é chamada de Transformação independente de Metamodelo. O desenvolvimento da solução teve diferentes contribuições, descritas abaixo: 1. Implementação de um caso de uso de transformação de modelos usando um resolvedor de busca, para avaliação da abordagem chamada “Transformação como Busca”. Foi escolhida a transformação Class2Relational, por ser uma referência em aplicações MDE. Esta implementação fortaleceu a hipótese da necessidade de automatização da cadeia de interoperabilidade entre os diferentes espaços tecnológicos. 2. Definição de uma cadeia de quatro transformações para transformar a solução no formato do resolvedor em um modelo em conformidade com o metamodelo de saı́da para uma operação de modelagem como busca. A cadeia de operações foi instanciada usando o framework Alloy. As três primeiras transformações são relativas a interoperabilidade entre os espaços tecnológicos: 58 2.1 Injeção do programa Alloy em um modelo de programa Alloy no espaço tecnológico MDE. 2.2 Injeção da solução do framework Alloy num modelo XML, e em seguida este modelo é transformado em um modelo de instâncias Alloy. Este modelo de instâncias Alloy está em conformidade com um metamodelo criado a partir do esquema XSD utilizado pelos documentos XML que armazenam a solução do framework Alloy. 2.3 Extração do metamodelo de saı́da a partir do modelo de programa Alloy. 2.4 Definição de uma transformação independente do metamodelo de saı́da (TIMeR). Esta transformação permite injetar um modelo no espaço tecnológico MDE em conformidade com um metamodelo conhecido apenas durante o tempo de execução. A TIMeR, diferente das abordagens atuais, recebe como entrada tanto o metamodelo de saı́da quanto o modelo com a solução do resolvedor. A TIMeR produz como saı́da um modelo conforme a este metamodelo de entrada. Para que a transformação possa ser executada, foi necessário definir uma função de mapeamento entre o modelo e seu respectivo metamodelo. Pelos resultados encontrados foi verificado que esta abordagem é viável e o objetivo foi alcançado. As soluções foram injetadas corretamente no espaço tecnológico MDE em dois casos de estudo. A escrita de uma única transformação foi capaz de realizar transformações para diferentes metamodelos de saı́da. O uso dos métodos de reflexão do framework EMF permitiu a TIMeR obter as informações necessárias dos modelos e metamodelos, para que durante o tempo da execução o modelo de entrada fosse transformado de forma adequada. Analisando os pontos em aberto da abordagem, verificou-se que a implementação depende do resolvedor utilizado. Caso seja utilizado outro resolvedor, é preciso reimplementar todas as etapas da cadeia. Como já explicitado, uma das suposições da TIMeR é considerar que os modelos de entrada e saı́da possuam uma estrutura similar, definida por uma função de similaridade entre os elementos dos metamodelo. Na implementação atual esta similaridade é definida por elementos de nomes iguais e de tipos correspondentes. 59 Isto é uma limitação que a impede de ser usada de forma geral, porém no caso de injeções de soluções de resolvedores não há este problema. Entretanto, a abordagem é extensı́vel por permitir a trocar a função de mapeamento. Outra limitação da abordagem é que são considerados apenas os aspectos estruturais do modelo, para tratar os aspectos dinâmicos dos modelos é preciso estender a abordagem atual. Há diferentes possibilidades de trabalhos futuros, como descrevemos abaixo: • Utilizar uma variação desta abordagem para implementar a geração dos dados de busca (transformação M2SP) da cadeia da modelagem como busca. Nesta transformação o metamodelo de entrada é desconhecido, e o modelo de entrada deve ser transformado em um modelo de programa Alloy. • Otimização das quatro etapas da cadeia. Principalmente uma nova implementação da transformação 2, possivelmente usando a própria API do EMF, de modo a melhorar o tempo de execução. • Pode ser investigado o uso de um modelo de correspondências entre as entradas da transformação 4 para permitir flexibilizar ainda mais esta abordagem. • Implementação destas transformações usando outros resolvedores de programação por restrições. 60 BIBLIOGRAFIA [1] IBM ILOG CPLEX Optimizer. http://www-01.ibm.com/software/integration/ optimization/cplex-optimizer/, 2010. [2] Atl developer guide. http://wiki.eclipse.org/ATL/Developer_Guide, 2013. [3] Ocl+ usecase. http://www.lsis.org/kleinerm/MS/OCLP_mm.html, 2013. [4] SAPos - sistema de apoio à pós-graduação. http://web.inf.ufpr.br/didonet/ sapos-sistema-de-apoio-a-pos-graduacao, 2013. [5] Francis Alizon, Mariano Belaunde, Grégoire DuPre, Bertrand Nicolas, Sébastien Poivre, e Jacques Simonin. Les modèles dans l’action à France Télécom avec SmartQVT. Génie logiciel: Congrès Journées Neptune No5, 2007. [6] Freddy Allilaire, Jean Bezivin, Hugo Bruneliere, e Frederic Jouault. Global Model Management In Eclipse GMT/AM3. 2006. [7] Krzysztof Apt. Principles of Constraint Programming. Cambridge University Press, New York, NY, USA, 2003. [8] Krzysztof R. Apt e Mark Wallace. Constraint Logic Programming using Eclipse. Cambridge University Press, New York, NY, USA, 2007. [9] Thorsten Arendt, Enrico Biermann, Stefan Jurack, Christian Krause, e Gabriele Taentzer. Henshin: Advanced concepts and tools for in-place emf model transformations. DorinaC. Petriu, Nicolas Rouquette, e Oystein Haugen, editors, Model Driven Engineering Languages and Systems, volume 6394 of Lecture Notes in Computer Science, páginas 121–135. Springer Berlin Heidelberg, 2010. [10] Daniel Le Berre e Anne Parrain. The sat4j library, release 2.2. JSAT, 7(2-3):59–6, 2010. 61 [11] Matthias Biehl. Literature Study on Model Transformations. Relatório Técnico ISRN/KTH/MMK/R-10/07-SE, Royal Institute of Technology, julho de 2010. [12] Marco Brambilla, Jordi Cabot, e Manueal Wimmer. Model-Driven Software Engineering in Practice. Synthesis Lectures on Software Engineering. [13] Jordi Cabot, Robert Clarisó, e Daniel Riera. Umltocsp: A tool for the formal verification of uml/ocl models using constraint programming. Proceedings of the Twentysecond IEEE/ACM International Conference on Automated Software Engineering, ASE ’07, páginas 547–548, New York, NY, USA, 2007. ACM. [14] Antonio Cicchetti, Davide Di Ruscio, Romina Eramo, e Alfonso Pierantonio. Jtl: A bidirectional and change propagating transformation language. Brian A. Malloy, Steffen Staab, e Mark van den Brand, editors, SLE, volume 6563 of Lecture Notes in Computer Science, páginas 183–202. Springer, 2010. [15] Paul Clements e Linda Northrop. Software Product Lines: Practices and Patterns. Addison-Wesley Longman Publishing Co., Inc., Boston, MA, USA, 2001. [16] Stephen A. Cook. The complexity of theorem-proving procedures. Proceedings of the third annual ACM symposium on Theory of computing, STOC ’71, páginas 151–158, New York, NY, USA, 1971. ACM. [17] György Csertán, Gábor Huszerl, István Majzik, Zsigmond Pap, András Pataricza, e Dániel Varró. Viatra ”visual automated transformations for formal verification and validation of uml models. Proceedings of the 17th IEEE international conference on Automated software engineering, ASE ’02, páginas 267–, Washington, DC, USA, 2002. IEEE Computer Society. [18] Krzysztof Czarnecki e Simon Helsen. Feature-based survey of model transformation approaches. IBM Syst. J., 45(3):621–645, julho de 2006. [19] Marcos Didonet Del Fabro. Metadata management using model weaving and model transformations. Tese de Doutorado, University of Nantes, setembro de 2007. 62 [20] Eclipse Modeling Project. Xpand / xtend reference, 2012. [21] Eclipse.org. Atl transformation example - book to publication. http://www.eclipse.org/atl/atlTransformations/Book2Publication/ ExampleBook2Publication[v00.02].pdf, 2013. [22] Eclipse.org. Atl transformation examples. http://www.eclipse.org/atl/ atlTransformations/, 2013. [23] Niklas Eén e Niklas Sörensson. An extensible sat-solver. Enrico Giunchiglia e Armando Tacchella, editors, SAT, volume 2919 of Lecture Notes in Computer Science, páginas 502–518. Springer, 2003. [24] Sven Efftinge, Peter Friese, Arno Haase, Clemens Kadura, Bernd Kolb, Dieter Moroff, Karsten Thoms, e Markus Völter. open Architecture Ware User Guide, 2007. [25] John Ellson, Emden R. Gansner, Eleftherios Koutsofios, Stephen C. North, e Gordon Woodhull. Graphviz - Open Source Graph Drawing Tools. Graph Drawing, páginas 483–484, 2001. [26] Ramez A. Elmasri e Shankrant B. Navathe. Fundamentals of Database Systems. Addison-Wesley Longman Publishing Co., Inc., Boston, MA, USA, 3rd edition, 1999. [27] Martin Gogolla, Fabian Büttner, e Mark Richters. Use: A uml-based specification environment for validating uml and ocl. Sci. Comput. Program., 69(1-3):27–34, dezembro de 2007. [28] Philipp Huber. The Model Transformation Language Jungle - An Evaluation and Extension of Existing Approaches. Dissertação de Mestrado, Technische Universität Wien, maio de 2008. [29] Aleksandar. Torlak Emina. Kang Eunsuk. Jackson, Daniel. Milicevic e Joe Near. Alloy 4.2. http://alloy.mit.edu/alloy/, 2013. 63 [30] Daniel Jackson. Automating first-order relational logic. Proceedings of the 8th ACM SIGSOFT international symposium on Foundations of software engineering: twentyfirst century applications, SIGSOFT ’00/FSE-8, páginas 130–139, New York, NY, USA, 2000. ACM. [31] Daniel Jackson. Software Abstractions: Logic, Language, and Analysis. The MIT Press, 2006. [32] Joxan Jaffar e Michael J. Maher. Constraint Logic Programming: A Survey. Journal of Logic Programming, 19/20:503–581, 1994. [33] Frédéric Jouault, Freddy Allilaire, Jean Bézivin, e Ivan Kurtev. Atl: A model transformation tool. Sci. Comput. Program., 72(1-2):31–39, junho de 2008. [34] Frédéric Jouault, Jean Bézivin, e Ivan Kurtev. Tcs:: A dsl for the specification of textual concrete syntaxes in model engineering. Proceedings of the 5th International Conference on Generative Programming and Component Engineering, GPCE ’06, páginas 249–254, New York, NY, USA, 2006. ACM. [35] Stuart Kent. Model driven engineering. Proceedings of the Third International Conference on Integrated Formal Methods, IFM ’02, páginas 286–298, London, UK, UK, 2002. Springer-Verlag. [36] Mathias Kleiner, Marcos Didonet Del Fabro, e Patrick Albert. Alloy metamodel and tcs usecase for model search. http://www.lsis.org/kleinerm/MS/Alloy_mm.html, 2010. [37] Mathias Kleiner, Marcos Didonet Del Fabro, e Patrick Albert. Model search: Formalizing and automating constraint solving in mde platforms. Thomas Kuhne, Bran Selic, Marie-Pierre Gervais, e François Terrier, editors, ECMFA, volume 6138 of Lecture Notes in Computer Science, páginas 173–188. Springer, 2010. 64 [38] Mathias Kleiner, Marcos Didonet Del Fabro, e Davi De Queiroz Santos. Transformation as search. Pieter Van Gorp, Tom Ritter, e Louis M. Rose, editors, ECMFA, volume 7949 of Lecture Notes in Computer Science, páginas 54–69. Springer, 2013. [39] Dimitrios S. Kolovos, Richard F. Paige, e Fiona A.C. Polack. The epsilon transformation language. Antonio Vallecillo, Jeff Gray, e Alfonso Pierantonio, editors, Theory and Practice of Model Transformations, volume 5063 of Lecture Notes in Computer Science, páginas 46–60. Springer Berlin Heidelberg, 2008. [40] Philippe Kruchten. The Rational Unified Process: An Introduction. Addison-Wesley Longman Publishing Co., Inc., Boston, MA, USA, 3 edition, 2003. [41] Ivan Kurtev. State of the art of qvt: A model transformation language standard. Andy Schurr, Manfred Nagl, e Albert Zundorf, editors, Applications of Graph Transformations with Industrial Relevance, volume 5088 of Lecture Notes in Computer Science, páginas 377–393. Springer Berlin Heidelberg, 2008. [42] Jean marie Favre. Towards a basic theory to model model driven engineering. In Proc. of the UML2004 Int. Workshop on Software Model Engineering, 2004. [43] Tom Mens e Pieter Van Gorp. A taxonomy of model transformation. Electron. Notes Theor. Comput. Sci., 152:125–142, março de 2006. [44] MIT. Alloy language reference. http://alloy.mit.edu/alloy/documentation/ book-chapters/alloy-language-reference.pdf, 2013. [45] Naouel Moha, Sagar Sen, Cyril Faucher, Olivier Barais, e Jean-Marc Jezequel. Evaluation of kermeta for solving graph-based problems. Int. J. Softw. Tools Technol. Transf., 12(3-4):273–285, julho de 2010. [46] Matthew W. Moskewicz, Conor F. Madigan, Ying Zhao, Lintao Zhang, e Sharad Malik. Chaff: Engineering an efficient sat solver. Proceedings of the 38th Annual Design Automation Conference, DAC ’01, páginas 530–535, New York, NY, USA, 2001. ACM. 65 [47] Object Management Group, Inc. UML Object Constraint Language (OCL) 2.0 Specification, outubro de 2006. [48] OMG. Model Driven Architecture (MDA) Guide, 2003. OMG doc. ab/2003-06-01. [49] OMG. Meta Object Facility (MOF) Core Specification, 2006. [50] OMG. XML Metadata Interchange (XMI). OMG, 2007. [51] OMG. Meta Object Facility (MOF) 2.0 Query/View/Transformation (QVT), 2011. [52] Jean remy Falleri, Marianne Huchard, e Clementine Nebut. C.: Towards a traceability framework for model transformations in kermeta. In: ECMDA-TW Workshop, 2006. [53] James Rumbaugh, Ivar Jacobson, e Grady Booch. Unified Modeling Language Reference Manual, The (2Nd Edition). Pearson Higher Education, 2004. [54] Andy Schurr. Specification of graph translators with triple graph grammars. Ernst W. Mayr, Gunther Schmidt, e Gottfried Tinhofer, editors, Graph-Theoretic Concepts in Computer Science, volume 903 of Lecture Notes in Computer Science, páginas 151– 163. Springer Berlin Heidelberg, 1995. [55] Roger S. Scowen. Extended BNF - A Generic Base Standard. Proceedings of the 1993 Software Engineering Standards Symposium (SESS’93), agosto de 1993. [56] David Steinberg, Frank Budinsky, Marcelo Paternostro, e Ed Merks. EMF: Eclipse Modeling Framework 2.0. Addison-Wesley Professional, 2nd edition, 2009. [57] Leon Sterling e Ehud Shapiro. The Art of Prolog (2Nd Ed.): Advanced Programming Techniques. MIT Press, Cambridge, MA, USA, 1994. [58] W3C. Extensible markup language (xml) 1.0. http://www.w3.org/TR/REC-xml/, 2008. [59] W3C. Xml schema 1.1. http://www.w3.org/XML/Schema/, 2010. 66 APÊNDICE A CÓDIGO DA TRANSFORMAÇÃO 4 Esta é a implementação em Java da TIMeR para transformar um modelo de instância Alloy em um modelo em conformidade como o metamodelo de saı́da passado pelo segundo parâmetro de execução. Código A.1: Código Java da Transformação 4 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 import import import import import import import import java . io . I O E x c e p t i o n ; java . s e c u r i t y. A l l P e r m i s s i o n ; java . util . A r r a y L i s t; java . util . C o l l e c t i o n s ; java . util . H a s h M a p; java . util . I t e r a t o r; java . util . List ; java . util . Map ; import import import import import import import import import import import import import org . e c l i p s e. emf . common . util . URI ; org . e c l i p s e. emf . ecore . EClass ; org . e c l i p s e. emf . ecore . E D a t a T y p e ; org . e c l i p s e. emf . ecore . E O b j e c t; org . e c l i p s e. emf . ecore . E R e f e r e n c e; org . e c l i p s e. emf . ecore . E S t r u c t u r a l F e a t u r e ; org . e c l i p s e. emf . ecore . r e s o u r c e. R e s o u r c e; org . e c l i p s e. emf . ecore . r e s o u r c e. R e s o u r c e S e t ; org . e c l i p s e. emf . ecore . r e s o u r c e. impl . R e s o u r c e S e t I m p l ; org . e c l i p s e. emf . ecore . util . E c o r e U t i l; org . e c l i p s e. emf . ecore . xmi . X M L R e s o u r c e ; org . e c l i p s e. emf . ecore . xmi . impl . X M I R e s o u r c e F a c t o r y I m p l ; org . e c l i p s e. emf . ecore . E P a c k a g e; public class T r a n s f o r m a c a o { p r i v a t e List < EObject > a l l I n s t a n c e s = new ArrayList < EObject >() ; p r i v a t e List < EPackage > a l l P a c k a g e s = new ArrayList < EPackage >() ; p r i v a t e List < EClass > a l l C l a s s e s = new ArrayList < EClass >() ; p r i v a t e List < E S t r u c t u r a lF ea tur e > allSF = new ArrayList <←֓ E S t r u c t u r a lF ea tu re >() ; p r i v a t e List < EDataType > a l l D a t a T y p e s = new ArrayList < EDataType >() ; private >() ; private private private List < EObject > s o l u t i o n A l l I n s t a n c e s = new ArrayList < EObject ←֓ List < EObject > s o l u t i o n A l l A t o m s = new ArrayList < EObject >() ; List < EObject > s o l u t i o n A l l F i e l d s = new ArrayList < EObject >() ; List < EObject > s o l u t i o n A l l S i g s = new ArrayList < EObject >() ; 67 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 p r i v a t e HashMap < EObject , EObject > A t o m 2 E C l a s s = new HashMap <←֓ EObject , EObject >() ; p r i v a t e HashMap < EObject , EObject > E C l a s s 2 A t o m = new HashMap < EObject ←֓ , EObject >() ; p r i v a t e HashMap < EObject , List < EObject > > A t o m F i e l d s = new HashMap <←֓ EObject , List < EObject > >() ; p r i v a t e HashMap < EObject , Map < String , List < EObject > > > A t o m F i e l d s 2 = ←֓ new HashMap < EObject , Map < String , List < EObject > > >() ; public T r a n s f o r m a c a o ( String i n p u t E C o re Mod el , String ←֓ i n p u t A l l o y S o l u t io n Ec or e , String i n p u t A l l o y S o lut io n , String ←֓ o u t p u t R e s ul tM ode l , String r o o t C l a s s N a m e ) { R e s o u r c e. F a c t o r y. R e g i s t r y reg = R e s o u r c e. F a c t o r y. R e g i s t r y.←֓ I N S T A N C E; Map m = reg . g e t E x t e n s i o n T o F a c t o r y M a p () ; m . put (" xmi " , new X M I R e s o u r c e F a c t o r y I m p l () ) ; m . put (" ecore " , new X M I R e s o u r c e F a c t o r y I m p l () ) ; R e s o u r c e S e t rs = new R e s o u r c e S e t I m p l () ; R e s o u r c e e c o r e I n R e s = rs . g e t R e s o u r c e ( URI . c r e a t e U R I (""+←֓ i n p u t E C o r e M o d e l ) , true ) ; // C a r r e g a ←֓ m e t a m o d e l o ecore R e s o u r c e a l l o y S o l E c o r e I n R e s = rs . g e t R e s o u r c e( URI . c r e a t e U R I (""+←֓ i n p u t A l l o y S o l u t i o n E c o r e ) , true ) ; // c a r r e g a m e t a m o d e l o ←֓ da s o l u ç ~ a o para e x t r a i r o pacote E P a c k a g e epkg = ( E P a c k a g e) a l l o y S o l E c o r e I n R e s . g e t C o n t e n t s () . get←֓ (0) ; // extrai o pacote rs . g e t P a c k a g e R e g i s t r y () . put ( epkg . g e t N s U R I () , epkg ) ; ←֓ // r e g i s t r a o ←֓ pacote R e s o u r c e a l l o y S o l I n R e s = rs . g e t R e s o u r c e ( URI . c r e a t e U R I (""+←֓ i n p u t A l l o y S o l u t i o n ) , true ) ; // C a r r e g a o modelo ←֓ com a s o l u ç ~ a o o. p r o c e s s E C o r e M o d e l ( e c o r e I n R e s) ; processAlloySolution( alloySolInRes); // E x e c u t a a t r a n s f o r m a ç ~ ao List o u t E l e m e n t s = d o T r a n s f o r m a t i o n () ; // Salva o modelo URI uri = URI . c r e a t e U R I ("" + o u t p u t R e s u l t M o d e l ) ; R e s o u r c e outRes = rs . c r e a t e R e s o u r c e ( uri ) ; // System . out . p r i n t l n ("\ n \ n R e s u l t a d o da T r a n s f o r m a c a o \ n \ n ") ; i m p r i m e R e s u l t a d o T r a n s f o r m a c a o ( o u t E l e m e n t s) ; // System . out . p r i n t l n ("\ n \ n P r e p a r a n d o para salvar \ n \ n ") ; /**/ List o b j s T o S e a r c h = new A r r a y L i s t () ; List < EObject > d a n g l i n g O b j s = new A r r a y L i s t () ; for ( I t e r a t o r i t e r a t o r = o u t E l e m e n t s . i t e r a t o r () ; i t e r a t o r.←֓ h a s N e x t () ;) { 68 79 80 81 E O b j e c t object = ( E O b j e c t) i t e r a t o r . next () ; if (( object . e C o n t a i n e r () == null && ( r o o t C l a s s N a m e == null ||←֓ r o o t C l a s s N a m e . equals ("") ) ) || ( object . e C o n t a i n e r () == null && object . eClass←֓ () . g e t N a m e () . equals ( r o o t C l a s s N a m e ) ) ) outRes . g e t C o n t e n t s () . add ( object ) ; 82 83 84 85 86 87 88 89 90 91 92 93 94 95 if ( E c o r e U t i l. getURI ( object ) . t o S t r i n g () . equals ( " # / / " )) { d a n g l i n g O b j s . add ( object ) ; } else { o b j s T o S e a r c h . add ( object ) ; } } /**/ b o o l e a n c o m p l e t e d = false ; Object c u r r e n t O b j = null ; while ( ! c o m p l e t e d && ! ( r o o t C l a s s N a m e == null || r o o t C l a s s N a m e←֓ . equals ("") ) ) { for ( Iterator < EObject > i t e r a t o r = d a n g l i n g O b j s . i t e r a t o r () ; ←֓ i t e r a t o r. h a s N e x t () ;) { E O b j e c t object = ( E O b j e c t) i t e r a t o r . next () ; if ( E c o r e U t i l. U s a g e C r o s s R e f e r e n c e r . find ( object , o b j s T o S e a r c h )←֓ . size () > 0) { outRes . g e t C o n t e n t s () . add ( object ) ; o b j s T o S e a r c h . add ( object ) ; c u r r e n t O b j = object ; c o m p l e t e d = false ; break ; } c o m p l e t e d = true ; } if ( c o m p l e t e d || d a n g l i n g O b j s . size () == 0) break ; d a n g l i n g O b j s . remove ( c u r r e n t O b j ) ; } /**/ try { if ( outRes . g e t C o n t e n t s () . size () > 0 ) { outRes . save ( C o l l e c t i o n s . E M P T Y _ M A P) ; } else System . out . p r i n t l n (" Modelo vazio ") ; } catch ( I O E x c e p t i o n e ) { System . out . p r i n t l n (" Erro ") ; e . p r i n t S t a c k T r a c e () ; } /**/ System . out . p r i n t l n (" Pronto ") ; 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 } p r i v a t e List d o T r a n s f o r m a t i o n () { List o u t E l e m e n t s = new A r r a y L i s t () ; 69 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 // cria um e l e m e n t o do modelo para cada atom for ( E O b j e c t atom : s o l u t i o n A l l A t o m s ) { // Salva o label do atom e da sig do atom String label = g e t V a l u e( atom , " label ") . t o S t r i n g () ; String s i g L a b e l = g e t S i g L a b e l F r o m A t o m ( atom ) . t o S t r i n g () ; s i g L a b e l = s i g L a b e l. split (" this /") [ s i g L a b e l. split (" this /") .←֓ length -1]; System . out . p r i n t l n (" atom "+ label ) ; // System . out . p r i n t l n ("1 "+ s i g L a b e l ) ; // Cria uma classe para cada e l e m e n t o: EClass eClass = g e t C l a s s B y N a m e ( s i g L a b e l) ; // System . out . p r i n t l n( eClass ) ; if ( eClass == null ) { System . out . p r i n t l n (" Nao tem uma class para a sig " + ←֓ s i g L a b e l) ; c o n t i n u e; } E O b j e c t newObj = eClass . g e t E P a c k a g e () . g e t E F a c t o r y I n s t a n c e () .←֓ create ( eClass ) ; // System . out . p r i n t l n( newObj ) ; // Salva o e l e m e n t o E C l a s s 2 A t o m. put ( newObj , atom ) ; A t o m 2 E C l a s s. put ( atom , newObj ) ; List < EObject > l i s t a F i e l d s = new ArrayList < EObject >() ; HashMap < String , List < EObject > > l i s t a F i e l d s 2 = new HashMap <←֓ String , List < EObject > >() ; for ( E O b j e c t field : s o l u t i o n A l l F i e l d s ) { // E O b j e c t owner = ( E O b j e c t) g e t V a l u e( field , " owner ") ; E O b j e c t origin = ( E O b j e c t) g e t V a l u e( field , " origin ") ; E O b j e c t dest = ( E O b j e c t) g e t V a l u e( field , " ref ") ; if ( g e t V a l u e( origin , " label ") . equals ( g e t V a l u e( atom , " label←֓ ") ) ) { System . out . p r i n t l n (" " + g e t V a l u e( field , " label ") ) ; System . out . p r i n t l n (" origem :" + g e t V a l u e( origin , ←֓ " label ") + " d e s t i n o :" + g e t V a l u e( dest , " label ") ) ; l i s t a F i e l d s . add ( field ) ; 166 167 168 169 170 171 if ( l i s t a F i e l d s 2 . c o n t a i n s K e y ( g e t V a l u e( field , " label ") .←֓ t o S t r i n g () ) ) { l i s t a F i e l d s 2 . get ( g e t V a l u e( field , " label ") . t o S t r i n g ()←֓ ) . add ( field ) ; } else { List < EObject > n o v a L i s t a = new ArrayList < EObject >() ; n o v a L i s t a. add ( field ) ; l i s t a F i e l d s 2 . put ( g e t V a l u e( field , " label ") . t o S t r i n g ()←֓ , n o v a L i s t a) ; } 172 173 174 175 176 177 178 179 180 } } 70 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 A t o m F i e l d s . put ( newObj , l i s t a F i e l d s) ; A t o m F i e l d s 2. put ( newObj , l i s t a F i e l d s 2 ) ; o u t E l e m e n t s. add ( newObj ) ; } i m p r i m e R e s u l t a d o P r o c e s s a m e n t o D o s F i e l d s () ; System . out . p r i n t l n(" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n \ n ")←֓ ; // Itera sobre os e l e m e n t o s c r i a d o s e c o m p l e t a os e l e m e n t o s. for ( Iterator < EObject > i t e r a t o r = o u t E l e m e n t s . i t e r a t o r () ; ←֓ i t e r a t o r. h a s N e x t () ;) { E O b j e c t m o d e l E l e m e n t = i t e r a t o r. next () ; System . out . p r i n t l n( m o d e l E l e m e n t . eClass () . g e t N a m e () + " " + ←֓ m o d e l E l e m e n t . h a s h C o d e () ) ; for ( Iterator < E S t r u c t u r a lF ea tu re > s f I t e r a t o r = m o d e l E l e m e n t .←֓ eClass () . g e t E A l l S t r u c t u r a l F e a t u r e s () . i t e r a t o r () ; ←֓ s f I t e r a t o r . h a s N e x t () ;) { E S t r u c t u r a l F e a t u r e f e a t u r e = s f I t e r a t o r . next () ; System . out . p r i n t l n (" " + f e a t u r e. g e t N a m e () ) ; // if ( f e a t u r e. g e t N a m e () . equals (" super ") ) System . out . p r i n t l n←֓ (" super !" + f e a t u r e. g e t N a m e () ) ; List < EObject > fields = A t o m F i e l d s . get ( m o d e l E l e m e n t ) ; Map < String , List < EObject > > f i e l d s 2 = A t o m F i e l d s 2. get (←֓ m o d e l E l e m e n t) ; List < EObject > v a l o r e s = f i e l d s 2. get ( f e a t u r e. g e t N a m e () ) ; // if ( f e a t u r e. g e t N a m e () . equals (" super ") ) { System . out .←֓ p r i n t l n (" vals =" + v a l o r e s) ; } if ( f e a t u r e i n s t a n c e o f E R e f e r e n c e ) { if ( f e a t u r e. g e t U p p e r B o u n d () > 1 || f e a t u r e.←֓ g e t U p p e r B o u n d () == -1 ) { if ( v a l o r e s == null ) c o n t i n u e; for ( E O b j e c t e : v a l o r e s) { E O b j e c t val = ( E O b j e c t) g e t V a l u e(e , " ref ") ; System . out . p r i n t l n (" " + A t o m 2 E C l a s s . get (←֓ val ) . eClass () . g e t N a m e () + " " + A t o m 2 E C l a s s .←֓ get ( val ) . h a s h C o d e () ) ; (( List ) m o d e l E l e m e n t . eGet ( f e a t u r e) ) . add ( ←֓ A t o m 2 E C l a s s . get ( val ) ) ; } } else // mono v a l o r a d o { if ( v a l o r e s == null ) c o n t i n u e; E O b j e c t val = ( E O b j e c t) g e t V a l u e( v a l o r e s. get (0) , "←֓ ref ") ; if ( val == null ) c o n t i n u e; 71 226 // System . out . p r i n t l n (" " + A t o m 2 E C l a s s. get (←֓ val ) . eClass () . g e t N a m e () + " " + A t o m 2 E C l a s s . get (←֓ val ) . h a s h C o d e () ) ; m o d e l E l e m e n t . eSet ( feature , A t o m 2 E C l a s s . get ( val ) ) ; // m o d e l E l e m e n t . eSet ( feature , val ) ; 227 228 229 230 231 232 233 } } else // ? a t r i b u t o { String t y p e N a m e = f e a t u r e. g e t E T y p e () . g e t N a m e () .←֓ t o L o w e r C a s e () ; 234 235 if ( t y p e N a m e. i n d e x O f (" int ") >= 0 || t y p e N a m e. i n d e x O f ("←֓ long ") >= 0) { E O b j e c t val = ( E O b j e c t) g e t V a l u e( v a l o r e s. get (0) , "←֓ ref ") ; I n t e g e r i n t V a l u e = new I n t e g e r( I n t e g e r. p a r s e I n t(←֓ g e t V a l u e( val ," label ") . t o S t r i n g () ) ) ; System . out . p r i n t l n (" " + i n t V a l u e) ; m o d e l E l e m e n t . eSet ( feature , i n t V a l u e) ; } else if ( t y p e N a m e. i n d e x O f (" string ") >= 0) { E O b j e c t val = ( E O b j e c t) g e t V a l u e( v a l o r e s. get (0) , "←֓ ref ") ; String str = g e t V a l u e( val ," label ") . t o S t r i n g () ; m o d e l E l e m e n t . eSet ( feature , str ) ; System . out . p r i n t l n (" " + str ) ; } else System . out . p r i n t l n (" Erro tipo d e s c o n h e c i d o . " + ←֓ t y p e N a m e) ; 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 } System . out . p r i n t l n (" eGet ( f e a t u r e) ) ; r e s u l t a d o: " + m o d e l E l e m e n t .←֓ } } return o u t E l e m e n t s; } p r i v a t e void i m p r i m e R e s u l t a d o T r a n s f o r m a c a o ( List o u t E l e m e n t s ) { for ( I t e r a t o r i t e r a t o r = o u t E l e m e n t s . i t e r a t o r () ; i t e r a t o r.←֓ h a s N e x t () ;) { E O b j e c t object = ( E O b j e c t) i t e r a t o r . next () ; System . out . p r i n t l n( object . eClass () . g e t N a m e () ) ; for ( Iterator < E S t r u c t u r a lF ea tu re > s f I t e r a t o r = object . eClass ←֓ () . g e t E A l l S t r u c t u r a l F e a t u r e s () . i t e r a t o r () ; s f I t e r a t o r.←֓ h a s N e x t () ;) { E S t r u c t u r a l F e a t u r e f e a t u r e = s f I t e r a t o r . next () ; System . out . p r i n t l n (" " + object . eGet ( f e a t u r e) ) ; } } } 72 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 p r i v a t e void i m p r i m e R e s u l t a d o P r o c e s s a m e n t o D o s F i e l d s () { System . out . p r i n t l n ("\ n \n - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -")←֓ ; for ( Map . Entry < EObject , Map < String , List < EObject > > > c l a s s e s : ←֓ A t o m F i e l d s 2. e n t r y S e t () ) { System . out . p r i n t l n( c l a s s e s. getKey () . eClass () . g e t N a m e () ) ; Map < String , List < EObject > > sf = c l a s s e s. g e t V a l u e () ; for ( Map . Entry < String , List < EObject > > d : sf . e n t r y S e t () ) { System . out . p r i n t l n (" " + d . getKey () ) ; List < EObject > v a l o r e s = d . g e t V a l u e () ; for ( E O b j e c t e : v a l o r e s) { E O b j e c t origin = ( E O b j e c t) g e t V a l u e(e , " origin ") ; E O b j e c t dest = ( E O b j e c t) g e t V a l u e(e , " ref ") ; System . out . p r i n t l n (" " + g e t V a l u e( origin , " label←֓ ") + " , " + g e t V a l u e( dest ," label ") ) ; } } } } // R e t o r n a o valor de uma s t r u c t u r a l f e a t u r e p r i v a t e Object g e t V a l u e( E O b j e c t atom , String value ) { return atom . eGet ( atom . eClass () . g e t E S t r u c t u r a l F e a t u r e ( value ) ) ; } p r i v a t e Object g e t S i g L a b e l F r o m A t o m ( E O b j e c t atom ) { E O b j e c t o = ( E O b j e c t) g e t V a l u e( atom ," type ") ; return g e t V a l u e (o , " label ") ; } // R e t o r n a uma classe a partir do nome p r i v a t e EClass g e t C l a s s B y N a m e ( String name ) { String c l a s s N a m e = name . t o L o w e r C a s e () ; for ( Iterator < EClass > c l a s s e s = a l l C l a s s e s . i t e r a t o r () ; c l a s s e s.←֓ h a s N e x t () ;) { EClass eClass = c l a s s e s. next () ; if ( eClass . g e t N a m e () . t o L o w e r C a s e () . equals ( c l a s s N a m e ) ) return eClass ; } return null ; } public String g e t E l e m e n t N a m e ( EClass aclass , E S t r u c t u r a l F e a t u r e ←֓ f e a t u r e) { return " d_ " + aclass . g e t N a m e () . t o L o w e r C a s e () + " _ " + f e a t u r e.←֓ g e t N a m e () . t o L o w e r C a s e () ; } p r i v a t e String g e t C l a s s N a m e A l l o y S o l ( E O b j e c t obj ) { EClass o b j C l a s s = obj . eClass () ; return o b j C l a s s . g e t N a m e () ; 73 324 325 326 327 328 329 330 331 332 333 334 335 } /////////////////////////// p r i v a t e void p r o c e s s A l l o y S o l u t i o n ( R e s o u r c e model ) { for ( I t e r a t o r e l e m e n t s = model . g e t A l l C o n t e n t s () ; e l e m e n t s.←֓ h a s N e x t () ;) { Object e l e m e n t = e l e m e n t s. next () ; if ( e l e m e n t i n s t a n c e o f E O b j e c t) { // System . out . p r i n t l n (" E O b j e c t " + e l e m e n t. t o S t r i n g () ) ; s o l u t i o n A l l I n s t a n c e s . add (( E O b j e c t) e l e m e n t) ; // Saves the atoms , fields and s i g n a t u r e s if ( g e t C l a s s N a m e A l l o y S o l (( E O b j e c t) e l e m e n t) . t o L o w e r C a s e () .←֓ equals (" atom ") ) s o l u t i o n A l l A t o m s . add (( E O b j e c t) e l e m e n t) ; if ( g e t C l a s s N a m e A l l o y S o l (( E O b j e c t) e l e m e n t) . t o L o w e r C a s e () .←֓ equals (" field ") ) s o l u t i o n A l l F i e l d s . add (( E O b j e c t) e l e m e n t) ; if ( g e t C l a s s N a m e A l l o y S o l (( E O b j e c t) e l e m e n t) . t o L o w e r C a s e () .←֓ equals (" sig ") ) s o l u t i o n A l l S i g s . add (( E O b j e c t) e l e m e n t) ; 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 } } } p r i v a t e void p r o c e s s E C o r e M o d e l ( R e s o u r c e model ) { for ( I t e r a t o r e l e m e n t s = model . g e t A l l C o n t e n t s () ; e l e m e n t s.←֓ h a s N e x t () ;) { Object e l e m e n t = e l e m e n t s. next () ; if ( e l e m e n t i n s t a n c e o f EClass ) { // System . out . p r i n t l n (" EClass " + e l e m e n t. t o S t r i n g () ) ; a l l C l a s s e s . add (( EClass ) e l e m e n t) ; } if ( e l e m e n t i n s t a n c e o f E P a c k a g e) { // System . out . p r i n t l n (" E P a c k a g e " + e l e m e n t. t o S t r i n g () ) ; a l l P a c k a g e s . add (( E P a c k a g e) e l e m e n t) ; } if ( e l e m e n t i n s t a n c e o f E S t r u c t u r a l F e a t u r e ) { // System . out . p r i n t l n (" E S t r u c t u r a l F e a t u r e " + e l e m e n t.←֓ t o S t r i n g () ) ; allSF . add (( E S t r u c t u r a l F e a t u r e ) e l e m e n t) ; } if ( e l e m e n t i n s t a n c e o f E S t r u c t u r a l F e a t u r e ) { // System . out . p r i n t l n (" E D a t a T y p e " + e l e m e n t. t o S t r i n g () ) ; allSF . add (( E S t r u c t u r a l F e a t u r e ) e l e m e n t) ; } if ( e l e m e n t i n s t a n c e o f E O b j e c t) { // System . out . p r i n t l n (" E O b j e c t " + e l e m e n t. t o S t r i n g () ) ; a l l I n s t a n c e s . add (( E O b j e c t) e l e m e n t) ; } } } p r i v a t e static void d e b u g M o d e l( R e s o u r c e model , String out ) { System . out . p r i n t l n( out ) ; for ( I t e r a t o r e l e m e n t s = model . g e t A l l C o n t e n t s () ; e l e m e n t s.←֓ h a s N e x t () ;) { Object e l e m e n t = e l e m e n t s. next () ; 74 375 376 377 378 379 380 381 382 383 System . out . p r i n t l n( e l e m e n t) ; } } public static void main ( String [] args ) { if ( args . length < 2) { System . out . p r i n t l n (" A r g u m e n t s not valid : {←֓ I N _ A l l o y _ P r o b l e m _ m e t a m o d e l _ pa t h , ←֓ I N _ A l l o y S o l u t i o n _ m o d el _ pa t h , O U T _ m o d e l _ p a t h }.") ; } else { String in1 = args [0]; String in2 = args [1]; String out = args [2]; T r a n s f o r m a c a o t = new T r a n s f o r m a c a o ( in1 , " T r a n s f o r m a c a o /←֓ A l l o y X S D. ecore " , in2 , out , "") ; } 384 385 386 387 388 389 390 391 } }