Autoria de Transformações de Modelo em Modelo

Você pode dividir soluções de mapeamento de modelo para modelo em vários modelos de mapeamento de diversos projetos de mapeamento. A criação de transformações de modelo em modelo com mapeamentos é o processo de criar um modelo de mapeamento, que consiste em um modelo contendo regras que descrevem como mapear os objetos em um modelo para os objetos em outro modelo. Depois que você criar o modelo de mapeamento, poderá gerar o código-fonte de transformação que transforma um modelo de origem em um modelo de destino.

Pré-requisitos para a Autoria de Transformações

Nota: Para criar transformações, é necessário instalar os recursos de extensibilidade, compactados como um componente de produto opcional. Você deve ativar os recursos de Modelagem e Desenvolvedor XML.

Processo de Criação de Transformações de Modelo em Modelo

O processo de criação de transformações de modelo em modelo consiste nas seguintes etapas de alto nível:
  1. Você cria um projeto de mapeamento de transformações de modelo em modelo, também chamado de projeto de mapeamento, contendo um modelo de mapeamento. Um projeto de mapeamento pode conter vários modelos de mapeamento. Quando você cria um projeto de mapeamento, o serviço de transformação registra uma transformação. Cada transformação possui um provedor de transformações, uma transfiguração chamada MainTransform e uma transfiguração para cada declaração de mapeamento no projeto.
  2. Você inclui declarações de mapeamento, também chamadas de mapas, no modelo de mapeamento. Um modelo de mapeamento pode conter uma ou mais declarações de mapeamento.
  3. Você inclui regras de mapeamento às declarações de mapeamento em um modelo de mapeamento.
  4. Você gera o código-fonte da transformação a partir de um ou mais modelos de mapeamento no projeto de mapeamento. As ferramentas de criação de transformações de modelo em modelo geram uma transformação para cada modelo no projeto de mapeamento. Para cada declaração de mapeamento, as ferramentas de criação geram um arquivo de origem Java™ que implementa uma transformação. Para cada regra de mapeamento customizada ou de movimentação em uma declaração de mapeamento, é gerada uma regra no código-fonte da transfiguração. Para cada regra de mapeamento de submapa em uma declaração de mapeamento, é gerado um extrator de conteúdo no código-fonte da transfiguração.

Projetos de Mapeamento de Transformações de Modelo em Modelo

Projetos de mapeamento de transformações de modelo em modelo, também chamados de projetos de mapeamento, são plug-ins do Eclipse que estendem o ponto de extensão denominado com.ibm.xtools.transform.core.transformationProviders. Ao criar transformações de modelo em modelo em projetos de mapeamento de transformação, você especifica como os elementos nos modelos selecionados de origem e destino, ou metamodelos, estão relacionados, em vez de criar um código que represente os detalhes de implementação da transformação.

Um projeto de mapeamento pode conter mais de um arquivo de mapeamento, que também é chamado de modelo de mapeamento. Você pode gerar iterativamente um código-fonte quando modificar o modelo de mapeamento. Quando você gera o código-fonte da transformação, uma transfiguração externamente visível, chamada MainTransform, é registrada automaticamente, e o código-fonte Java de uma transfiguração é gerado para cada declaração de mapeamento no modelo de mapeamento.

É possível especificar um ou mais metamodelos de origem e destino ao criar um projeto de mapeamento. É possível especificar metamodelos, que possuem .ecore como extensão de nome de arquivo, ou perfis UML, que possuem .epx ou .uml como extensão de nome de arquivo. Se você especificar metamodelos de origem e destino ao criar um projeto, as dependências necessárias serão incluídas no arquivo de manifesto de plug-ins automaticamente. Se você incluir os metamodelos utilizando os comandos na área do editor depois de criar o projeto de mapeamento, deverá incluir quaisquer novas dependências necessárias ao arquivo de manifesto de plug-ins.

Modelos de Mapeamento

