SAP Professional Journal
Use this table introspector utility class to dynamically generate a formatted representation of internal tables at run time, with support for subsequent output to various formats.
Key Concept
The ABAP Run Time Type Services (RTTS) library is an object-oriented framework that allows type information on existing objects to be deduced via Run Time Type Information (RTTI), as well as new data types to be defined via Run Time Type Creation (RTTC).
It is a common requirement to format internal tables for output to either a flat file or as an attachment to an email. To do this properly, however, the programmer is usually forced to statically define appropriate structures at development time in which to store the data. In addition, the programmer must spend time manipulating the data (i.e., creating and formatting a header line, enclosing string values in quotes, converting internal date and time formats to a suitable external representation, and inserting a delimiter between fields). These are all common operations.
In this article I present a table introspector utility class that I have developed to encapsulate behavior related to storing, manipulating, and formatting internal tables in preparation for output. The output table’s structure is created dynamically at run time, and a header line with field descriptions is built through exploiting information stored in the Data Dictionary. Support is offered for subsequent output to file, email, and SAP List Viewer (ALV) formats. You can download the code as follows:
Click here to see the source code for the demonstration program z_introspector_demo_source.
Click here to see the source code for the table introspector utility class zcl_bc_table_introspector_source.
Click here to see the source code for the table introspector utility class and dependent exception class table introspector utility class readme.
Table Introspector Demonstration Program
To show the basic capabilities of the table introspector class, I have developed a simple demonstration program. The data used to populate the internal table at runtime is sourced from the Flight Data Model that is often used in examples provided by SAP. A selection screen (Figure 1) allows input for selecting flights based on source airport, destination airport, and airline. Additionally, several output options are presented to the user that demonstrate some of the formatting options available to them by way of the introspector class.

Figure 1
Demonstration program selection screen
Let’s look at the main method (Figure 2) used in the program’s local class to see the flow of control at a high level.

Figure 2
Demonstration program
Prepare by instantiating the local class first. Once this step is done, proceed with the following method calls:
1) The first call is to load_data, which simply calls the BAPI function bapi_flight_getlist to populate the internal table you need (Figure 3). The internal table is stored as an instance attribute in the local class.

Figure 3
Populate the internal table with flight list data
2) The second method is responsible for loading the table introspection itself. The instantiation context of the table introspector class is set to private. Therefore, you need a way to otherwise bootstrap or initialize the class. You can use one of the two factory methods provided to do this. Here, because you are working with an internal table, you will naturally call the factory method factory_using_table. Later when I explore the implementation you will see that a second factory method also exists. User-specified formatting options are provided as well as the flight data that is passed to this method, A table introspector object is then returned and stored as an instance attribute of the local class. In detail this looks like what you see in Figure 4.

Figure 4
Call the table introspector utility class
3) The next method call is to the download method. Here you call the standard method cl_gui_frontend_services=>gui_download to facilitate exporting the data to a file. In this respect, the implementation is unremarkable. Where the implementation departs from common practice is the way in which the table is provided. It is here that you turn to the table introspector object that was previously initialized. The public instance method gives a clue as to the various formats you can request, each suitable for a different purpose. In this case, my purpose is to download the table to a file. The method I call on the table introspector object, then, is get_file_rendition (Figure 5).

Figure 5
Retrieving the internal table representation suitable for file output
I am not finished with the table introspector object just yet. I also want to display a simple ALV grid to provide a visual representation to the user’s screen of the same data that has just been downloaded. In this case, the display method of the local class is implemented to call the introspector method get_alv_rendition. The exported parameter is an object that represents the state of the introspector in ALV form using standard class cl_salv_table. This object is used in a standard fashion to export data to the screen (Figure 6).

Figure 6
Retrieving the internal table presentation suitable for ALV output
The file downloaded to disk, when opened in a text editor, looks like what you see in Figure 7.

Figure 7
Internal table as file output
The output to the screen on completion of the transaction looks like Figure 8.

