E aí, como vai aquele desenvolvimento bizarro em que você precisa identificar qual o tipo de uma determinada variável em tempo de execução? Hoje vamos explorar alguns exemplos do RTTS para fazer esse trabalho sujo.
Lembrando que na Parte 1 você entende a teoria por trás do RTTS e aprende um pouco sobre casting e o operador “=?”. Esta é a Parte 2 e, na Parte 3, iremos analisar como criar variáveis em tempo de execução.
Pegue a sua lupa de Sherlock Homes e vem comigo identificar essas variáveis safadas.
Analisando o tipo de uma váriável
Muita gente conhece o bom e velho DESCRIBE para tentar descobrir alguma coisa sobre as variáveis em tempo de execução. Nós mesmo já falamos um pouco do DESCRIBE TABLE por aqui. Porém, se você ler o HELP do describe, vai ver que a recomendação é usar o RTTS ao invés do DESCRIBE e variações, por conta da flexibilidade que as classes do RTTS proporcionam.
Como ignorar essas recomendação é algo abapossauriano, vamos fuçar um pouco no funcionamento do RTTS para analisar uma variável. No exemplo abaixo a variável “material” está referenciada ao DDIC. Experimente trocar de “mara-matnr” para outros tipos para ver diferentes resultados:
DATA: material TYPE mara-matnr,
texto TYPE string.
DATA: relative_name TYPE string.
DATA: o_typedescr TYPE REF TO cl_abap_typedescr.
DATA: x031l TYPE TABLE OF x031l,
x030l TYPE x030l.
* Criando o objeto para análise. Este objeto fará referência ao
* tipo da variável "material".
o_typedescr = cl_abap_typedescr=>describe_by_data( material ).
* Nome absoluto contém o nome + identificador. No caso, será \TYPE=MATNR
* que indica que este objeto é referente a um TYPE MATNR(elemento de dados).
* Experimente trocar o tipo do campo para PSTAT, e você verá que o TYPE muda
* para o elemento de dados, que é o PSTAT_D.
WRITE / o_typedescr->absolute_name.
* Nome utilizado para referência no programa.
relative_name = o_typedescr->get_relative_name( ).
WRITE / relative_name.
* Métodos que só funcionam se o tipo referenciado veio do DDIC.
* Se você trocar de "material" para "texto" na hora de chamar o
* método describe_by_data, irá tomar um DUMP na cabeça.
x031l[] = o_typedescr->get_ddic_object( ).
x030l = o_typedescr->get_ddic_header( ).
* Tipo da Variável, neste caso C de CHAR
WRITE: / o_typedescr->type_kind.
* Tamanho da variável, para o elemento de dados MATNR - 18
WRITE: / o_typedescr->length.
Neste caso utilizamos o método describe_by_data, mas você pode utilizar outros métodos para buscar a instância, como o describe_by_name. No caso do e describe_by_name, você teria que passar o nome do tipo direto – “MATNR” ( pelo describe_by_data voccê passa a variável).
Para quem não está acostumado com OO, este exemplo pode levar a uma pergunta importante…
Ué, mas onde foi parar o CREATE OBJECT?
Quando você precisa utilizar um objeto, nem sempre isso quer dizer que você precisará criá-lo. É extremamente comum no mundo do ABAP Objects delegar a atividade de criação para algum método de alguma classe. Tipo aqueles chefes que só delegam, delegam, delegam e não fazem nada.
Sério agora: no caso do exemplo anterior, quem irá executar o “CREATE OBJECT” é o método “cl_abap_typedescr=>describe_by_data“. Esse método é um método estático, ou seja, não precisa ser acessado a partir de uma instância. Se você não entendeu nada, imagine que chamar um método estático público de uma classe da SE24, é igual a chamar uma função, no sentido de ser um pedaço de código que está em algum lugar e que pode ser acessado por qualquer programa.
Pelo lado do design do RTTS, imagine que você está fazendo a seguinte requisição: “Hey RTTS, me devolva a instância certa, preenchida com todos os dados dessa variável que estou te enviando“. Saber disso, por hora, é o suficiente 🙂 .
Para os curiosos de plantão, existe uma construção clássica (design pattern) em OO que se chama Factory. Ela abusa de métodos estáticos para que a classe crie a instância da melhor maneira possível. Dê uma olhada neste link para saber como funciona, com exemplos em ABAP. Diz a lenda que eu vou fazer um post disso algum dia, mas é só uma lenda urbana e pode ser mentira, tipo bicho papão, saci pererê, geisy arruda…
Descobrindo os campos de uma estrutura
Agora que você entendeu como instanciar objetos do RTTS, e como fazer análises em uma variável simples, está na hora de utilizar a maravilinda análise de estruturas.
Manja quando você tem aquela estrutura maldita e precisa descobrir se ela tem o campo que você precisa, na posição que você precisa, do tipo que você precisa, do elemento de dados que você precisa, aaaaaaaAAaAaAAa? Pois seus problemas acabaram! 🙄 Descobrir os campos de uma estrutura, sua posição e outras parafernálias nunca foi tão fácil!
DATA: estrutura TYPE mara.
DATA: componentes TYPE cl_abap_structdescr=>component_table.
DATA: structdescr TYPE REF TO cl_abap_structdescr,
campodescr TYPE REF TO cl_abap_datadescr.
FIELD-SYMBOLS <component> LIKE LINE OF cl_abap_structdescr=>components.
structdescr ?= cl_abap_structdescr=>describe_by_data( estrutura ).
* Aqui você recebe a lista de todos os componentes da estrutura, com outros
* objetos já referenciados de acordo com o tipo do campo. (abra no debug para entender)
* Com isso você pode saber O QUE VOCÊ QUISER sobre QUALQUER CAMPO da estrutura.
* Não é feitiçaria, é SAPLoLogia (só pra rimar)
componentes = structdescr->get_components( ).
* Vamos analisar um campo específico da estrutura:
campodescr = structdescr->get_component_type( 'MATNR' ). "Troque o campo para experimentar!"
WRITE: /01 'MATNR',
10 'É do tipo',
20 campodescr->type_kind.
*Ah, mas tudo que eu quero é um lista dos campos e já era!
LOOP AT structdescr->components ASSIGNING <component>.
* Tá, toma aí
WRITE: /01 <component>-name,
20 <component>-decimals,
30 <component>-length,
40 <component>-type_kind,
50 sy-tabix. "posição na estrutura
ENDLOOP.
Viu ali o operador =? entrando em cena? Isso acontece porque o método “describe_by_data” está declarado na classe pai CL_ABAP_TYPEDESCR, e esse método retorna um parâmetro do tipo TYPEDESCR. Porém, como programadores espertos que somos, sabemos que AQUELE “describe_by_data” da classe CL_ABAP_STRUCTDESCR vai retornar uma instância da classe CL_ABAP_STRUCTDESCR. É aí que o =? entra em jogo, e dizemos para o verificador de sintaxe não encher o saco porque a gente sabe o que está fazendo.
AI MEU DEUS MAURICIO, NÃO ENTENDI NADA DESSE ÚLTIMO PARÁGRAFO!!!1!!1!!1!!
Leia a Parte 1. Se mesmo assim não entendeu, pergunta pra gente aí nos comentários 🙂 .
Com a classe CL_ABAP_STRUCTDESCR você descobre os campos da estrutura, mas para analisar um campo específico, vai ter que usar a classe para o tipo específico daquele campo. Debugue o exemplo que você vai me entender!
Bom, espero que agora a hierarquia de classes esteja fazendo um pouco mais de sentido para você 😛
Análises em Tabelas
E, por fim, vamos analisar uma tabela interna. Acho que você já sacou que a classe CL_ABAP_TABLEDESCR vai fazer uma análise geral na tabela, mas você vai precisar utilizar a hierarquia de classes para analisar o campos (CL_ABAP_DATADESCR e derivados) de uma linha específica (CL_ABAP_STRUCTDESCR).
DATA: tabela TYPE TABLE OF mara.
DATA: tabledescr TYPE REF TO cl_abap_tabledescr,
structdescr TYPE REF TO cl_abap_structdescr.
FIELD-SYMBOLS <key> LIKE LINE OF tabledescr->key.
tabledescr ?= cl_abap_tabledescr=>describe_by_data( tabela ).
* Vamos fuçar na linha da tabela
structdescr ?= tabledescr->get_table_line_type( ).
* Note que o programa faz um downcast do tipo que GET_TABLE_LINE_TYPE retorna para o
* objeto STRUCTDESCR.
* E aqui você pode usar o exemplo de estruturas para fuçar no objeto STRUCTDESCR.
* [insira o código do exemplo anterior aqui]
* Campos da tabela
LOOP AT tabledescr->key ASSIGNING <key> .
WRITE / sy-tabix. "Posição do Campo
WRITE 20 <key>-name. "Números da Mega-Sena (quem sabe assim algúem testa este exemplo!)
ENDLOOP.
* Tem mais um monte de atributos que descrevem outras coisas da tabela
* interna. Explore!
É meu, tô falando… esse tal RTTS é pior que paparazzi na praia de copacabana.
Guarde sua lupa porque por hoje chega pessoal. Espero que os exemplos estejam claros o suficiente para que você possa fazer seus próprios experimentos com as classes do RTTS. Não adianta ficar lendo, tem que executar, testar e estudar!
Abraços a todos aqueles que usam a boina do Sherlock Holmes.
Esses dias eu estava com preguiça de procurar no google o nome dessas classes quando apareceu esse tweet no meu feed 😉 foi uma mão na roda!
Eu quera analisar uns dados e como eu sou preguiçoso demais pra aprender as funcionalidades todas do excel… fiz um programa que eu copiava uma planilha e ele gerava um alv pra mim igualzinho 😀
Aí sim heim Fawcs! Fico feliz de ter ajudado. RTTS está longe de ser algo novo, mas é bem legal e merece esta sequência de posts.
Abraços! 🙂
É mano, ABAP Zombie superando o Google!
Falando em algo novo… Sabe se existe algum repositório ou uma maneira de saber a partir de qual versão uma classe/função está disponível no SAP? No momento a maneira que eu tenho de descobrir isso é logando num ambiente 4.7 e procurando!
Putz não sei… deve dar para saber olhando a data de criação do objeto abap.
Mas só pesquisando para saber!
Fala Abaper’s!!
seguinte, utilizei as dicas aqui e tenho outra relacionada:
* Métodos que só funcionam se o tipo referenciado veio do DDIC.
* Se você trocar de “material” para “texto” na hora de chamar o
* método describe_by_data, irá tomar um DUMP na cabeça.
Para evitar o Dump é só verificar se campo verificado vem do ddic, da seguinte forma:
“Recebe o nome do dominio (somente para campos do dicionário)
o_typedescr = cl_abap_typedescr=>describe_by_data( material ).
“Verifica se tipo é do dicionário
CHECK o_typedescr->is_ddic_type( ) EQ abap_true.
é isso ai, espero ter ajudado!
Abraços!
Desconsiderem essa parte:
““Recebe o nome do dominio (somente para campos do dicionário)” heheheheeh
abraços