Modelos de mapeamento, também chamados de arquivos de mapeamento, são instâncias de metamodelos Eclipse Modeling Framework (EMF), também chamados de modelos Ecore, que contêm referências aos metamodelos que estão sendo mapeados. Quando você cria um projeto de mapeamento, as ferramentas de criação cria um modelo de mapeamento no projeto utilizando um ou mais modelos de entrada e saída que forem especificados. Um projeto de mapeamento contém pelo menos um modelo de mapeamento. Um modelo de mapeamento tem .mapping como extensão de nome de arquivo. A criação de vários modelos de mapeamento em um projeto incentiva a reutilização de mapas em outros projetos de mapeamento. Essa funcionalidade é útil para soluções de mapeamento em grande escala, na qual existe vários modelos de mapeamento em diversos projetos de mapeamento.

Modelos de mapeamento são armazenados e serializados como arquivos XML. A visualização Problemas exibe informações de erro detalhadas sobre modelos de mapeamento. Nessa visualização, dê um clique duplo em um item para abrir o modelo de mapeamento em um editor de texto e visualize a linha que contém o erro. Esse método de resolução de problemas costuma ser mais fácil do que a resolução de problemas por meio da visualização do modelo de mapeamento na área do editor.

Declarações de Mapeamento

Uma declaração de mapeamento, também chamada de mapa, especifica como criar ou atualizar um objeto de saída para um determinado objeto de entrada. Cada declaração de mapeamento especifica um tipo de entrada e um tipo de saída, que você seleciona a partir dos metamodelos que inclui no modelo de mapeamento. Um modelo de mapeamento pode conter várias declarações de mapeamento.

A finalidade da criação de um mapa é designar valores a um destino, com base nos valores em uma origem. Um mapa entre elementos estabelece a correspondência entre seus atributos, o que permite a troca de dados entre eles. A maioria dos mapas oferece a capacidade de manipular ainda mais os dados entre a origem e o destino. Por exemplo, você pode optar por especificar cálculos ou fazer outras modificações nos dados criando um código customizado, que possibilita designar valores ao destino.

Declarações de mapeamento podem conter objetos de saída abstratos. Essas declarações de mapeamento podem apenas ser herdadas, estendidas ou referenciadas em uma regra de mapeamento de submapa cujo atributo de destino contenha uma propriedade de contenção definida como false. Esses atributos de destino podem ser preenchidos com referências a objetos que são criados por uma declaração de mapeamento que é herdada a partir da declaração de mapeamento especificada na regra de mapeamento de submapa. Você pode utilizar uma declaração de mapeamento que especifique um objeto de saída abstrato para simplificar a criação de referências de objeto.

Em geral, declarações de mapeamento seguem uma convenção de nomenclatura de x2y, em que x representa o tipo de objeto de entrada e y representa o tipo de objeto de saída. Por exemplo, uma declaração de mapeamento denominada Package2EPackage especifica uma declaração de mapeamento que possui Package como objeto de entrada e EPackage como objeto de saída.

Quando você gera o código-fonte da transformação, o serviço de transformação gera um arquivo de origem Java para cada declaração de mapeamento em um modelo de mapeamento, que implementa uma transformação.

Declarações de mapeamento contêm uma ou mais regras de mapeamento.

Herança de Declarações de Mapeamento

Declarações de mapeamento podem herdar de outras declarações de mapeamento. As declarações de mapeamento herdadas e hereditárias não precisam ser definidas no mesmo modelo de mapeamento. Os objetos de entrada e saída hereditários devem ser subtipos, mas não necessariamente subtipos característicos, dos objetos de entrada e saída na declaração de mapeamento herdada. A declaração de mapeamento hereditária herda todos os mapeamentos definidos na declaração de mapeamento herdada ou por ela herdados. As regras de mapeamento na declaração de mapeamento herdada se tornam parte da declaração de mapeamento hereditária; entretanto, você pode criar regras de mapeamento customizadas para substituir as que se encontram na declaração de mapeamento herdada. Você também pode criar regras de mapeamento adicionais na declaração de mapeamento hereditária. A transfiguração gerada a partir de uma declaração de mapeamento hereditária estende a transfiguração gerada a partir da declaração de mapeamento herdada.