Figure 8
Internal table as ALV output
The nice thing about abstracting away implementation complexities inside a utility class is that, when properly designed, it offers the caller a simple and flexible interface with which to interact. The demonstration program above shows this is true of the table introspector class. It is simple to call, and offers formatting and rendering capabilities for internal tables whose structure is only given at run time. Developers are now freed from these concerns, and can focus their efforts on the business logic of the application.
Next, I turn my attention to implementation details.
Table Introspector Utility Class Implementation Details
At a high level, the basic building blocks of the table introspector class can be determined from looking at the demonstration program. You know already that factory methods are used to initialize the class. These factory methods must perform some kind of initialization related to table introspection, the results of which are then made available to the caller via publicly accessible methods that return one of the available representations.
But how does it do this in detail? The answer lies in the use of the ABAP Run Time Type Services (RTTS) library. You can deduce type information on existing objects via Run Time Type Information (RTTI), as well as allow new data types to be defined via Run Time Type Creation (RTTC). Both capabilities of the framework are exploited by the factory methods to perform the necessary operations to transform a given internal table into a suitable format for output.
In examining the implementation details, I will perform a walkthrough of the code as it is called in the demonstration program. Of course, it would be impractical to describe every line of code here. Instead, I want to focus on the type of processing that is done using the RTTS library and how the changing structures at runtime move you closer to your goal of populating the source internal table in a format suitable for output.
Let’s begin with the instantiation process, which is where the heavy lifting is done. The table introspector class is privately instantiated, which prevents object references from being created directly. Instead, you can use factory methods to initialize the class. This provides several benefits. For example, you can maintain strict control over complex initialization processes, ensuring they are carried out correctly. You are also free to change the implementation details without breaking dependent code. Additionally, you can offer multiple ways to initialize the class.
I mentioned previously that the table introspector offers two factory methods. The factory method you are familiar with from the demonstration program is factory_using_table. The other factory method we have not come across yet is called factory_using_sql. Both methods are very similar. However, instead of accepting an internal table as input, the latter method accepts components of an Open SQL query from which the internal table is then constructed.
As this walkthrough is based on the demonstration program, I will focus on the factory_using_table method. The signature of this method appears in Figure 9. An arbitrary internal table, together with formatting options, is accepted as input. The introspector object is returned to the caller when the method is complete. Class-based exception handling is used to encapsulate and propagate exception conditions.

Figure 9
Signature of internal table factory method
The high-level code for the factory_using_table method is presented in Figure 10.

Figure 10
Internal table factory method high level flow of control
I describe each of these methods:
1. The first method call is to get_internal_table_metadata. This method returns a flat collection of table components, filtered according to any supplied columns.
Although simple calls to the available methods in classes cl_abap_structdescr and cl_abap_tabledescr are used to complete much of the work in this method, it is worthwhile to examine the case in which the structure of the underlying internal table is not flat. This is because special processing needs to take place in this case.
Although this case more frequently arises when the internal table is typed according to a transparent table, in the demonstration program I have chosen to artificially declare a nested type definition for the storing of flight list data (Figure 11).

Figure 11
Nested type structure definition for flight list data
The component collection is then represented in the ABAP debugger as shown in Figure 12. Notice that the last row is flagged as an include structure, and is typed accordingly.

Figure 12
Initial internal table components
Due to processing demands both in this method and in other subsequent methods, it is simpler to store and work with table representations that consist only of flat collection of elements. For example, in this method you have to restrict the fields according to the supplied column filter. To do this, any include structure must be resolved into its component elements to determine whether each such element should be filtered.
This resolution process must take into account the fact that each such structure can contain other structures, which themselves can contain yet other structures again, and so on to an indefinite reach. Usually, when the solution to a problem depends, as this does, on the solution to smaller instances of the same problem, the use of recursion is a natural fit. The initial call to the method recursive_filter_columns looks like what you see in Figure 13.

