Skip to Content

Forewords
I recently wrote a Yet another webdynpro (ABAP) se16 clone

on a webdynpro SE16-like data browser based on a custom class. Now it’s

time to explain how that class works. This has nothing to do

with webdynpro, only dynamic ABAP programming.The source code for this

stuff is available on my google code page.

Class architecture<br />This

class in named 

zcl_se16_core 

and relys heavily on cl_abap_typedescr

and its subclasses.Its main goal is to implement the programming logic

of se16, leaving to others both the user interface and error handling

(it does a little exception handling, but for the most part only lets

exceptions flow to the caller).
Every instance deals with a specific table (or view) specified in the constructor.

Attributes
public:
TABNAME      – the name of the table currently processed
TABDESC      – table description in current language
FIELDS         – all the table fields
MAINFIELDS – key fields + fields with a control table
USERFIELDS – all the table fields but the client
private:
DESCR         – CL_ABAP_STRUCTDESCR object for the current table
OPTIONS      – select options used to build the dynamic query
TAB_DATA    – table contents

Constructor<br />

First of all I take the table name and get its typedescr. I use a

temporary variable because of type mismatch, and then cast it to global

parameter descr. As in most of this code I leave to the caller the

burden of handling exceptions such as casting errors.In this case I

went a little too far: I should really check that the given structure

is actually a table or a view calling GET_DDIC_HEADER, but as long as I

keep handling exceptions trying to select from a structure is not

really dangerous, only annoying.
The rest of the method simply fills various public attributes: field lists and table details.

method constructor.

  data: field like line of fields,

        desctmp type ref to cl_abap_typedescr.

  call method cl_abap_structdescr=>describe_by_name

    exporting

      p_name         = name

    receiving

      p_descr_ref    = desctmp

    exceptions

      type_not_found = 1.

  if sy-subrc <> 0.

    raise exception type cx_sy_create_object_error.

  endif.

  descr ?= desctmp.

  tabname = name.

  fields = descr->get_ddic_field_list( ).

  userfields = fields.

  delete userfields where fieldname  eq ‘MANDT’.

  loop at userfields into field where keyflag is not initial or checktable is not initial.

    append field to mainfields.

  endloop.

  select single ddtext from dd02t into tabdesc

          where tabname = tabname

            and ddlanguage = sy-langu

            and as4local = ‘A’.

  if sy-subrc ne 0.

    select single ddtext from dd02t into tabdesc

         where tabname = tabname

           and ddlanguage = ‘E’

           and as4local = ‘A’.

  endif.

endmethod.

Adding a select-option<br />I store the select

options in an internal table with a two columns: a fieldname and a

reference to a range table.Method add_sel gets two matching parameters:

if the range table is not empty fills an entry in the options table

after copying the range table to a newly created unnamed

variable.Copying the range slows the program and eats memory but may

save trouble with unbound references.To tell the truth I think this

copying code is pretty useless, but when I coded it I was in a paranoid

mood.<br />Empty range is useless for my purposes, so I simply  ignore them. <br /><div style=”margin-left: 40px”><br />method add_sel.<br />

  data: opt type selopt_el.<br />

  field-symbols:<intab> type table,<outtab> type table.<br />

  try.<br />

      assign rangetab->* to <intab>.<br />

      check <intab> is not initial.<br />

      create data opt-rangetab like <intab>.<br />

      assign opt-rangetab->* to <outtab>.<br />

      <outtab> = <intab>.<br />

      opt-id = id.<br />

      append opt to options.<br />

    catch cx_root.<br />

  endtry.<br />

endmethod.<br /><br /></div>the method for clearing the options table is too trivial to be discussed:<br /><div style=”margin-left: 40px”><br />

method CLEAR_SEL.<br />

  free OPTIONS.<br />

endmethod.

<br /><br /></div>Creating a dynamic variable with all the needed ranges<br />Method

create_data returns a condition string and a reference to a structure

which contains all the needed ranges.This is the trickiest part of the

code.<br />The ABAP syntax for dynamyc where conditions require a

distinct name for every range table used.Usually I use a different

variable for every range, but in this case I’ll use a single complex

variable with several ranges inside, such as:<br /><br /><div style=”margin-left: 40px”>data:begin of range_str,<br />       rcarrid type range of carrid,<br />       rflid   type range of flid,<br />     end of rangetab,<br />     wherecond type string.<br />wherecond = ‘carrid in range_str-rcarrid and flid in range_str-rflid’.<br /></div><br />Since

i need an unknown number of ranges and I don’t know their type in

advance, I’ll create the range structure dynamically, calling the

fields with a simple prefix followed by a number.<br />To create the

dynamic structure I first fill a component table of

type cl_abap_structdescr=>component_table with a record for every 

line of the options table, setting the field type from the range it

references and the field id and generating the name with a counter.<br />Then I create a structdesc object and use it to create the dynamic range structure:<br /><div style=”margin-left: 40px”>struct_type = cl_abap_structdescr=>create( comp_tab ).<br />create data rangestruct type handle struct_type.<br /></div>Finally

I loop on options again to copy the range into the structure, and at

the same time I create the condition string. This needs a lot of field

symbols since I’m working with dynamic objects, but aside from that

it’s pretty straightforward.Of course the condition string only works

with the ranges structure assigned to a field symbol named <sel>.<br />If the options table is empty, the condition string will be emty too and the range structure reference will be unbound.<br /><br /><div style=”margin-left: 40px”>

method create_data.<br />

  field-symbols:<sel> type any,<br />

                <optt> type any table,<br />

                <sel_f> type any table.<br />

  data: struct_type type ref to cl_abap_structdescr,<br />

        comp_tab    type cl_abap_structdescr=>component_table,<br />

        comp        like line of comp_tab.<br />

  data: field_id(4) type n,<br />

        opt type selopt_el,<br />

        fieldname type string.<br />

  loop at options into opt.<br />

    concatenate ‘S_’ field_id into comp-name.<br />

    comp-type ?= cl_abap_elemdescr=>describe_by_data_ref( opt-rangetab ).<br />

    append comp to comp_tab.<br />

    field_id = field_id + 1.<br />

  endloop.<br />

  if comp_tab is not initial.<br />

    struct_type = cl_abap_structdescr=>create( comp_tab ).<br />

    create data rangestruct type handle struct_type.<br />

    assign rangestruct->* to <sel>.<br />

    loop at options into opt.<br />

      assign opt-rangetab->* to <optt>.<br />

      read table comp_tab into comp index sy-tabix.<br />

      concatenate ‘<sel>-‘ comp-name into fieldname.<br />

      if sy-tabix = 1.<br />

        concatenate opt-id ‘in’ fieldname into condition separated by space.<br />

      else.<br />

        concatenate condition ‘and’ opt-id ‘in’ fieldname into condition separated by space.<br />

      endif.<br />

      assign COMPONENT comp-name OF STRUCTURE <sel> to <sel_f>.<br />

      <sel_f> = <optt>.<br />

    endloop.<br />

  endif.<br />

endmethod.

<br /></div>

<br />There’s a limit on the number of fields in the range structure, but

as someone pointed the same holds for the table we’re trying to read

from the database, so it won’t be an issue.<br /><br />Query execution<br />This

is pretty straightforward: first it creates an unnamed table storing

its reference in tab_data, leveraging on the fact that we’re dealing

with DDIC objects, then assigns a couple of field symbols and runs the

query.If the condition string is empty it omits the where condition

completely.<br />That’s all.

I used the same class for a classic report too, maybe I’ll write about it in another post.

To report this post you need to login first.

Be the first to leave a comment

You must be Logged on to comment or reply to a post.

Leave a Reply