Regras de Mapeamento

Regras de mapeamento, também chamadas de mapeamentos, especificam como designar um valor a um atributo de um objeto de saída, de acordo com os valores dos atributos de um objeto de entrada.

Você pode criar os seguintes tipos de regras de mapeamento entre os atributos de objetos de entrada e saída:
Mover
Uma movimentação, também chamada de regra de mapeamento simples, é o tipo mais básico de regra de mapeamento. Os atributos de entrada e saída devem ser tipos de dados compatíveis: o Ecore EDataTypes dos atributos de entrada e saída devem ser iguais, ou o tipo de dados do atributo de saída é Cadeia e o tipo de dados do atributo de entrada pode ser serializado. Ambos os atributos de entrada e saída, ou nenhum deles, têm vários valores. Por exemplo, selecione essa opção se um atributo do objeto de saída for Cadeia e um atributo do objeto de entrada puder ser convertido em Cadeia sem um código customizado. Esse tipo de regra de mapeamento suporta um mapeamento entre um atributo ou elemento de origem e um atributo ou elemento de destino. O código-fonte da transformação que é gerado para uma regra de mapeamento de movimentação implementa uma regra que copia o valor de um atributo de entrada para um atributo de saída.
Submapa
Um submapa é uma chamada de um mapa a partir de outro mapa. O submapa que está sendo invocado pode, mas não precisa, ser definido no mesmo arquivo de mapeamento do que o mapa que o invoca. Um submapa permite mapear um tipo complexo no modelo de entrada para um tipo complexo no modelo de saída. O submapa que você criar pode invocar um mapa existente em qualquer arquivo de mapeamento. Por exemplo, considere um modelo de mapeamento NewMappingModel e um modelo de mapeamento OriginalMappingModel que contém um mapa denominado Package2Package. Para criar um mapa em NewMappingModel que especifique um mapeamento entre um pacote de entrada e um pacote de saída, cujo comportamento seja o mesmo do que o mapa Package2Package em OriginalMappingModel, você pode especificar o arquivo de mapeamento para OriginalMappingModel. A definição de submapas em arquivos de mapeamento separados incentiva a reutilização de mapas; entretanto, a criação de vários arquivos de mapeamento pode intensificar a manutenção do projeto. Submapas também podem incluir outros submapas, resultando em uma estrutura hierárquica.

Você também pode criar regras de mapeamento de submapas entre os objetos de entrada e os objetos de saída em uma declaração de mapeamento.

Regras de mapeamento de submapa suportam os seguintes tipos de mapeamentos:
  • Mapeamentos de 1 para 1 entre objetos de entrada e objetos de saída ou entre os atributos dos objetos de entrada e saída
  • Mapeamentos de 1 para m ou m para 1 entre atributos dos objetos de entrada e saída
  • Mapeamentos de m para n entre atributos dos objetos de entrada e saída
Se a regra de submapa especificar um mapeamento de 1 para m, a transfiguração gerada anexará um objeto a partir de um atributo singular a uma lista; para um mapeamento de m para 1, a transfiguração irá extrair um objeto de uma lista e inseri-lo em um atributo singular.

Para cada submapa em uma declaração de mapeamento, um extrator denominado getInputFeatureToOutputFeature_UsingMap_Extractor é gerado na transformação de inclusão, em que InputFeature representa o nome do atributo de entrada e OutputFeature representa o nome do atributo de saída, enquanto Map representa o nome da declaração de mapeamento.

Customizado
Esse tipo de mapeamento permite especificar um código customizado que calcule o valor de uma propriedade de saída. Por exemplo, selecione esse tipo de mapeamento para configurar o valor de uma propriedade no objeto de saída como igual à concatenação de várias propriedades de objeto de entrada.

Você pode especificar refinamentos semânticos utilizando a API Object Constraint Language (OCL) fornecida pelo Eclipse.

