Skip to Content

Below is source code of program to generate SAP code statistic for Gource visualization This blog post how to use geerated statistic file for generating video vizualization of it can be found here – SAP code visualization – make a cool video of your project evolution.

Just create new Z-program in SAP called ZAR_GENERATE_CODE_STATISTIC using SE38 or SE80 transaction and copy/paste line belows into it (line numbers would be not copied).

*&---------------------------------------------------------------------*
 *& Report  ZAR_GENERATE_CODE_STATISTIC
 *& Generate SAP code statistic for Gource visualization
 *&---------------------------------------------------------------------*
 *& Author: Artem Ruzak
 *& Since: 24 Jan 2012
 *&---------------------------------------------------------------------*
 REPORT  zar_generate_code_statistic.
 *&---------------------------------------------------------------------*
 *&  TYPE-DEFINITIONS
 *&---------------------------------------------------------------------*
 TYPE-POOLS: trwbo, strhi, trlck, trsel, ctslg, slis, sctsc.
 CLASS cl_ixml DEFINITION LOAD.
 TABLES sscrfields.
 TYPES: BEGIN OF xml_str,
         data(132) TYPE c,
        END OF xml_str.
 TYPES: BEGIN OF xml_line,
         data(256) TYPE x,
        END OF xml_line.
 DATA:
     lo_conv TYPE REF TO cl_abap_conv_out_ce,
     lv_objtype TYPE trobjtype,
     lt_string_components TYPE TABLE OF swastrtab,
     ls_string_components TYPE swastrtab,
     lt_xml_str TYPE TABLE OF xml_str,
     lo_ixml TYPE REF TO if_ixml,
     lo_streamfactory TYPE REF TO if_ixml_stream_factory,
     lo_istream TYPE REF TO if_ixml_istream,
     lo_parser TYPE REF TO if_ixml_parser,
     parseerror TYPE REF TO if_ixml_parse_error,
     lo_ostream TYPE REF TO if_ixml_ostream,
     lo_renderer TYPE REF TO if_ixml_renderer,
     lo_xml_doc TYPE REF TO if_ixml_document,
     lo_root TYPE REF TO if_ixml_element,
     lo_node_pointer TYPE REF TO if_ixml_node,
     lo_comment TYPE REF TO if_ixml_comment,
     lo_element TYPE REF TO if_ixml_element,
     lo_new_element TYPE REF TO if_ixml_element,
     lo_path_element TYPE REF TO if_ixml_element,
     lo_attribute TYPE REF TO if_ixml_attribute,
     lo_filter1  TYPE REF TO if_ixml_node_filter,
     lo_filter2  TYPE REF TO if_ixml_node_filter,
     lo_filter3  TYPE REF TO if_ixml_node_filter,
     lo_filter4  TYPE REF TO if_ixml_node_filter,
     lo_iterator TYPE REF TO if_ixml_node_iterator,
     lv_value TYPE string,
     lv_value2 TYPE string,
     lv_func_desc LIKE  ko013-trfunctn_l,
     lt_name TYPE TABLE OF char100,
     lv_name TYPE string,
     lv_obj_name TYPE string,
     lv_subobj_name TYPE string,
     lo_text TYPE REF TO if_ixml_text,
     lv_xml_string  TYPE string,
     lv_xml_xstring  TYPE xstring,
     lt_xml_table TYPE TABLE OF xml_line,
     lv_xml_size  TYPE i,
     ls_request_header TYPE trwbo_request_header,
     lt_request_headers TYPE trwbo_request_headers,
     ls_request TYPE trwbo_request,
     gv_sysk_pattern LIKE e070-trkorr,
     system_name TYPE sysysid,
     system_type TYPE sysysid,
     gt_object_texts LIKE ko100 OCCURS 10,
     ls_object_texts LIKE ko100,
     lt_objects TYPE TABLE OF e071,
     ls_objects TYPE e071,
     ls_selection TYPE trwbo_selection,
     ls_popup TYPE  strhi_popup,
     ls_ranges TYPE  trsel_ts_ranges,
     lv_filename TYPE string,
     lv_req_num TYPE i,
     lv_perc_tmp TYPE f,
     lv_perc TYPE i VALUE 0,
     str TYPE string,
     line TYPE i,
     column TYPE i,
     count TYPE i,
     index TYPE i,
     lv_pointer_tstm TYPE timestamp,
     lv_tstm TYPE timestamp,
     lv_uname TYPE xubname,
     ls_uaddr TYPE bapiaddr3,
     lt_bapiret TYPE bapirettab,
     lt_obj_type TYPE TABLE OF string,
     lv_obj_type TYPE string,
     lv_short_name_len TYPE i,
     lv_short_name TYPE c LENGTH 8.
 SELECTION-SCREEN BEGIN OF BLOCK mysel WITH FRAME TITLE tit.
 SELECTION-SCREEN BEGIN OF LINE.
 SELECTION-SCREEN COMMENT 1(15) pref.
 PARAMETERS: p1(30) TYPE c LOWER CASE.
 SELECTION-SCREEN END OF LINE.
 SELECTION-SCREEN BEGIN OF LINE.
 SELECTION-SCREEN COMMENT 1(15) file.
 PARAMETERS: f1 LIKE ibipparms-path VISIBLE LENGTH 30 .
 SELECTION-SCREEN PUSHBUTTON 50(13) but2 USER-COMMAND cli2.
 SELECTION-SCREEN END OF LINE.
 SELECTION-SCREEN BEGIN OF LINE.
 PARAMETERS: c1 TYPE xfeld AS CHECKBOX.
 SELECTION-SCREEN COMMENT 3(45) append.
 SELECTION-SCREEN END OF LINE.
 SELECTION-SCREEN BEGIN OF LINE.
 PARAMETERS: c2_note TYPE xfeld AS CHECKBOX.
 SELECTION-SCREEN COMMENT 3(45) no_note.
 SELECTION-SCREEN END OF LINE.
 SELECTION-SCREEN BEGIN OF LINE.
 PARAMETERS: c3_shnam TYPE xfeld AS CHECKBOX DEFAULT 'X'.
 SELECTION-SCREEN COMMENT 3(45) shrtnam.
 SELECTION-SCREEN END OF LINE.
 SELECTION-SCREEN END OF BLOCK mysel.
 SELECTION-SCREEN BEGIN OF BLOCK obj_sel WITH FRAME TITLE sel_t.
 SELECTION-SCREEN BEGIN OF LINE.
 SELECTION-SCREEN COMMENT 1(20) obj_seli.
 SELECT-OPTIONS s_obj_i FOR lv_objtype.
 SELECTION-SCREEN END OF LINE.
 SELECTION-SCREEN BEGIN OF LINE.
 SELECTION-SCREEN COMMENT 1(20) obj_sele.
 SELECT-OPTIONS s_obj_e FOR lv_objtype.
 SELECTION-SCREEN END OF LINE.
 SELECTION-SCREEN END OF BLOCK obj_sel.
 AT SELECTION-SCREEN.
   CASE sscrfields-ucomm.
     WHEN 'CLI2'. "select file from disk
       CALL FUNCTION 'F4_FILENAME'
         EXPORTING
           program_name  = syst-cprog
           dynpro_number = syst-dynnr
           field_name    = ' '
         IMPORTING
           file_name     = f1.
 *    WHEN 'ONLI'.
 *      .
 *    WHEN OTHERS.
 *      LEAVE.
   ENDCASE.
 INITIALIZATION.
   tit = 'General generation settings'.
   pref = 'Path prefix'.
   file = 'Save to file'.
   append = 'Merge with existing'.
   no_note = 'Ignore Note corrections'.
   shrtnam = 'Shorten object type description'.
   but2 = 'Choose file...'.
   sel_t = 'Step 1 - Select objects types that shall be included/excluded from statistic'.
   obj_seli = 'Objects included'.
   obj_sele = 'Objects excluded'.
   p1 = '/SAP'.
 START-OF-SELECTION.
   "Remove all corrections from object processing
   s_obj_e-sign = 'I'.
   s_obj_e-option = 'EQ'.
   s_obj_e-low = 'CORR'.
   APPEND s_obj_e.
   CALL FUNCTION 'TR_SYS_PARAMS'
     IMPORTING
       systemname = system_name
       systemtype = system_type.
   gv_sysk_pattern(3)   = system_name(3).
   gv_sysk_pattern+3(2) = 'K*'.
   "Read texts of object list headings
   CALL FUNCTION 'TR_OBJECT_TABLE'
     TABLES
       wt_object_text = gt_object_texts.
   SORT gt_object_texts BY pgmid object.
   ls_selection-trkorrpattern = gv_sysk_pattern.
   ls_selection-reqfunctions  = 'CDEFGKMOPQRSTWX'. "all types of requests.
   ls_selection-reqstatus     = 'DLP'.
   CALL FUNCTION 'TRINT_SELECT_REQUESTS'
     EXPORTING
       is_selection           = ls_selection
       iv_complete_projects   = 'X'
       iv_via_selscreen       = 'X'
       is_popup               = ls_popup
       iv_title               = 'Step 2 - Select Requests to collect statistic'
     IMPORTING
       et_requests            = lt_request_headers
     CHANGING
       cs_ranges              = ls_ranges
     EXCEPTIONS
       action_aborted_by_user = 1.
   IF sy-subrc = 1.
     MESSAGE e653(tk)  RAISING action_aborted_by_user.
   ENDIF.
   "Now complete the projects (we didn't allow TRINT_SELECT_REQUESTS
   "to do this due to the highlighting)
   CALL FUNCTION 'TRINT_FOLLOW_STRKORR_FORWARD'
     CHANGING
       ct_requests = lt_request_headers.
   CALL FUNCTION 'TRINT_FOLLOW_STRKORR_BACKWARD'
     CHANGING
       ct_requests = lt_request_headers.
   "stop processing if nothing selected
   CHECK lt_request_headers[] IS NOT INITIAL.
   "prepare objects needed for XML creation
   "create iXML instance
   lo_ixml = cl_ixml=>create( ).
   "get XML document
   lo_xml_doc = lo_ixml->create_document( ).
   lo_streamfactory = lo_ixml->create_stream_factory( ).
   "if soure file contains XML and data shall be merged -- read source file
   IF f1 IS NOT INITIAL AND c1 IS NOT INITIAL.
     "upload a file from the client's workstation
     CALL FUNCTION 'WS_UPLOAD'
       EXPORTING
         filename   = f1
         filetype   = 'BIN'
       IMPORTING
         filelength = lv_xml_size
       TABLES
         data_tab   = lt_xml_table
       EXCEPTIONS
         OTHERS     = 11.
     IF sy-subrc IS NOT INITIAL.
       WRITE: / 'Failed to load file'.
       RETURN.
     ENDIF.
     lo_istream = lo_streamfactory->create_istream_itable( table = lt_xml_table
                                               size  = lv_xml_size ).
     "create the iXML parser:
     CALL METHOD lo_ixml->create_parser
       EXPORTING
         stream_factory = lo_streamfactory
         istream        = lo_istream
         document       = lo_xml_doc
       RECEIVING
         rval           = lo_parser.
     "parse document & check if error happened
     IF lo_parser->parse( ) NE 0.
       IF lo_parser->num_errors( ) NE 0.
         count = lo_parser->num_errors( ).
         WRITE: / 'During XML parsing ', count, ' parse errors have occured:'.
         index = 0.
         WHILE index < count.
           parseerror = lo_parser->get_error( index = index ).
           line = parseerror->get_line( ).
           column = parseerror->get_column( ).
           str = parseerror->get_reason( ).
           WRITE: / 'Line: ', line, ' Column: ', column, '  ', str.
           index = index + 1.
         ENDWHILE.
       ENDIF.
       RETURN.
     ENDIF.
     "get log node for processing:
     lo_root = lo_xml_doc->find_from_name( name = 'log' ).
     IF lo_root IS NOT BOUND.
       WRITE: / 'Cannot find <log> node in XML file.'.
       RETURN.
     ENDIF.
   ELSE.
     "create new XML document
     "add carriage return at the beginning of document  <--- required by Gource's implementation of XML log parsing
     lo_comment = lo_xml_doc->create_comment( '#CR-LF#' ).
     lo_xml_doc->append_child( lo_comment ).
     "create root element:
     lo_root = lo_xml_doc->create_element( name = 'log' ).
     lo_xml_doc->append_child( lo_root ).
   ENDIF.
   "find first logentry node for processing:
   lo_filter1  = lo_xml_doc->create_filter_name( name = 'log' ).
   "and set it as filter for parent node:
   lo_filter2 = lo_xml_doc->create_filter_parent( lo_filter1 ).
   "find nodes
   lo_filter3  = lo_xml_doc->create_filter_name( name = 'logentry' ).
   "combine filters together
   lo_filter4  = lo_xml_doc->create_filter_and( filter1 = lo_filter2 filter2 = lo_filter3 ).
   "iterate over selected nodes:
   lo_iterator = lo_xml_doc->create_iterator_filtered( lo_filter4 ).
   lo_node_pointer = lo_iterator->get_next( ).
   IF lo_node_pointer IS BOUND.
     "read timestamp of first logentry as we shall 'merge' SAP changes into currently existing change log
     lo_element ?= lo_node_pointer.
     FREE lo_new_element.
     lo_new_element = lo_element->find_from_name( name = 'date' depth = 1 ).
     IF lo_new_element IS BOUND.
       lv_value = lo_new_element->get_value( ).
     ELSE.
       WRITE: / 'Do not found <date> tag inside <logentry>'.
       RETURN.
     ENDIF.
     "convert value into date and time:
     TRY.
         CALL METHOD cl_gdt_conversion=>date_time_inbound
           EXPORTING
             im_value       = lv_value
           IMPORTING
             ex_value_short = lv_pointer_tstm.
       CATCH cx_gdt_conversion .
         WRITE: / 'Failed to convert into time value ', lv_value.
         RETURN.
     ENDTRY.
   ELSE.
     "set pointer to comment which will recieve all data
     "since no logentry found  - insert all nodes just above this comment node:
     lo_comment = lo_xml_doc->create_comment( 'Generated by Artem Ruzak' ).
     lo_node_pointer ?= lo_comment.
     lo_root->append_child( lo_node_pointer ).
     "add carriage return before closing log tag <--- required by Gource's implementation of XML log parsing
     lo_comment = lo_xml_doc->create_comment( '#CR-LF#' ).
     lo_root->append_child( lo_comment ).
     "set pointer date and time to maximum:
     lv_pointer_tstm = '99991231235959'.
   ENDIF.
   SORT lt_request_headers BY as4date as4time.
   lv_req_num = LINES( lt_request_headers ).
   "get request objects and process them
   LOOP AT lt_request_headers INTO ls_request_header.
     CONVERT DATE ls_request_header-as4date TIME ls_request_header-as4time
        INTO TIME STAMP lv_tstm TIME ZONE sy-zonlo.
     "check at what place change shall be added:
     IF lv_pointer_tstm < lv_tstm.
       "find next changerequest that is higher that current one
       WHILE lv_pointer_tstm < lv_tstm.
         lo_node_pointer = lo_iterator->get_next( ).
         IF lo_node_pointer IS BOUND.
           "read timestamp of next logentry
           FREE: lo_element, lo_new_element.
           lo_element ?= lo_node_pointer.
           lo_new_element = lo_element->find_from_name( name = 'date' depth = 1 ).
           IF lo_new_element IS BOUND.
             lv_value = lo_new_element->get_value( ).
           ELSE.
             WRITE: / 'Do not found <date> tag inside <logentry>'.
             RETURN.
           ENDIF.
           "convert value into date and time:
           TRY.
               CALL METHOD cl_gdt_conversion=>date_time_inbound
                 EXPORTING
                   im_value       = lv_value
                 IMPORTING
                   ex_value_short = lv_pointer_tstm.
             CATCH cx_gdt_conversion .
               WRITE: / 'Failed to convert into time value ', lv_value.
               RETURN.
           ENDTRY.
         ELSE.
           "set pointer to comment which will recieve all data
           "since no logentry found  - insert all nodes just above this comment node:
           lo_comment = lo_xml_doc->create_comment( 'Generated by Artem Ruzak' ).
           lo_node_pointer ?= lo_comment.
           lo_root->append_child( lo_node_pointer ).
           "set pointer date and time to maximum:
           lv_pointer_tstm = '99991231235959'.
         ENDIF.
       ENDWHILE.
     ENDIF.
     "set progress indicator
     lv_perc_tmp = ( sy-tabix / lv_req_num ) * 100.
     IF TRUNC( lv_perc_tmp ) > lv_perc.
       lv_perc = lv_perc_tmp.
       MOVE lv_req_num TO lv_value.
       MOVE lv_perc TO lv_value2.
       CONCATENATE lv_value2 '% out of  ' lv_value ' requests' INTO lv_value2.
       CALL FUNCTION 'SAPGUI_PROGRESS_INDICATOR'
         EXPORTING
           percentage = lv_perc
           text       = lv_value2.
     ENDIF.
     REFRESH: lt_objects.
     ls_request-h = ls_request_header.
     CALL FUNCTION 'TRINT_READ_REQUEST'
       EXPORTING
         iv_read_objs_keys = 'X'
       CHANGING
         cs_request        = ls_request
       EXCEPTIONS
         OTHERS            = 0.
     CHECK sy-subrc IS INITIAL.
     APPEND LINES OF ls_request-objects TO lt_objects.
     CHECK lt_objects[] IS NOT INITIAL.
     "ignore corrections with notes
     IF c2_note IS NOT INITIAL.
       READ TABLE ls_request-objects WITH KEY object = 'NOTE'
         TRANSPORTING NO FIELDS.
       IF sy-subrc IS INITIAL.
         "this transport contains corrections -- skip it:
         CONTINUE.
       ENDIF.
     ENDIF.
     "create log element:
     lo_element = lo_xml_doc->create_element( name = 'logentry' ).
     "set request number
     lv_value = ls_request_header-trkorr.
     lo_element->set_attribute( name = 'revision' value = lv_value ).
     "add info about user
     lo_new_element = lo_xml_doc->create_element( name = 'author' ).
     lv_uname = ls_request_header-as4user.
     CLEAR ls_uaddr.
     CALL FUNCTION 'BAPI_USER_GET_DETAIL'
       EXPORTING
         username      = lv_uname
         cache_results = 'X'
       IMPORTING
         address       = ls_uaddr
       TABLES
         return        = lt_bapiret.
     IF ls_uaddr-firstname IS INITIAL AND ls_uaddr-lastname IS INITIAL.
       lv_value = ls_request_header-as4user.
     ELSE.
       CONCATENATE ls_uaddr-firstname ls_uaddr-lastname  INTO lv_value SEPARATED BY space.
     ENDIF.
     lo_text = lo_xml_doc->create_text( lv_value ).
     lo_new_element->append_child( lo_text ).
     lo_element->append_child( lo_new_element ).
     "add info about request creation date and time
     lo_new_element = lo_xml_doc->create_element( name = 'date' ).
     TRY.
         CALL METHOD cl_gdt_conversion=>date_time_outbound
           EXPORTING
             im_date  = ls_request_header-as4date
             im_time  = ls_request_header-as4time
           IMPORTING
             ex_value = lv_value.
       CATCH cx_gdt_conversion .
         CONTINUE.
     ENDTRY.
     lo_text = lo_xml_doc->create_text( lv_value ).
     lo_new_element->append_child( lo_text ).
     lo_element->append_child( lo_new_element ).
     lo_new_element = lo_xml_doc->create_element( name = 'paths' ).
     LOOP AT lt_objects INTO ls_objects.
       "check that this object shall be included:
       CHECK ls_objects-object IN s_obj_i.
       "check that this object is not in exclude list
       CHECK ls_objects-object NOT IN s_obj_e.
       lo_path_element = lo_xml_doc->create_element( name = 'path' ).
       "<path
       "   kind="file"
       "   action="A">/temp/NPQFM/FacilityManagement.launch</path>
       lo_path_element->set_attribute( name = 'kind' value = 'file' ).
       lo_path_element->set_attribute( name = 'action' value = 'M' ).
       "build path:
       lv_value = p1.
       "request type
       CALL FUNCTION 'TR_REQUEST_TYPE_TEXT'
         EXPORTING
           iv_request_type           = ls_request_header-trfunction
         IMPORTING
           ev_request_type_text_long = lv_func_desc.
       IF lv_func_desc IS NOT INITIAL.
         REPLACE ALL OCCURRENCES OF REGEX '[#!:/]{1,100}' IN lv_func_desc WITH ' '.
         CONCATENATE lv_value '/' lv_func_desc INTO lv_value.
       ENDIF.
       "add object type
       READ TABLE gt_object_texts INTO ls_object_texts
         WITH KEY pgmid = ls_objects-pgmid
                  object = ls_objects-object BINARY SEARCH.
       IF sy-subrc IS INITIAL.
         REPLACE ALL OCCURRENCES OF REGEX '[#!:/]{1,100}' IN ls_object_texts-text WITH ' '.
         CONCATENATE lv_value '/' ls_object_texts-text INTO lv_value.
       ENDIF.
       "split by object-subobject relation
       SPLIT ls_objects-obj_name AT space INTO lv_obj_name lv_subobj_name.
       "add object itself:
       REFRESH lt_name.
       REPLACE ALL OCCURRENCES OF REGEX '[ #!]{1,100}' IN lv_obj_name WITH '/'.
       SPLIT lv_obj_name AT '/' INTO TABLE lt_name.
       LOOP AT lt_name INTO lv_name WHERE table_line IS NOT INITIAL.
         CONCATENATE lv_value '/' lv_name INTO lv_value.
       ENDLOOP.
       "add object itself:
       REFRESH lt_name.
       REPLACE ALL OCCURRENCES OF REGEX '[ #!]{1,100}' IN lv_subobj_name WITH '/'.
       SPLIT lv_subobj_name AT '/' INTO TABLE lt_name.
       LOOP AT lt_name INTO lv_name WHERE table_line IS NOT INITIAL.
         CONCATENATE lv_value '/' lv_name INTO lv_value.
       ENDLOOP.
       "as well add object type as object extension
       READ TABLE gt_object_texts INTO ls_object_texts
         WITH KEY pgmid = ls_objects-pgmid
                  object = ls_objects-object BINARY SEARCH.
       IF sy-subrc IS INITIAL.
         REPLACE ALL OCCURRENCES OF REGEX '[#!:/]{1,100}' IN ls_object_texts-text WITH ' '.
         IF c3_shnam IS INITIAL.
           CONCATENATE lv_value '.SAP ' ls_object_texts-text INTO lv_value.
         ELSE.
           "shorten object type descriptions to 8 letters, omit spaces:
           lv_short_name_len = 0.
           CLEAR lv_short_name.
           SPLIT ls_object_texts-text AT space INTO TABLE lt_obj_type.
           LOOP AT lt_obj_type INTO lv_obj_type.
             IF sy-tabix = 1.
               "add perfix SAP:
               IF lv_obj_type NS  'SAP'.
                 CONCATENATE 'SAP' lv_obj_type INTO lv_obj_type.
               ENDIF.
             ENDIF.
             "Make first letter CAPITAL
             "TRANSLATE lv_obj_type(1) TO UPPER CASE.
             CONCATENATE lv_short_name lv_obj_type INTO lv_short_name.
             lv_short_name_len = lv_short_name_len + STRLEN( lv_obj_type ).
             "exit if we got enough letters:
             IF lv_short_name_len > 8.
               EXIT.
             ENDIF.
           ENDLOOP.
           CONCATENATE lv_value '.' lv_short_name INTO lv_value.
         ENDIF.
       ENDIF.
       lo_text = lo_xml_doc->create_text( lv_value ).
       lo_path_element->append_child( lo_text ).
       lo_new_element->append_child( lo_path_element ).
     ENDLOOP.
     "add <paths>
     lo_element->append_child( lo_new_element ).
     "add carriage return after <paths>    <--- required by Gource's implementation of XML log parsing
     lo_comment = lo_xml_doc->create_comment( '#CR-LF#' ).
     lo_element->append_child( lo_comment ).
     "add <msg> if exist
     IF ls_request_header-as4text IS NOT INITIAL.
       lo_new_element = lo_xml_doc->create_element( name = 'msg' ).
       lv_value = ls_request_header-as4text.
       lo_text = lo_xml_doc->create_text( lv_value ).
       lo_new_element->append_child( lo_text ).
       lo_element->append_child( lo_new_element ).
     ENDIF.
     "add carriage return befor closing log entry   <--- required by Gource's implementation of XML log parsing
     lo_comment = lo_xml_doc->create_comment( '#CR-LF#' ).
     lo_element->append_child( lo_comment ).
     "insert new log entry on proper place
     lo_root->insert_child( new_child = lo_element ref_child = lo_node_pointer ).
     "add carriage return after new logentry <--- required by Gource's implementation of XML log parsing
     lo_comment = lo_xml_doc->create_comment( '#CR-LF#' ).
     lo_root->insert_child( new_child = lo_comment ref_child = lo_node_pointer ).
   ENDLOOP.
   "render XML and show on page
   "create output stream
   lo_ostream = lo_streamfactory->create_ostream_cstring( lv_xml_string ).
   "render document
   lo_renderer = lo_ixml->create_renderer( ostream  = lo_ostream document = lo_xml_doc ).
   lo_renderer->render( ).
   "use only the first xml_size bytes of the xml table!!
   lv_xml_size = lo_ostream->get_num_written( ).
   "replace all <!--#CL-LF#--> with line breaks
   REPLACE ALL OCCURRENCES OF '<!--#CR-LF#-->' IN lv_xml_string WITH cl_abap_char_utilities=>cr_lf.
   "remove BOM
   SHIFT lv_xml_string BY 1 PLACES LEFT IN CHARACTER MODE.
   "change encoding
   TRY .
       lo_conv = cl_abap_conv_out_ce=>create( encoding = '1100' endian = 'B' ). "iso_8859_1
       lo_conv->convert( EXPORTING data = lv_xml_string IMPORTING buffer = lv_xml_xstring ).
     CATCH cx_sy_conversion_codepage.
       "move it as it is:
       CALL FUNCTION 'SCMS_STRING_TO_XSTRING'
         EXPORTING
           text   = lv_xml_string
         IMPORTING
           buffer = lv_xml_xstring
         EXCEPTIONS
           failed = 1
           OTHERS = 2.
   ENDTRY.
   CALL FUNCTION 'SCMS_XSTRING_TO_BINARY'
     EXPORTING
       buffer        = lv_xml_xstring
     IMPORTING
       output_length = lv_xml_size
     TABLES
       binary_tab    = lt_xml_table.
   "store on disk
   CALL FUNCTION 'SAPGUI_PROGRESS_INDICATOR'
     EXPORTING
       percentage = 1
       text       = 'Store on disk...'.
   lv_filename = f1.
   CALL FUNCTION 'GUI_DOWNLOAD'
     EXPORTING
       bin_filesize            = lv_xml_size
       filename                = lv_filename
       filetype                = 'BIN'
       codepage                = '4110'                      "cp-1250
       replacement             = space
       show_transfer_status    = abap_true
     TABLES
       data_tab                = lt_xml_table
     EXCEPTIONS
       file_write_error        = 1
       no_batch                = 2
       gui_refuse_filetransfer = 3
       invalid_type            = 4
       no_authority            = 5
       unknown_error           = 6
       header_not_allowed      = 7
       separator_not_allowed   = 8
       filesize_not_allowed    = 9
       header_too_long         = 10
       dp_error_create         = 11
       dp_error_send           = 12
       dp_error_write          = 13
       unknown_dp_error        = 14
       access_denied           = 15
       dp_out_of_memory        = 16
       disk_full               = 17
       dp_timeout              = 18
       file_not_found          = 19
       dataprovider_exception  = 20
       control_flush_error     = 21
       OTHERS                  = 22.
   IF sy-subrc <> 0.
     MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
             WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
   ENDIF.
   CALL FUNCTION 'SAPGUI_PROGRESS_INDICATOR'
     EXPORTING
       percentage = 100
       text       = 'Finished.'.