Figure 13
Recursively filtering internal table components
Within this method, look at each component to determine whether it is an element or a structure. If it is an element, append the component to a new collection (if it exists in the supplied column filter). Alternatively, if the component is a structure, the collection of components for that structure is recursively passed to recursive_filter_columns. The stop condition of the recursion is reached whenever all the structures in a given branch have been flattened. At this point the recursion starts to unwind, passing back the filtered components and continuing to process all remaining branches. Figure 14 shows how the component type is dynamically derived at run time, and also shows how the recursive call is made when the component type is a structure.

Figure 14
Recursively processing nested structures
The call stack shows the depth of the recursion at any given point in time as shown in Figure 15.

Figure 15
Call stack during recursion
After the recursion has done its job, you are left with the components that now are used to present the data from this point forward (Figure 16).

Figure 16
Filtered and flattened components
2. The next method called is populate_header. This method uses the description of the internal table’s underlying structure (returned by the method get_internal_table_metadata) to populate a new structure that serves as the header line for the output table.
For each component in the structure, a corresponding character-based component is created in which the Data Dictionary description of the original component is stored (Figure 17).

Figure 17
Populating header structure with Data Dictionary description
The populated structure looks like what you see in Figure 18.

Figure 18
Populated header structure
3. The populate_table method is called to populate the new internal table (Figure 19). It works by creating source and target structures to copy data from the source internal table to the target (column filtered) internal table.

Figure 19
Populating target internal table with data from source internal table
The populated table looks like what you see in Figure 20.

Figure 20
Populated target internal table
4. The build_formatted_components method is responsible for building both header and record structures interleaved with delimiter fields. Figure 21 shows how the interleaved header components are built. A similar logic is used to build the interleaved record components. If column alignment is selected, the header elements must assume the output length of the corresponding source elements. If column alignment is not selected, the header element output length defaults to the long description length from the Data Dictionary in order to capture the complete description.

Figure 21
Building a delimiter-interleaved header structure
The components for the header structure look like what you see in Figure 22.

Figure 22
Built delimiter-interleaved header structure
5. The populate_formatted_header method populates the interleaved header structure created in method build_components. The first step is to transfer the Data Dictionary values stored for the header returned in populate_header. The partially complete structure is shown in Figure 23.

Figure 23
Partially populated delimiter-interleaved header structure
Then you iterate through each component of the interleaved structure. If the component is a field, you apply basic formatting options to properly render the descriptions if the enclosed strings option is chosen. If the component is a delimiter, insert the delimiter (Figure 24).

Figure 24
Populating a delimiter-interleaved header structure
The complete structure looks like what you see in Figure 25. Notice that some fields have been reduced in output length to allow for quotation marks.

Figure 25
Populated delimiter-interleaved header structure
6. The populate_formatted_table method populates the interleaved record structure created in method build_components (Figure 26). Each field value is copied from the original table returned in populate_table. Formatting is applied to each field depending on its type and selected options.

Figure 26
Populate a delimiter-interleaved record structure
When finished, each field is populated and the delimiters have been injected (Figures 27 and 28).

Figure 27
Populated delmiiter-interleaved record structure

Figure 28
Populated delimiter-interleaved table
7. Finally, you are in a position to instantiate the table introspector class. The constructor loads two configuration options as well as representations of the formatted table (necessary for interrogation by getter methods after the class is instantiated) as shown in Figure 29.

Figure 29
Constructor for table introspector class
Calling the Getter Methods
Once you have an instance of the table introspector class returned to you, you are free to call one of the available getter methods that return the desired format. The getter methods each rely on the table representations passed to the instance via the factory method.
The available getter methods (as well as the other methods discussed above) can be seen in the object list for the utility class shown in Figure 30.

Figure 30
Object list for table introspector class
David Duncombe
David Duncombe is an SAP Analyst at Powerlink Queensland. He has more than five years experience as a developer, and specializes in ABAP Objects and SAP NetWeaver Process Integration. He holds an honors degree in information technology from Queensland University of Technology.
You may contact the author at duncombe007@hotmail.com.
If you have comments about this article or publication, or would like to submit an article idea, please contact the editor.