Regras de mapeamento customizadas suportam os seguintes tipos de mapeamentos:
  • Mapeamentos de 1 para n entre objetos de entrada e saída, ou entre atributos dos objetos de entrada e saída
  • Mapeamentos de m para n entre objetos de entrada e saída, ou entre atributos dos objetos de entrada e saída
  • Mapeamentos de m para 1, em que m para 1 representa um dos seguintes mapeamentos:
    • Um mapeamento a partir de um único atributo de entrada, cuja multiplicidade esteja configurada para m, para um único atributo de saída, cuja multiplicidade esteja configurada para 1
    • Um mapeamento a partir de vários atributos de entrada para um único atributo de saída
    • Um mapeamento a partir de vários objetos de entrada para um único objeto de saída
Mapas herdados
Uma declaração de mapeamento hereditária herda as regras de mapeamento que são definidas na declaração de mapeamento herdada. Você pode substituir regras de mapeamento herdadas definindo uma regra de mapeamento que tenha as seguintes qualidades:
  • A propriedade de objeto de entrada e a propriedade de objeto de saída são iguais à regra de mapeamento herdada.
  • As regras de mapeamento de substituição e herdada são mapeamentos de submapa que possuem extratores correspondentes, ou as regras de mapeamento de substituição e herdada são ambas mapeamentos de movimentação ou mapeamentos customizados.
Se você criar uma regra de mapeamento de mapas herdada, deverá especificar a declaração de mapeamento que contém as regras de mapeamento que deseja herdar. Não é possível especificar mais de um mapa herdado em uma declaração de mapeamento.

Uma regra de mapeamento herdada, e a regra ou o extrator gerado a partir desse mapeamento, mantêm a mesma posição relativa na ordem de processamento que a regra de mapeamento e sua regra ou seu extrator gerado.

Para cada regra de mapeamento de movimentação ou customizada em uma declaração de mapeamento, é incluída uma regra no código-fonte de transformação gerado. Para cada regra de mapeamento de submapa, é gerado um extrator de conteúdo no código-fonte da transformação.

Quando você cria uma regra de mapeamento, seu tipo é determinado pelos atributos de entrada e saída que são selecionados. Por exemplo, se os atributos de entrada e saída forem tipos primitivos compatíveis, como cadeias ou inteiros, uma regra de mapeamento de movimentação será especificada. Se os atributos de entrada e saída forem tipos complexos, uma regra de submapa será especificada. Se movimentação e submapa não forem tipos de regras de mapeamento apropriados, uma regra de mapeamento customizada será especificada.

Você também pode criar uma regra de mapeamento de mapas herdada, customizada ou de submapa entre um objeto de entrada e um objeto de saída.

Comportamento de Regras de Mapeamento de Submapa

As características de objetos de saída e as propriedades de contenção de atributos em um objeto de saída determinam o comportamento das regras de mapeamento de submapa.

Estrutura de Submapas
Uma declaração de mapeamento pode conter uma ou mais definições de submapa. Cada submapa é definido por um conjunto de valores, como no exemplo a seguir: (InputFeature, OutputFeature, (Map, InputElement, OutputElement)), em que os valores representam os itens na tabela a seguir:
Item Representação
InputFeature O compartimento no objeto de entrada que é conectado ao elemento de submapa
OutputFeature O compartimento no objeto de saída que é conectado ao elemento de submapa
Map A declaração de mapeamento que o submapa aplica quando a transfiguração gerada é executada
InputElement O objeto de entrada especificado pela declaração de mapeamento Map
OutputElement O objeto de saída especificado pela declaração de mapeamento Map

Quando você gerar o código-fonte de transformação para um modelo de mapeamento, para cada declaração de mapeamento, as ferramentas de criação irão gerar uma transfiguração denominada MapTransform. Para cada submapa em uma declaração de mapeamento, as ferramentas de criação geram um extrator de conteúdo denominado getInputFeatureToOutputFeature_UsingMap_Extractor na transformação.

Quando uma transformação de modelo em modelo é executada, ela gera outros objetos ou referências aos objetos gerados, dependendo das configurações de recursos nesse metamodelo. Se a transformação gerar objetos, ela registrará informações de identificação sobre esses objetos em um mapa de hash. A transformação resolve referências em objetos durante o pós-processamento com base nesse mapa de hash.