To report this post you need to login first.

8 Comments

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

  1. Michelle Crapo

    Nice job with the code.   I googled Gource visualization.   I still really don’t know what it does or what I could use it for.

    Can you give me an example of what it was used for?  Or was it just for fun?

    Michelle

    (0) 
    1. Artem Ruzak Post author

      Hello Michelle,
      I positively surpised how incrediable sharp-eyed are you 🙂

      This blog was ment to hold only source code (hence is posted in private space) as preparation to another blog on how to use Gource for visualization of SAP projects by exporting logs from SAP transport system.
      I hope to finish it by end of the day (bewteen all the usual work I shall do) so you will get also directions how to make your own video from it.
      As sneak preview – here what you shall be able to get at the end 😉   http://youtu.be/l21awbB3Uoo

      Best regards, Artem

      (0) 
  2. Martin Voros

    Hi Artem,

    I just wanted to write a program that generates an input for Gource. Fortunately, I googled before and I found this blog. So I have less work. I have 3 questions/comments:

    1. Why did you decide to generate complex XML file instead of a simple CSV file?

    2. Have you though about reusing Code Inspector functionality. You can define various sets of objects in CI and there is FM SCI_GET_INSPECTION_PLAIN_LIST that gives a list of objects.

    3. Splitting code into routines would increase readability of code. People should stop writing these spaghetti codes even in throw away programs.

    Thanks for your effort.

    (0) 
    1. Artem Ruzak Post author

      Hello Martin,

        glad to hear you found this code helpful.

         To your questions:
      1. I used XML in order to have some better readability of data which helps a lot during testing. Also another reason for me was that I am integrating that code with logs generated from SVN repository (as part of our project is on Java) and since SVN gives me back XML version I just need to provide SAP logs in same format
      2. Well, I have little knowledge about code inspector lists and used more familiar to me transport system as way to select objects relevant for development. Also becouse you want to see not only changes to objects that exist now but to ones that were created in past abd were also deleted already

      3. For the p.3 you are absolutely right – I should make code more nicer before posting it to community but this is a matter that is always pushed awayof  ;(

      If you like to improve the code or add an option for pulling data set from code inspector – go ahead. I am also curious to see what you have generated out of it. Please share a link later for myself and others.

      Best regards, Artem

      (0) 
      1. Martin Voros

        Hi,

        I haven’t run it yet and I am not sure when I will have some time. But I just noticed that you use transports for your selection. I would like to use a different approach. I would like to search by development objects first and then find corresponding transport for selected period. That’s why I proposed to reuse code from code inspector. It allows you to define objects and then use them for transport selection. So I might write a new program with some pieces from you if you don’t mind.

        Cheers

        (0) 
        1. Artem Ruzak Post author

          You are welcome! The stright forward way to do what you want with possibly some manual work coudl be usage of SE03 transaction for searching of transports by objects.

          Best regards, artem

          (0) 

Leave a Reply