*&---------------------------------------------------------------------* *& Include ZABAPGIT_UTIL *&---------------------------------------------------------------------* *----------------------------------------------------------------------* * CLASS lcl_language DEFINITION *----------------------------------------------------------------------* * This helper class is used to set and restore the current language. * As some of the SAP functions used rely on SY-LANGU containing the * master language, this class is used to temporarily change and then * restore the value of SY-LANGU. *----------------------------------------------------------------------* CLASS lcl_language DEFINITION. PUBLIC SECTION. CLASS-DATA: current_language TYPE langu READ-ONLY, login_language TYPE langu READ-ONLY. CLASS-METHODS: class_constructor, restore_login_language, set_current_language IMPORTING iv_language TYPE langu. ENDCLASS. *----------------------------------------------------------------------* * CLASS lcl_language IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_language IMPLEMENTATION. METHOD class_constructor. DATA lv_dummy TYPE string. GET LOCALE LANGUAGE login_language COUNTRY lv_dummy MODIFIER lv_dummy. ENDMETHOD. METHOD restore_login_language. SET LOCALE LANGUAGE login_language. ENDMETHOD. METHOD set_current_language. SET LOCALE LANGUAGE iv_language. ENDMETHOD. ENDCLASS. CLASS lcl_state DEFINITION. PUBLIC SECTION. CLASS-METHODS: reduce IMPORTING iv_cur TYPE char1 CHANGING cv_prev TYPE char1. ENDCLASS. CLASS lcl_state IMPLEMENTATION. METHOD reduce. IF cv_prev = iv_cur OR iv_cur IS INITIAL. RETURN. " No change ELSEIF cv_prev IS INITIAL. cv_prev = iv_cur. ELSE. cv_prev = zif_abapgit_definitions=>gc_state-mixed. ENDIF. ENDMETHOD. ENDCLASS. *----------------------------------------------------------------------* * CLASS lcl_convert DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_convert DEFINITION FINAL. PUBLIC SECTION. CLASS-METHODS bitbyte_to_int IMPORTING iv_bits TYPE clike RETURNING VALUE(rv_int) TYPE i. CLASS-METHODS x_to_bitbyte IMPORTING iv_x TYPE x RETURNING VALUE(rv_bitbyte) TYPE zif_abapgit_definitions=>ty_bitbyte. CLASS-METHODS string_to_xstring_utf8 IMPORTING iv_string TYPE string RETURNING VALUE(rv_xstring) TYPE xstring. CLASS-METHODS xstring_to_string_utf8 IMPORTING iv_data TYPE xstring RETURNING VALUE(rv_string) TYPE string. CLASS-METHODS xstring_to_int IMPORTING iv_xstring TYPE xstring RETURNING VALUE(rv_i) TYPE i RAISING zcx_abapgit_exception. CLASS-METHODS int_to_xstring4 IMPORTING iv_i TYPE i RETURNING VALUE(rv_xstring) TYPE xstring. CLASS-METHODS split_string IMPORTING iv_string TYPE string RETURNING VALUE(rt_lines) TYPE string_table. ENDCLASS. "lcl_convert DEFINITION *----------------------------------------------------------------------* * CLASS lcl_convert IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_convert IMPLEMENTATION. METHOD int_to_xstring4. * returns xstring of length 4 containing the integer value iv_i DATA: lv_x TYPE x LENGTH 4. lv_x = iv_i. rv_xstring = lv_x. ENDMETHOD. "int_to_xstring METHOD xstring_to_int. DATA: lv_xstring TYPE xstring, lv_x TYPE x. lv_xstring = iv_xstring. WHILE xstrlen( lv_xstring ) > 0. lv_x = lv_xstring(1). rv_i = rv_i * 256 + lv_x. lv_xstring = lv_xstring+1. ENDWHILE. ENDMETHOD. "xstring_to_int METHOD xstring_to_string_utf8. DATA: lv_len TYPE i, lo_obj TYPE REF TO cl_abap_conv_in_ce. TRY. lo_obj = cl_abap_conv_in_ce=>create( input = iv_data encoding = 'UTF-8' ). lv_len = xstrlen( iv_data ). lo_obj->read( EXPORTING n = lv_len IMPORTING data = rv_string ). CATCH cx_parameter_invalid_range cx_sy_codepage_converter_init cx_sy_conversion_codepage cx_parameter_invalid_type. "#EC NO_HANDLER ENDTRY. ENDMETHOD. "xstring_to_string_utf8 METHOD string_to_xstring_utf8. DATA: lo_obj TYPE REF TO cl_abap_conv_out_ce. TRY. lo_obj = cl_abap_conv_out_ce=>create( encoding = 'UTF-8' ). lo_obj->convert( EXPORTING data = iv_string IMPORTING buffer = rv_xstring ). CATCH cx_parameter_invalid_range cx_sy_codepage_converter_init cx_sy_conversion_codepage cx_parameter_invalid_type. "#EC NO_HANDLER ENDTRY. ENDMETHOD. "string_to_xstring_utf8 METHOD bitbyte_to_int. DATA: lv_bits TYPE string. lv_bits = iv_bits. rv_int = 0. WHILE strlen( lv_bits ) > 0. rv_int = rv_int * 2. IF lv_bits(1) = '1'. rv_int = rv_int + 1. ENDIF. lv_bits = lv_bits+1. ENDWHILE. ENDMETHOD. "bitbyte_to_int METHOD x_to_bitbyte. DATA: lv_b TYPE n. CLEAR rv_bitbyte. DO 8 TIMES. GET BIT sy-index OF iv_x INTO lv_b. CONCATENATE rv_bitbyte lv_b INTO rv_bitbyte. ENDDO. ENDMETHOD. "x_to_bitbyte METHOD split_string. FIND FIRST OCCURRENCE OF cl_abap_char_utilities=>cr_lf IN iv_string. " Convert string into table depending on separator type CR_LF vs. LF IF sy-subrc = 0. SPLIT iv_string AT cl_abap_char_utilities=>cr_lf INTO TABLE rt_lines. ELSE. SPLIT iv_string AT cl_abap_char_utilities=>newline INTO TABLE rt_lines. ENDIF. ENDMETHOD. "split_string ENDCLASS. "lcl_convert IMPLEMENTATION *----------------------------------------------------------------------* * CLASS lcl_hash DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_hash DEFINITION FINAL. PUBLIC SECTION. TYPES: ty_adler32 TYPE x LENGTH 4. CLASS-METHODS adler32 IMPORTING iv_xstring TYPE xstring RETURNING VALUE(rv_checksum) TYPE ty_adler32. CLASS-METHODS sha1 IMPORTING iv_type TYPE zif_abapgit_definitions=>ty_type iv_data TYPE xstring RETURNING VALUE(rv_sha1) TYPE zif_abapgit_definitions=>ty_sha1 RAISING zcx_abapgit_exception. CLASS-METHODS sha1_raw IMPORTING iv_data TYPE xstring RETURNING VALUE(rv_sha1) TYPE zif_abapgit_definitions=>ty_sha1 RAISING zcx_abapgit_exception. ENDCLASS. "lcl_hash DEFINITION *----------------------------------------------------------------------* * CLASS lcl_hash IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_hash IMPLEMENTATION. METHOD adler32. CONSTANTS: lc_adler TYPE i VALUE 65521, lc_max_b TYPE i VALUE 1800000000. DATA: lv_index TYPE i, lv_a TYPE i VALUE 1, lv_b TYPE i VALUE 0, lv_x TYPE x LENGTH 2, lv_ca TYPE c LENGTH 4, lv_cb TYPE c LENGTH 4, lv_char8 TYPE c LENGTH 8. DO xstrlen( iv_xstring ) TIMES. lv_index = sy-index - 1. lv_a = lv_a + iv_xstring+lv_index(1). lv_b = lv_b + lv_a. * delay the MOD operation until the integer might overflow * articles describe 5552 additions are allowed, but this assumes unsigned integers * instead of allowing a fixed number of additions before running MOD, then * just compare value of lv_b, this is 1 operation less than comparing and adding IF lv_b > lc_max_b. lv_a = lv_a MOD lc_adler. lv_b = lv_b MOD lc_adler. ENDIF. ENDDO. lv_a = lv_a MOD lc_adler. lv_b = lv_b MOD lc_adler. lv_x = lv_a. lv_ca = lv_x. lv_x = lv_b. lv_cb = lv_x. CONCATENATE lv_cb lv_ca INTO lv_char8. rv_checksum = lv_char8. ENDMETHOD. "adler32 METHOD sha1_raw. DATA: lv_hash TYPE hash160. CALL FUNCTION 'CALCULATE_HASH_FOR_RAW' EXPORTING data = iv_data IMPORTING hash = lv_hash EXCEPTIONS unknown_alg = 1 param_error = 2 internal_error = 3 OTHERS = 4. IF sy-subrc <> 0. zcx_abapgit_exception=>raise( 'Error while calculating SHA1' ). ENDIF. rv_sha1 = lv_hash. TRANSLATE rv_sha1 TO LOWER CASE. ENDMETHOD. "sha1_raw METHOD sha1. DATA: lv_len TYPE i, lv_char10 TYPE c LENGTH 10, lv_string TYPE string, lv_xstring TYPE xstring. lv_len = xstrlen( iv_data ). lv_char10 = lv_len. CONDENSE lv_char10. CONCATENATE iv_type lv_char10 INTO lv_string SEPARATED BY space. lv_xstring = lcl_convert=>string_to_xstring_utf8( lv_string ). lv_string = lv_xstring. CONCATENATE lv_string '00' INTO lv_string. lv_xstring = lv_string. CONCATENATE lv_xstring iv_data INTO lv_xstring IN BYTE MODE. rv_sha1 = sha1_raw( lv_xstring ). ENDMETHOD. "sha1 ENDCLASS. "lcl_hash IMPLEMENTATION CLASS lcl_path DEFINITION FINAL. PUBLIC SECTION. CLASS-METHODS split_file_location IMPORTING iv_fullpath TYPE string EXPORTING ev_path TYPE string ev_filename TYPE string. CLASS-METHODS is_root IMPORTING iv_path TYPE string RETURNING VALUE(rv_yes) TYPE abap_bool. CLASS-METHODS is_subdir IMPORTING iv_path TYPE string iv_parent TYPE string RETURNING VALUE(rv_yes) TYPE abap_bool. CLASS-METHODS change_dir IMPORTING iv_cur_dir TYPE string iv_cd TYPE string RETURNING VALUE(rv_path) TYPE string. CLASS-METHODS get_filename_from_syspath IMPORTING iv_path TYPE string RETURNING VALUE(rv_filename) TYPE string. ENDCLASS. "lcl_path CLASS lcl_path IMPLEMENTATION. METHOD split_file_location. DATA: lv_cnt TYPE i, lv_len TYPE i. FIND FIRST OCCURRENCE OF REGEX '^/(.*/)?' IN iv_fullpath MATCH COUNT lv_cnt MATCH LENGTH lv_len. IF lv_cnt > 0. ev_path = iv_fullpath+0(lv_len). ev_filename = iv_fullpath+lv_len. ELSE. CLEAR ev_path. ev_filename = iv_fullpath. ENDIF. ENDMETHOD. "split_file_location METHOD is_root. rv_yes = boolc( iv_path = '/' ). ENDMETHOD. "is_root METHOD is_subdir. DATA lv_len TYPE i. DATA lv_last TYPE i. lv_len = strlen( iv_parent ). lv_last = lv_len - 1. rv_yes = boolc( strlen( iv_path ) > lv_len AND iv_path+0(lv_len) = iv_parent AND ( iv_parent+lv_last(1) = '/' OR iv_path+lv_len(1) = '/' ) ). ENDMETHOD. "is_subdir METHOD change_dir. DATA: lv_last TYPE i, lv_temp TYPE string. lv_last = strlen( iv_cur_dir ) - 1. IF iv_cd = '' OR iv_cd = '.'. " No change rv_path = iv_cur_dir. ELSEIF iv_cd+0(1) = '/'. " Absolute path rv_path = iv_cd. ELSEIF iv_cd = '..'. " CD back IF iv_cur_dir = '/' OR iv_cur_dir = ''. " Back from root = root rv_path = iv_cur_dir. ELSE. lv_temp = reverse( iv_cur_dir ). IF lv_temp+0(1) = '/'. SHIFT lv_temp BY 1 PLACES LEFT. ENDIF. SHIFT lv_temp UP TO '/' LEFT. rv_path = reverse( lv_temp ). ENDIF. ELSEIF iv_cur_dir+lv_last(1) = '/'. " Append cd to cur_dir separated by / rv_path = iv_cur_dir && iv_cd. ELSE. rv_path = iv_cur_dir && '/' && iv_cd. ENDIF. " TODO: improve logic and cases ENDMETHOD. "change_dir METHOD get_filename_from_syspath. DATA: lv_split TYPE c LENGTH 1, lv_index TYPE i, lt_split TYPE TABLE OF string. " filename | c:\filename | /dir/filename | \\server\filename IF iv_path CA '/'. lv_split = '/'. ELSE. lv_split = '\'. ENDIF. SPLIT iv_path AT lv_split INTO TABLE lt_split. lv_index = lines( lt_split ). READ TABLE lt_split INDEX lv_index INTO rv_filename. ENDMETHOD. " get_filename_from_syspath. ENDCLASS. "lcl_path *----------------------------------------------------------------------* * CLASS lcl_url DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_url DEFINITION FINAL. PUBLIC SECTION. CLASS-METHODS host IMPORTING iv_repo TYPE string RETURNING VALUE(rv_host) TYPE string RAISING zcx_abapgit_exception. CLASS-METHODS name IMPORTING iv_repo TYPE string RETURNING VALUE(rv_name) TYPE string RAISING zcx_abapgit_exception. CLASS-METHODS path_name IMPORTING iv_repo TYPE string RETURNING VALUE(rv_path_name) TYPE string RAISING zcx_abapgit_exception. PRIVATE SECTION. CLASS-METHODS regex IMPORTING iv_repo TYPE string EXPORTING ev_host TYPE string ev_path TYPE string ev_name TYPE string RAISING zcx_abapgit_exception. ENDCLASS. "lcl_repo DEFINITION *----------------------------------------------------------------------* * CLASS lcl_url IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_url IMPLEMENTATION. METHOD host. regex( EXPORTING iv_repo = iv_repo IMPORTING ev_host = rv_host ). ENDMETHOD. "host METHOD name. regex( EXPORTING iv_repo = iv_repo IMPORTING ev_name = rv_name ). ENDMETHOD. "short_name METHOD path_name. DATA: lv_host TYPE string ##NEEDED. FIND REGEX '(.*://[^/]*)(.*)' IN iv_repo SUBMATCHES lv_host rv_path_name. ENDMETHOD. "path_name METHOD regex. FIND REGEX '(.*://[^/]*)(.*/)([^\.]*)[\.git]?' IN iv_repo SUBMATCHES ev_host ev_path ev_name. IF sy-subrc <> 0. zcx_abapgit_exception=>raise( 'Malformed URL' ). ENDIF. ENDMETHOD. "url ENDCLASS. "lcl_repo IMPLEMENTATION *----------------------------------------------------------------------* * CLASS lcl_diff DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_diff DEFINITION FINAL. PUBLIC SECTION. CONSTANTS: BEGIN OF c_diff, insert TYPE c LENGTH 1 VALUE 'I', delete TYPE c LENGTH 1 VALUE 'D', update TYPE c LENGTH 1 VALUE 'U', END OF c_diff. TYPES: BEGIN OF ty_diff, new_num TYPE c LENGTH 6, new TYPE string, result TYPE c LENGTH 1, old_num TYPE c LENGTH 6, old TYPE string, short TYPE abap_bool, beacon TYPE i, END OF ty_diff. TYPES: ty_diffs_tt TYPE STANDARD TABLE OF ty_diff WITH DEFAULT KEY. TYPES: BEGIN OF ty_count, insert TYPE i, delete TYPE i, update TYPE i, END OF ty_count. DATA mt_beacons TYPE zif_abapgit_definitions=>ty_string_tt READ-ONLY. * assumes data is UTF8 based with newlines * only works with lines up to 255 characters METHODS constructor IMPORTING iv_new TYPE xstring iv_old TYPE xstring. METHODS get RETURNING VALUE(rt_diff) TYPE ty_diffs_tt. METHODS stats RETURNING VALUE(rs_count) TYPE ty_count. PRIVATE SECTION. DATA mt_diff TYPE ty_diffs_tt. DATA ms_stats TYPE ty_count. CLASS-METHODS: unpack IMPORTING iv_new TYPE xstring iv_old TYPE xstring EXPORTING et_new TYPE abaptxt255_tab et_old TYPE abaptxt255_tab, render IMPORTING it_new TYPE abaptxt255_tab it_old TYPE abaptxt255_tab it_delta TYPE vxabapt255_tab RETURNING VALUE(rt_diff) TYPE ty_diffs_tt, compute IMPORTING it_new TYPE abaptxt255_tab it_old TYPE abaptxt255_tab RETURNING VALUE(rt_delta) TYPE vxabapt255_tab. METHODS: calculate_line_num_and_stats, map_beacons, shortlist. ENDCLASS. "lcl_diff DEFINITION *----------------------------------------------------------------------* * CLASS lcl_diff IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_diff IMPLEMENTATION. METHOD get. rt_diff = mt_diff. ENDMETHOD. "get METHOD stats. rs_count = ms_stats. ENDMETHOD. "count METHOD unpack. DATA: lv_new TYPE string, lv_old TYPE string. lv_new = lcl_convert=>xstring_to_string_utf8( iv_new ). lv_old = lcl_convert=>xstring_to_string_utf8( iv_old ). SPLIT lv_new AT zif_abapgit_definitions=>gc_newline INTO TABLE et_new. SPLIT lv_old AT zif_abapgit_definitions=>gc_newline INTO TABLE et_old. ENDMETHOD. "unpack METHOD compute. DATA: lt_trdirtab_old TYPE TABLE OF trdir, lt_trdirtab_new TYPE TABLE OF trdir, lt_trdir_delta TYPE TABLE OF xtrdir. CALL FUNCTION 'SVRS_COMPUTE_DELTA_REPS' TABLES texttab_old = it_old texttab_new = it_new trdirtab_old = lt_trdirtab_old trdirtab_new = lt_trdirtab_new trdir_delta = lt_trdir_delta text_delta = rt_delta. ENDMETHOD. "compute METHOD shortlist. DATA: lv_index TYPE i. FIELD-SYMBOLS: LIKE LINE OF mt_diff. IF lines( mt_diff ) < 20. LOOP AT mt_diff ASSIGNING . -short = abap_true. ENDLOOP. ELSE. LOOP AT mt_diff TRANSPORTING NO FIELDS WHERE NOT result IS INITIAL AND short = abap_false. lv_index = sy-tabix. DO 8 TIMES. " Backward READ TABLE mt_diff INDEX ( lv_index - sy-index ) ASSIGNING . IF sy-subrc <> 0 OR -short = abap_true. " tab bound or prev marker EXIT. ENDIF. -short = abap_true. ENDDO. DO 8 TIMES. " Forward READ TABLE mt_diff INDEX ( lv_index + sy-index - 1 ) ASSIGNING . IF sy-subrc <> 0. " tab bound reached EXIT. ENDIF. CHECK -short = abap_false. " skip marked -short = abap_true. ENDDO. ENDLOOP. ENDIF. ENDMETHOD. " shortlist METHOD calculate_line_num_and_stats. DATA: lv_new TYPE i VALUE 1, lv_old TYPE i VALUE 1. FIELD-SYMBOLS: LIKE LINE OF mt_diff. LOOP AT mt_diff ASSIGNING . -new_num = lv_new. -old_num = lv_old. CASE -result. " Line nums WHEN c_diff-delete. lv_old = lv_old + 1. CLEAR -new_num. WHEN c_diff-insert. lv_new = lv_new + 1. CLEAR -old_num. WHEN OTHERS. lv_new = lv_new + 1. lv_old = lv_old + 1. ENDCASE. CASE -result. " Stats WHEN c_diff-insert. ms_stats-insert = ms_stats-insert + 1. WHEN c_diff-delete. ms_stats-delete = ms_stats-delete + 1. WHEN c_diff-update. ms_stats-update = ms_stats-update + 1. ENDCASE. ENDLOOP. ENDMETHOD. " calculate_line_num_and_stats METHOD map_beacons. DEFINE _add_regex. CREATE OBJECT lo_regex EXPORTING pattern = &1 ignore_case = abap_true ##NO_TEXT. APPEND lo_regex TO lt_regex_set. END-OF-DEFINITION. DATA: lv_beacon_idx TYPE i, lv_offs TYPE i, lv_beacon_str TYPE string, lv_beacon_2lev TYPE string, lv_submatch TYPE string, lo_regex TYPE REF TO cl_abap_regex, lt_regex_set TYPE TABLE OF REF TO cl_abap_regex. FIELD-SYMBOLS: LIKE LINE OF mt_diff. _add_regex '^\s*(CLASS|FORM|MODULE|REPORT|METHOD)\s'. _add_regex '^\s*START-OF-'. _add_regex '^\s*INITIALIZATION(\s|\.)'. LOOP AT mt_diff ASSIGNING . CLEAR lv_offs. -beacon = lv_beacon_idx. LOOP AT lt_regex_set INTO lo_regex. " FIND FIRST OCCURRENCE OF REGEX lo_regex IN -new SUBMATCHES lv_submatch. IF sy-subrc = 0. " Match lv_beacon_str = -new. lv_submatch = to_upper( lv_submatch ). " Get rid of comments and end of line FIND FIRST OCCURRENCE OF '.' IN lv_beacon_str MATCH OFFSET lv_offs. IF sy-subrc <> 0. FIND FIRST OCCURRENCE OF '"' IN lv_beacon_str MATCH OFFSET lv_offs. ENDIF. IF lv_offs > 0. lv_beacon_str = lv_beacon_str(lv_offs). ENDIF. IF lv_submatch = 'CLASS'. lv_beacon_2lev = lv_beacon_str. ELSEIF lv_submatch = 'METHOD'. lv_beacon_str = lv_beacon_2lev && ` => ` && lv_beacon_str. ENDIF. APPEND lv_beacon_str TO mt_beacons. lv_beacon_idx = sy-tabix. -beacon = lv_beacon_idx. EXIT. "Loop ENDIF. ENDLOOP. ENDLOOP. ENDMETHOD. " map_beacons METHOD constructor. DATA: lt_delta TYPE vxabapt255_tab, lt_new TYPE abaptxt255_tab, lt_old TYPE abaptxt255_tab. unpack( EXPORTING iv_new = iv_new iv_old = iv_old IMPORTING et_new = lt_new et_old = lt_old ). lt_delta = compute( it_new = lt_new it_old = lt_old ). mt_diff = render( it_new = lt_new it_old = lt_old it_delta = lt_delta ). calculate_line_num_and_stats( ). map_beacons( ). shortlist( ). ENDMETHOD. "diff METHOD render. DEFINE _append. CLEAR ls_diff. ls_diff-new = &1. ls_diff-result = &2. ls_diff-old = &3. APPEND ls_diff TO rt_diff. END-OF-DEFINITION. DATA: lv_oindex TYPE i VALUE 1, lv_nindex TYPE i VALUE 1, ls_new LIKE LINE OF it_new, ls_old LIKE LINE OF it_old, ls_diff LIKE LINE OF rt_diff, lt_delta LIKE it_delta, ls_delta LIKE LINE OF it_delta. lt_delta = it_delta. DO. READ TABLE lt_delta INTO ls_delta WITH KEY number = lv_oindex. IF sy-subrc = 0. DELETE lt_delta INDEX sy-tabix. CASE ls_delta-vrsflag. WHEN c_diff-delete. _append '' c_diff-delete ls_delta-line. lv_oindex = lv_oindex + 1. WHEN c_diff-insert. _append ls_delta-line c_diff-insert ''. lv_nindex = lv_nindex + 1. WHEN c_diff-update. CLEAR ls_new. READ TABLE it_new INTO ls_new INDEX lv_nindex. ASSERT sy-subrc = 0. _append ls_new c_diff-update ls_delta-line. lv_nindex = lv_nindex + 1. lv_oindex = lv_oindex + 1. WHEN OTHERS. ASSERT 0 = 1. ENDCASE. ELSE. CLEAR ls_new. READ TABLE it_new INTO ls_new INDEX lv_nindex. "#EC CI_SUBRC lv_nindex = lv_nindex + 1. CLEAR ls_old. READ TABLE it_old INTO ls_old INDEX lv_oindex. "#EC CI_SUBRC lv_oindex = lv_oindex + 1. _append ls_new '' ls_old. ENDIF. IF lv_nindex > lines( it_new ) AND lv_oindex > lines( it_old ). EXIT. " current loop ENDIF. ENDDO. ENDMETHOD. " render ENDCLASS. "lcl_diff IMPLEMENTATION CLASS lcl_login_manager DEFINITION FINAL. PUBLIC SECTION. CLASS-METHODS: load IMPORTING iv_uri TYPE string ii_client TYPE REF TO if_http_client OPTIONAL RETURNING VALUE(rv_authorization) TYPE string RAISING zcx_abapgit_exception, save IMPORTING iv_uri TYPE string ii_client TYPE REF TO if_http_client RAISING zcx_abapgit_exception, clear, set IMPORTING iv_uri TYPE string iv_username TYPE string iv_password TYPE string RETURNING VALUE(rv_auth) TYPE string RAISING zcx_abapgit_exception. PRIVATE SECTION. TYPES: BEGIN OF ty_auth, uri TYPE string, authorization TYPE string, END OF ty_auth. CLASS-DATA: gt_auth TYPE TABLE OF ty_auth WITH DEFAULT KEY. CLASS-METHODS: append IMPORTING iv_uri TYPE string iv_auth TYPE string RAISING zcx_abapgit_exception. ENDCLASS. CLASS lcl_login_manager IMPLEMENTATION. METHOD clear. CLEAR gt_auth. ENDMETHOD. METHOD set. DATA: lv_concat TYPE string. ASSERT NOT iv_uri IS INITIAL. IF iv_username IS INITIAL OR iv_password IS INITIAL. RETURN. ENDIF. CONCATENATE iv_username ':' iv_password INTO lv_concat. rv_auth = cl_http_utility=>if_http_utility~encode_base64( lv_concat ). CONCATENATE 'Basic' rv_auth INTO rv_auth SEPARATED BY space ##NO_TEXT. append( iv_uri = iv_uri iv_auth = rv_auth ). ENDMETHOD. METHOD load. DATA: ls_auth LIKE LINE OF gt_auth. READ TABLE gt_auth INTO ls_auth WITH KEY uri = lcl_url=>host( iv_uri ). IF sy-subrc = 0. rv_authorization = ls_auth-authorization. IF NOT ii_client IS INITIAL. ii_client->request->set_header_field( name = 'authorization' value = ls_auth-authorization ). "#EC NOTEXT ii_client->propertytype_logon_popup = ii_client->co_disabled. ENDIF. ENDIF. ENDMETHOD. METHOD save. DATA: lv_auth TYPE string. lv_auth = ii_client->request->get_header_field( 'authorization' ). "#EC NOTEXT IF NOT lv_auth IS INITIAL. append( iv_uri = iv_uri iv_auth = lv_auth ). ENDIF. ENDMETHOD. METHOD append. FIELD-SYMBOLS: LIKE LINE OF gt_auth. READ TABLE gt_auth WITH KEY uri = lcl_url=>host( iv_uri ) TRANSPORTING NO FIELDS. IF sy-subrc <> 0. APPEND INITIAL LINE TO gt_auth ASSIGNING . -uri = lcl_url=>host( iv_uri ). -authorization = iv_auth. ENDIF. ENDMETHOD. ENDCLASS. CLASS lcl_progress DEFINITION FINAL. PUBLIC SECTION. CLASS-METHODS: show IMPORTING iv_key TYPE string VALUE(iv_current) TYPE i iv_total TYPE i iv_text TYPE csequence. PRIVATE SECTION. CLASS-METHODS: calc_pct IMPORTING iv_current TYPE i iv_total TYPE i RETURNING VALUE(rv_pct) TYPE i. ENDCLASS. CLASS lcl_progress IMPLEMENTATION. METHOD show. DATA: lv_pct TYPE i, lv_text TYPE string. lv_pct = calc_pct( iv_current = iv_current iv_total = iv_total ). CONCATENATE iv_key '-' iv_text INTO lv_text SEPARATED BY space. CALL FUNCTION 'SAPGUI_PROGRESS_INDICATOR' EXPORTING percentage = lv_pct text = lv_text. ENDMETHOD. METHOD calc_pct. DATA: lv_f TYPE f. lv_f = ( iv_current / iv_total ) * 100. rv_pct = lv_f. IF rv_pct = 100. rv_pct = 99. ENDIF. ENDMETHOD. ENDCLASS. CLASS lcl_log DEFINITION FINAL. PUBLIC SECTION. METHODS: add IMPORTING iv_msg TYPE csequence iv_type TYPE symsgty DEFAULT 'E' iv_rc TYPE balsort OPTIONAL, count RETURNING VALUE(rv_count) TYPE i, to_html RETURNING VALUE(ro_html) TYPE REF TO lcl_html, clear, has_rc "For unit tests mainly IMPORTING iv_rc TYPE balsort RETURNING VALUE(rv_yes) TYPE abap_bool, show. PRIVATE SECTION. TYPES: BEGIN OF ty_log, msg TYPE string, type TYPE symsgty, rc TYPE balsort, END OF ty_log. DATA: mt_log TYPE STANDARD TABLE OF ty_log WITH DEFAULT KEY. ENDCLASS. CLASS lcl_log IMPLEMENTATION. METHOD to_html. DATA: lv_class TYPE string, lv_icon TYPE string. FIELD-SYMBOLS: LIKE LINE OF mt_log. CREATE OBJECT ro_html. IF count( ) = 0. RETURN. ENDIF. LOOP AT mt_log ASSIGNING . CASE -type. WHEN 'W'. lv_icon = 'alert'. lv_class = 'warning'. WHEN 'E'. lv_icon = 'flame'. lv_class = 'error'. WHEN OTHERS. " ??? unexpected lv_icon = 'flame'. lv_class = 'error'. ENDCASE. ro_html->add( || ). ro_html->add_icon( iv_name = lv_icon ). ro_html->add( -msg ). ro_html->add( '' ). ENDLOOP. ENDMETHOD. METHOD add. FIELD-SYMBOLS: LIKE LINE OF mt_log. APPEND INITIAL LINE TO mt_log ASSIGNING . -msg = iv_msg. -type = iv_type. -rc = iv_rc. ENDMETHOD. METHOD show. * only supports showing 4 errors, but I guess this is okay * alternatively refactor to use method TO_HTML instead DATA: ls_log1 LIKE LINE OF mt_log, ls_log2 LIKE LINE OF mt_log, ls_log3 LIKE LINE OF mt_log, ls_log4 LIKE LINE OF mt_log. READ TABLE mt_log INDEX 1 INTO ls_log1. READ TABLE mt_log INDEX 2 INTO ls_log2. READ TABLE mt_log INDEX 3 INTO ls_log3. READ TABLE mt_log INDEX 4 INTO ls_log4. CALL FUNCTION 'POPUP_TO_INFORM' EXPORTING titel = 'Log' txt1 = ls_log1-msg txt2 = ls_log2-msg txt3 = ls_log3-msg txt4 = ls_log4-msg. ENDMETHOD. METHOD count. rv_count = lines( mt_log ). ENDMETHOD. METHOD clear. CLEAR mt_log. ENDMETHOD. " clear. METHOD has_rc. READ TABLE mt_log WITH KEY rc = iv_rc TRANSPORTING NO FIELDS. rv_yes = boolc( sy-subrc = 0 ). ENDMETHOD. "has_rc ENDCLASS.