Semânticas de Submapas

O elemento de metamodelo Ecore ou UML que você especifica como objeto de saída em uma declaração de mapeamento pode ser abstrato ou concreto. É possível instanciar elementos concretos, mas não elementos abstratos.

Os recursos, também conhecidos como atributos, de um objeto de saída podem conter outros objetos Ecore ou UML ou podem fazer referência e esses objetos. Se um objeto contiver outro objeto, o objeto contido será excluído quando o objeto de inclusão for excluído. Se um objeto fizer referência a outro objeto, a exclusão da referência ou a exclusão do proprietário dessa referência não irá excluir o objeto referenciado.

A tabela a seguir lista o comportamento das regras de mapeamento de submapa, dependendo das características do objeto de saída e dos atributos de saída.

Tipo de objeto de saída Tipo de contenção ou referência no atributo de saída Comportamento do submapa
Abstrato Contenção abstrata ou concreta Esse tipo de submapa não é suportado porque a transfiguração MapTransform não pode criar objetos e, portanto, não pode inserir objetos na coleta OutputFeature.
Abstrato ou concreto Referência abstrata ou concreta Após o processamento de todas as outras regras e extratores, a transformação processa esse tipo de submapa concluindo as etapas a seguir:
  • Procura objetos de destino no mapa de hash utilizando uma chave de procura igual a (MapTransform+InputItem), em que InputItem representa um objeto na coleta InputFeature.
    Nota: Uma correspondência exata na procura não é necessária. Por exemplo, uma chave de procura igual a (MapTransform+InputItem) retorna registros cujas chaves são iguais a (MapTransform*+InputItem), em que MapTransform* representa o conjunto de todas as transfigurações que herdam a partir da transfiguração MapTransform.
    • Se a procura retornar um objeto de destino denominado TargetObject, a transformação irá inserir uma referência a TargetObject na coleta OutputFeature apropriada.
    • Se a procura retornar vários objetos candidatos, a transformação irá aplicar o filtro de saída de submapa, se definido, para selecionar um objeto. Em seguida, a transformação irá inserir uma referência a TargetObject na coleta OutputFeature apropriada. Se o submapa não definir um filtro de saída, a transformação irá selecionar o primeiro objeto candidato na lista de resultados de procura e inserir uma referência a TargetObject na coleta OutputFeature apropriada.
    • Se a procura não retornar um resultado, a transformação registrará um erro e exibirá uma mensagem.
Concreto Contenção abstrata ou concreta Para cada objeto InputItem na coleta InputFeature, a transformação conclui as seguintes etapas:
  • Executa a transfiguração MapTransform para criar um objeto TargetObject do tipo OutputElement
  • Insere uma instância do objeto TargetObject na coleta OutputFeature
  • Registra a ação em um mapa de hash, cuja chave tem o seguinte formato: (MapTransform+InputItem->TargetObject)
Exemplo

O exemplo a seguir explica como uma transformação resolve referências para objetos de saída durante o pós-processamento.

Nesse exemplo, um modelo de mapeamento contém os seguintes itens:
  • Uma declaração de mapeamento Classifier2Classifier que especifica um classificador UML como o objeto de entrada e o objeto de saída
  • Uma declaração de mapeamento Class2Class que especifica uma classe UML como o objeto de entrada e o objeto de saída

    Essa declaração de mapeamento define uma regra de mapeamento de mapas herdada entre o objeto de entrada e o objeto de saída. A regra de mapeamento especifica que essa declaração de mapeamento herda da declaração de mapeamento Classifier2Classifier.

No código-fonte de transformação gerado, observe que a transfiguração Class2ClassTransform herda da transfiguração Classifier2ClassifierTransform:
public class Class2ClassTransform extends Classifier2ClassifierTransform {

...
}

Como um classificador é um tipo abstrato, a transfiguração Classifier2ClassifierTransform não pode criar objetos. Entretanto, a transfiguração Class2ClassTransform pode criar objetos, mesmo que uma classe herde a partir de um classificador.

