Tutorial: Desenvolvimento de Software Baseado em Modelos na Prática com Epsilon, Parte 2

Na primeira parte deste tutorial, foi implementada de forma básica uma ferramenta CASE para o desenvolvimento de um sistema de arquivos, utilizando a Epsilon para realizar esta implementação. Nesta ferramenta é possível criar modelos e gerar código que reflita o sistema modelado. Até aqui, foi definido o metamodelo que descreve o domínio, seus conceitos e como estes conceitos estão relacionados.

Nesta segunda etapa irei mostrar como definir a sintaxe concreta da sua DSML, de forma que a ferramenta gerada contenha esta sintaxe. Além disso, será definida a geração de código, finalizando o processo MDD proposto neste tutorial.

Novos ícones
Caso você tenha conseguido gerar a ferramenta da maneira descrita na parte 1 deste tutorial, foi possível perceber que o Epsilon gera um editor com ícones padrões na paleta de ferramentas de modelagem. Essa solução vai de encontro ao que se propõe ao definirmos uma DSML, já que a sintaxe concreta deve representar conceitos e relacionamentos do domínio.

Para adicionar ícones que representem estes conceitos e relacionamentos com maior semântica, efetue o download de um pacote de ícones (neste tutorial vou utilizar este pacote) e adicione estes ícones ao projeto "filesystem.edit", na pasta "icons/full/obj16". Após isso é preciso realizar modificações no arquivo "filesystem.emf". Serão adicionadas anotações referentes aos ícones para cara entidade do modelo. O código ficará da seguinte forma:

@namespace(uri="filesystem", prefix="filesystem")
@gmf
package filesystem;

@gmf.diagram
class Filesystem {
    val Drive[*] drives;
    val Sync[*] syncs;
}

@gmf.node(label.icon="true",tool.small.bundle="filesystem.edit", tool.small.path="/icons/full/obj16/CD.gif")
class Drive extends Folder {

}

@gmf.node(label.icon="true",tool.small.bundle="filesystem.edit", tool.small.path="/icons/full/obj16/Folder.gif")
class Folder extends File {
    @gmf.compartment
    val File[*] contents;
}

@gmf.node(label.icon="true",tool.small.bundle="filesystem.edit", tool.small.path="/icons/full/obj16/Trackback.gif")
class Shortcut extends File {
    @gmf.link(target.decoration="arrow", style="dash")
    ref File target;
}

@gmf.link(source="source", target="target", style="dot", width="2")
class Sync {
    ref File source;
    ref File target;
}

@gmf.node(label = "name", label.icon="true",tool.small.bundle="filesystem.edit", tool.small.path="/icons/full/obj16/List.gif")
class File {
    attr String name;
}

Observe a inserção da anotação "@gmf.node". Os atributos adicionados ("label.icon", "tool.small.bundle" e "tool.small.path") nesta anotação definem a imagem que representará a classe logo abaixo dela. Para maiores informações sobre estas anotações, acesse a documentação do GMF.

Desta forma, é possível adicionar uma representação gráfica básica, que descreva os elementos do domínio graficamente. Outra representação gráfica que pode ser adicionada é aos próprios elementos desenhados no modelo. Ao invés de utilizarmos formas geométricas padrões do GMF, como elipse e quadrado para representar os nós, "nodes", é possível definir uma imagem no formato .png que irá ser desenhada no diagrama modelado.

Um exemplo desta segunda forma de definir a sintaxe concreta para representar conceitos do domínio é realizada da seguinte maneira: crie um novo projeto, "New > Project > General... Project", chamado "filesystem.figures", neste novo projeto crie dois pacotes: 1) "figures" e 2) "filesystem.figures.activator". No pacote "figures" iremos implementar a classe que cria a imagem, com o código a seguir:

package figures;

import org.eclipse.draw2d.ImageFigure;

import filesystem.figures.activator.PluginActivator;

public class FileFigure extends ImageFigure {

 public FileFigure() { 
  super(PluginActivator.imageDescriptorFromPlugin(PluginActivator.ID,"images/File.png").createImage(), 0);
 }

}

Observe que esta classe depende de um plugin ativador, "PluginActivator". Este plugin é implementado da seguinte forma:


package filesystem.figures.activator;

import org.eclipse.core.runtime.Plugin;
import org.eclipse.ui.plugin.AbstractUIPlugin;
import org.osgi.framework.BundleContext;

/**
 * @generated
 */
public class PluginActivator extends AbstractUIPlugin {

 /**
  * @generated
  */
 public static final String ID = "filesystem.figures"; //$NON-NLS-1$

 /**
  * @generated
  */
 private static PluginActivator ourInstance;

 /**
  * @generated
  */
 public PluginActivator() {
 }

 /**
  * @generated
  */
 public void start(BundleContext context) throws Exception {
  super.start(context);
  ourInstance = this;
 }

 /**
  * @generated
  */
 public void stop(BundleContext context) throws Exception {
  ourInstance = null;
  super.stop(context);
 }

 /**
  * @generated
  */
 public static PluginActivator getDefault() {
  return ourInstance;
 }
}

Para finalizar a construção do projeto "filesystem.figures", basta criar o diretório "images" neste mesmo projeto e adicionar as figuras que deseja atribuir para cada nó. Neste tutorial, adicionei somente a figura "File.png".

Além das implementação do projeto "filesystem.figures", é preciso modificar o arquivo "filesystem.emf", presente no projeto principal, "filesystem". O arquivo modificado deve ficar da seguinte forma:

@namespace(uri="filesystem", prefix="")
package filesystem;

@gmf.diagram
class Filesystem {
    val Drive[*] drives;
    val Sync[*] syncs;
}

@gmf.node(label.icon="true",tool.small.bundle="filesystem.edit", tool.small.path="/icons/full/obj16/CD.gif")
class Drive extends Folder {

}

@gmf.node(label.icon="true",tool.small.bundle="filesystem.edit", tool.small.path="/icons/full/obj16/Folder.gif")
class Folder {
    @gmf.compartment
    val File[*] contents;
    
    attr String name;
}

@gmf.node(label.icon="true",tool.small.bundle="filesystem.edit", tool.small.path="/icons/full/obj16/Trackback.gif")
class Shortcut {
    @gmf.link(target.decoration="arrow", style="dash")
    ref File target;
    
    attr String name;
}

@gmf.link(source="source", target="target", style="dot", width="2")
class Sync {
    ref File source;
    ref File target;
}

/*Alteração da anotação abaixo para modificar a representação gráfica do nó "File".*/
@gmf.node(figure="figures.FileFigure", 
 label.icon="false", label="name", label.placement="external",
 label = "name", label.icon="true",tool.small.bundle="filesystem.edit", tool.small.path="/icons/full/obj16/List.gif"")
class File {
    attr String name;
}

E para finalizar esta parte do tutorial, um pequeno fix deve ser realizado, já que o EuGENia ainda não oferece uma maneira de adicionar dependências de projetos de forma padrão na geração do editor. Este fix deve ser criado no diretório "models" do projeto principal, "filesystem". Para isso, no diretório "models", selecione "New > Other... > Epsilon > EOL Program". Nomeie o arquivo como "FixGMFGen.eol" e adicione o seguinte código:

var plugin := GmfGen!GenPlugin.all.first;
plugin.requiredPlugins.add('filesystem.figures');

Estrutura do projeto.
Após esta modificação, basta gerar o editor normalmente através do EuGENia, como já visto na parte 1 deste tutorial (clique com o botão direito no arquivo "filesystem.emf", "Eugenia > Generate GMF Editor"). A imagem ao lado mostra como deve ficar a estrutura do seu projeto.

Basta executar o projeto "Run > filesystem" e o resultado final deve ser igual ao da imagem abaixo:
Ferramenta com a sintaxe concreta definida.

No próximo e último tutorial sobre o Epsilon, irei mostrar como gerar código a partir dos modelos criados na ferramenta que estamos implementando. Lembrando que o projeto está disponível no github (https://github.com/felipealencar/filesystem). Espero que tenham gostado. Até a próxima!


Felipe Alencar

Felipe Alencar é doutorando em Ciência da Computação na UFPE, professor, desenvolvedor e acredita que só não virou jogador de futebol, surfista ou músico profissional por falta de tempo e talento.

Nenhum comentário:

Postar um comentário