Se você executar a transformação em um modelo de origem que contém uma classe de entrada ClassA, a transfiguração Class2ClassTransform irá gerar uma classe de saída ClassB. Para registrar a criação de ClassB, a transfiguração Class2ClassTransform cria um registro de mapa de hash cuja chave é igual a (Class2ClassTransform+ClassA->ClassB).

Se a transformação procurar no mapa de hash um registro cuja chave seja igual a (Classifier2ClassifierTransform+ClassA), o registro cuja chave for igual a (Class2ClassTransform+ClassA) será retornado nos resultados da procura, uma vez que a transfiguração Class2ClassTransform herda a partir da transfiguração Classifier2ClassifierTransform.

Impacto do Desenvolvimento Iterativo em Regras de Mapeamento

Em um ambiente de desenvolvimento iterativo, podem ocorrer alterações nos metamodelos de entrada e saída que você especifica em um modelo de mapeamento. Essas alterações também podem afetar os objetos de entrada e saída que você especifica em uma declaração de mapeamento, podendo invalidar as regras de mapeamento correspondentes. Se essa situação ocorrer, você precisará refatorar as regras de mapeamento na declaração de mapeamento. Ao refatorar as regras de mapeamento, você pode especificar se o editor de mapeamentos deve desempenhar um mapeamento de melhor esforço entre os modelos alterados e as regras de mapeamento existentes, seja para deixar as regras de mapeamento existentes desconectadas ou excluir as regras de mapeamento inválidas. Os seguintes tipos de alterações podem exigir a refatoração de uma declaração de mapeamento:
  • O URI de um metamodelo é alterado
  • Metamodelos que contêm os objetos de origem ou destino especificados no modelo de mapeamento são incluídos ou removidos
  • Objetos de entrada e saída em uma declaração de mapeamento são alterados ou removidos
  • Atributos de um objeto no modelo ou metamodelo de origem ou destino são alterados ou removidos
  • As propriedades de contenção ou multiplicidade de atributos de objetos de entrada ou saída são alteradas

Por exemplo, considere um modelo de mapeamento OriginalMappingModel que contém uma declaração de mapeamento Package2Package. O objeto de entrada na declaração de mapeamento está em um metamodelo de entrada denominado MyPackageDefinition, enquanto o objeto de saída na declaração de mapeamento pertence a um metamodelo de saída denominado MyOtherPackageDefinition. A declaração de mapeamento contém uma regra de mapeamento de movimentação que conecta dois atributos de Cadeia, ambos denominados PackageName. Se o URI do metamodelo MyPackageDefinition for alterado, a regra de mapeamento Package2Package se tornará inválida. Da próxima vez que você abrir o modelo de mapeamento, a visualização Problemas exibirá mensagens de erro e, na área do editor de mapeamentos, uma indicação de erro aparecerá na declaração de mapeamento inválida. É necessário especificar um objeto de entrada correto e determinar como o editor de mapeamentos deve refatorar as regras de mapeamento existentes.

Depois de refatorar as regras de mapeamento, você poderá continuar a refinar o modelo de mapeamento e gerar novamente o código de transformação.

Suporte e Opções de Fusão

Se você planeja fundir a saída da transformação em um modelo existente, é necessário que o metamodelo de destino declare uma extensão de tipo de conteúdo CompareMerge. Se a saída da transformação for um modelo UML, a extensão do tipo de conteúdo será definida por padrão, e você não precisará configurar o suporte a fusões. Se o metamodelo do modelo de destino não declarar uma extensão CompareMerge, a transformação irá sobrescrever o modelo de destino.

Extensibilidade de Transformações

Quando o código de transformação é gerado, a tag @generated é incluída nos comentários de elementos Java, como classes, atributos e operações. A tag informa o gerador do código que um elemento de código específico está sob o seu controle, o que permite ao gerador do código atualizar o código referente ao elemento Java no caso de ocorrerem alterações no modelo de mapeamento. Você pode modificar e estender o código de transformação editando ou removendo a tag @generated do elemento Java que deseja alterar.


Feedback