diff --git a/src/objects/aff/zcl_abapgit_json_path.clas.abap b/src/objects/aff/zcl_abapgit_json_path.clas.abap index 47f65ca64..123ef9a55 100644 --- a/src/objects/aff/zcl_abapgit_json_path.clas.abap +++ b/src/objects/aff/zcl_abapgit_json_path.clas.abap @@ -4,6 +4,10 @@ CLASS zcl_abapgit_json_path DEFINITION PUBLIC CREATE PUBLIC. IMPORTING iv_json TYPE string RETURNING VALUE(rt_result) TYPE string_table RAISING zcx_abapgit_exception. + METHODS: deserialize + IMPORTING it_json_path TYPE string_table + RETURNING VALUE(rv_result) TYPE xstring + RAISING zcx_abapgit_exception. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. @@ -36,4 +40,10 @@ CLASS zcl_abapgit_json_path IMPLEMENTATION. CHANGING ct_json_paths = rt_result ). ENDMETHOD. + METHOD deserialize. + + rv_result = lcl_json_path=>deserialize( it_json_path ). + + ENDMETHOD. + ENDCLASS. diff --git a/src/objects/aff/zcl_abapgit_json_path.clas.locals_imp.abap b/src/objects/aff/zcl_abapgit_json_path.clas.locals_imp.abap index c19685295..968a4bb1e 100644 --- a/src/objects/aff/zcl_abapgit_json_path.clas.locals_imp.abap +++ b/src/objects/aff/zcl_abapgit_json_path.clas.locals_imp.abap @@ -11,6 +11,11 @@ CLASS lcl_json_path DEFINITION CREATE PUBLIC. it_path TYPE string_table CHANGING ct_json_paths TYPE string_table. + CLASS-METHODS: deserialize + IMPORTING it_json_path TYPE string_table + RETURNING VALUE(rv_result) TYPE xstring + RAISING zcx_abapgit_exception. + PROTECTED SECTION. PRIVATE SECTION. @@ -35,11 +40,165 @@ CLASS lcl_json_path DEFINITION CREATE PUBLIC. get_json_path IMPORTING it_path TYPE string_table RETURNING VALUE(rv_result) TYPE string. + CLASS-METHODS get_path_elements + IMPORTING iv_path TYPE string + RETURNING VALUE(rt_result) TYPE string_table + RAISING zcx_abapgit_exception. + CLASS-METHODS build_json + IMPORTING it_path_elements TYPE string_table + iv_value TYPE string + CHANGING cv_json_string TYPE string. + CLASS-METHODS path_contains_array + IMPORTING iv_path TYPE string + RETURNING VALUE(rv_result) TYPE abap_bool. + CLASS-METHODS: to_json + IMPORTING iv_json_path TYPE string + RETURNING VALUE(ro_result) TYPE REF TO zcl_abapgit_ajson + RAISING zcx_abapgit_ajson_error + zcx_abapgit_exception. + CLASS-METHODS: is_primitiv + IMPORTING iv_string TYPE string + RETURNING VALUE(rv_result) TYPE abap_bool. + CLASS-METHODS: is_comment_or_empty_line + IMPORTING iv_line TYPE string + RETURNING VALUE(rv_result) TYPE abap_bool. ENDCLASS. CLASS lcl_json_path IMPLEMENTATION. + METHOD to_json. + DATA: lv_path TYPE string, + lv_value TYPE string, + lt_path_elements TYPE string_table, + lv_json TYPE string. + + FIND REGEX `(.*)=(.*$)` IN iv_json_path SUBMATCHES lv_path lv_value. + + IF path_contains_array( lv_path ) = abap_true. + + lt_path_elements = get_path_elements( lv_path ). + + build_json( EXPORTING it_path_elements = lt_path_elements + iv_value = lv_value + CHANGING cv_json_string = lv_json ). + + ro_result = zcl_abapgit_ajson=>parse( lv_json ). + ELSE. + + REPLACE FIRST OCCURRENCE OF '$.' IN lv_path WITH ''. + REPLACE '.' IN lv_path WITH '/'. + ro_result = zcl_abapgit_ajson=>create_empty( iv_keep_item_order = abap_true ). + ro_result->set( iv_path = lv_path + iv_val = lv_value ). + ENDIF. + + + ENDMETHOD. + + METHOD path_contains_array. + DATA lv_array_pattern TYPE string VALUE `.*\[.*\].*`. + rv_result = boolc( matches( val = iv_path + regex = lv_array_pattern ) ). + ENDMETHOD. + + METHOD build_json. + DATA: lt_new_path_element TYPE string_table, + lv_sub_match TYPE string, + lv_key_name TYPE string, + lv_key_value TYPE string, + lv_name TYPE string, + lv_first_elem TYPE string. + + lt_new_path_element = it_path_elements. + + IF lines( lt_new_path_element ) = 0. + RETURN. + ENDIF. + + READ TABLE lt_new_path_element INTO lv_first_elem INDEX 1. + + IF lv_first_elem = `$`. " is root level + + DELETE lt_new_path_element INDEX 1. + build_json( EXPORTING it_path_elements = lt_new_path_element + iv_value = iv_value + CHANGING cv_json_string = cv_json_string ). + + ELSEIF is_primitiv( lv_first_elem ) = abap_true. + + cv_json_string = cv_json_string && | \{"{ lv_first_elem+1 }": |. + + DELETE lt_new_path_element INDEX 1. + + build_json( EXPORTING it_path_elements = lt_new_path_element + iv_value = iv_value + CHANGING cv_json_string = cv_json_string ). + + cv_json_string = cv_json_string && ` }`. + + ELSE. " is array + + FIND REGEX `\[(.*)\]` IN lv_first_elem SUBMATCHES lv_sub_match. + FIND REGEX `(\w+)(?==='([^']*)')` IN lv_sub_match SUBMATCHES lv_key_name lv_key_value. + READ TABLE lt_new_path_element INTO lv_name INDEX 2. + + + DELETE lt_new_path_element INDEX 1. + DELETE lt_new_path_element INDEX 1. + + IF lines( lt_new_path_element ) = 0. + + cv_json_string = cv_json_string && + |[ \{ "{ lv_key_name }": "{ lv_key_value }", "{ lv_name+1 }": "{ iv_value }"\} ]|. + + ELSE. + + cv_json_string = cv_json_string && |[ \{ "{ lv_key_name }": "{ lv_key_value }", "{ lv_name+1 }":|. + + build_json( EXPORTING it_path_elements = lt_new_path_element + iv_value = iv_value + CHANGING cv_json_string = cv_json_string ). + + cv_json_string = cv_json_string && `} ] `. + + ENDIF. + ENDIF. + + ENDMETHOD. + + METHOD is_primitiv. + + FIND REGEX `^.\w+` IN iv_string. " string start with . + rv_result = boolc( sy-subrc = 0 ). + + ENDMETHOD. + + + + METHOD get_path_elements. + DATA: lv_pcre_pattern TYPE string, + lt_match_result TYPE match_result_tab, + lv_match TYPE match_result, + lv_hit TYPE string, + lx_find TYPE REF TO cx_root. + + lv_pcre_pattern = `(^\$)|(\.\w+)|(\[[^\]]*\])`. + + TRY. + FIND ALL OCCURRENCES OF REGEX lv_pcre_pattern IN iv_path RESULTS lt_match_result. + CATCH cx_sy_find_infinite_loop cx_sy_range_out_of_bounds cx_sy_invalid_regex cx_sy_regex_too_complex INTO lx_find. + zcx_abapgit_exception=>raise_with_text( lx_find ). + ENDTRY. + LOOP AT lt_match_result INTO lv_match. + lv_hit = substring( val = iv_path + off = lv_match-offset + len = lv_match-length ). + APPEND lv_hit TO rt_result. + ENDLOOP. + + ENDMETHOD. + METHOD is_array. rv_result = boolc( io_reader->name = 'array' ). ENDMETHOD. @@ -179,4 +338,69 @@ CLASS lcl_json_path IMPLEMENTATION. ENDMETHOD. + METHOD deserialize. + + DATA: lo_merged TYPE REF TO zif_abapgit_ajson, + lv_json_path TYPE string, + lo_deserialization_result TYPE REF TO zif_abapgit_ajson, + lx_ajson TYPE REF TO zcx_abapgit_ajson_error, + lv_result_as_string TYPE string. + + TRY. + lo_merged = zcl_abapgit_ajson=>parse( `` ). + CATCH zcx_abapgit_ajson_error INTO lx_ajson. + zcx_abapgit_exception=>raise_with_text( lx_ajson ). + ENDTRY. + + LOOP AT it_json_path INTO lv_json_path. + IF is_comment_or_empty_line( lv_json_path ) = abap_true. + CONTINUE. + ENDIF. + + + TRY. + lo_deserialization_result = to_json( lv_json_path ). + CATCH zcx_abapgit_ajson_error INTO lx_ajson. + zcx_abapgit_exception=>raise_with_text( lx_ajson ). + ENDTRY. + + TRY. + lo_merged = zcl_abapgit_ajson_utilities=>new( )->merge( io_json_a = lo_merged + io_json_b = lo_deserialization_result ). + CATCH zcx_abapgit_ajson_error INTO lx_ajson. + zcx_abapgit_exception=>raise_with_text( lx_ajson ). + ENDTRY. + + ENDLOOP. + + TRY. + lv_result_as_string = lo_merged->stringify( 2 ). + rv_result = zcl_abapgit_convert=>string_to_xstring( lv_result_as_string ). + CATCH zcx_abapgit_ajson_error INTO lx_ajson. + zcx_abapgit_exception=>raise_with_text( lx_ajson ). + ENDTRY. + ENDMETHOD. + + + METHOD is_comment_or_empty_line. + + IF iv_line IS INITIAL. + rv_result = abap_true. + RETURN. + ENDIF. + + FIND REGEX `^!` IN iv_line. + IF sy-subrc = 0. + rv_result = abap_true. + RETURN. + ENDIF. + + FIND REGEX `^#` IN iv_line. + IF sy-subrc = 0. + rv_result = abap_true. + RETURN. + ENDIF. + + ENDMETHOD. + ENDCLASS. diff --git a/src/objects/aff/zcl_abapgit_json_path.clas.testclasses.abap b/src/objects/aff/zcl_abapgit_json_path.clas.testclasses.abap index 3300b75d0..7f83b4d94 100644 --- a/src/objects/aff/zcl_abapgit_json_path.clas.testclasses.abap +++ b/src/objects/aff/zcl_abapgit_json_path.clas.testclasses.abap @@ -7,9 +7,12 @@ CLASS ltcl_json_path DEFINITION FINAL FOR TESTING mt_exp TYPE string_table, ms_data TYPE zif_abapgit_aff_intf_v1=>ty_main. METHODS: + deserialize_simple FOR TESTING RAISING cx_static_check, + deserialize_nested_arrays FOR TESTING RAISING cx_static_check, flat_structure FOR TESTING RAISING cx_static_check, array FOR TESTING RAISING cx_static_check, - array_nested FOR TESTING RAISING cx_static_check. + array_nested FOR TESTING RAISING cx_static_check, + deserialize_with_comments FOR TESTING RAISING cx_static_check. METHODS: serialize IMPORTING is_data TYPE zif_abapgit_aff_intf_v1=>ty_main @@ -139,4 +142,84 @@ CLASS ltcl_json_path IMPLEMENTATION. ENDMETHOD. + METHOD deserialize_nested_arrays. + DATA: lt_file TYPE string_table, + lo_cut TYPE REF TO zcl_abapgit_json_path, + lv_xact TYPE xstring, + lv_act TYPE string, + lv_exp TYPE string, + lt_exp TYPE string_table, + lv_is_equal TYPE abap_bool. + + APPEND `$.header.description=Text` TO lt_file. + APPEND `$.descriptions.methods[?(@.name=='METH1')].description=Sonne` TO lt_file. + APPEND `$.descriptions.methods[?(@.name=='METH1')].parameters[?(@.name=='param2')].description=ABC` TO lt_file. + + CREATE OBJECT lo_cut. + lv_xact = lo_cut->deserialize( lt_file ). + + APPEND `{ "header": { "description": "Text" } ,` TO lt_exp. + APPEND `"descriptions": {` TO lt_exp. + APPEND `"methods": [ ` TO lt_exp. + APPEND ` { "name": "METH1",` TO lt_exp. + APPEND `"description": "Sonne",` TO lt_exp. + APPEND `"parameters": [ { "name": "param2", "description": "ABC" } ]` TO lt_exp. + APPEND `}]}}` TO lt_exp. + + + lv_exp = concat_lines_of( table = lt_exp + sep = cl_abap_char_utilities=>newline ). + + lv_act = zcl_abapgit_convert=>xstring_to_string_utf8( lv_xact ). + + lv_is_equal = zcl_abapgit_ajson_utilities=>new( )->is_equal( iv_json_a = lv_act + iv_json_b = lv_exp ). + + cl_abap_unit_assert=>assert_equals( act = lv_is_equal + exp = abap_true ). + + ENDMETHOD. + + METHOD deserialize_simple. + DATA: lt_file TYPE string_table, + lo_cut TYPE REF TO zcl_abapgit_json_path, + lv_xact TYPE xstring, + + lv_act TYPE string, + lv_is_equal TYPE abap_bool. + + APPEND `$.header.description=Text` TO lt_file. + + CREATE OBJECT lo_cut. + lv_xact = lo_cut->deserialize( lt_file ). + lv_act = zcl_abapgit_convert=>xstring_to_string_utf8( lv_xact ). + + lv_is_equal = zcl_abapgit_ajson_utilities=>new( )->is_equal( + iv_json_a = lv_act + iv_json_b = ` { "header": { "description": "Text" } } ` ). + + cl_abap_unit_assert=>assert_equals( act = lv_is_equal + exp = abap_true ). + + ENDMETHOD. + + METHOD deserialize_with_comments. + DATA: lt_file TYPE string_table, + lo_cut TYPE REF TO zcl_abapgit_json_path, + lv_xact TYPE xstring, + lv_act TYPE string. + + APPEND `# comment = abc` TO lt_file. + APPEND `!this is a comment [abc]` TO lt_file. + APPEND `` TO lt_file. + + CREATE OBJECT lo_cut. + lv_xact = lo_cut->deserialize( lt_file ). + lv_act = zcl_abapgit_convert=>xstring_to_string_utf8( lv_xact ). + + + cl_abap_unit_assert=>assert_initial( lv_act ). + + ENDMETHOD. + ENDCLASS. diff --git a/src/objects/core/zcl_abapgit_objects_files.clas.abap b/src/objects/core/zcl_abapgit_objects_files.clas.abap index c0dafadff..fdc8b9292 100644 --- a/src/objects/core/zcl_abapgit_objects_files.clas.abap +++ b/src/objects/core/zcl_abapgit_objects_files.clas.abap @@ -105,6 +105,8 @@ CLASS zcl_abapgit_objects_files DEFINITION VALUE(rt_i18n_files) TYPE zif_abapgit_i18n_file=>ty_table_of RAISING zcx_abapgit_exception . + METHODS get_i18n_properties_file + RETURNING VALUE(rt_result) TYPE zif_abapgit_git_definitions=>ty_files_tt. PROTECTED SECTION. @@ -497,4 +499,31 @@ CLASS zcl_abapgit_objects_files IMPLEMENTATION. ENDLOOP. ENDMETHOD. + + METHOD get_i18n_properties_file. + + DATA lv_lang TYPE laiso. + DATA lv_ext TYPE string. + FIELD-SYMBOLS LIKE LINE OF mt_files. + + LOOP AT mt_files ASSIGNING . + + " TODO: Maybe this should be in zcl_abapgit_filename_logic + FIND FIRST OCCURRENCE OF REGEX 'i18n\.([^.]{2})\.([^.]+)$' IN -filename SUBMATCHES lv_lang lv_ext. + CHECK sy-subrc = 0. + + IF sy-subrc = 0 AND lv_ext = `properties`. + + APPEND TO rt_result. + mark_accessed( + iv_path = -path + iv_file = -filename + iv_sha1 = -sha1 ). + + ENDIF. + + ENDLOOP. + + ENDMETHOD. + ENDCLASS. diff --git a/src/objects/texts/zcl_abapgit_properties_file.clas.abap b/src/objects/texts/zcl_abapgit_properties_file.clas.abap index 23af7eeeb..47bb1ab8c 100644 --- a/src/objects/texts/zcl_abapgit_properties_file.clas.abap +++ b/src/objects/texts/zcl_abapgit_properties_file.clas.abap @@ -51,8 +51,8 @@ CLASS zcl_abapgit_properties_file IMPLEMENTATION. METHOD zif_abapgit_i18n_file~render. DATA: lv_translation TYPE string, - lo_buf TYPE REF TO zcl_abapgit_string_buffer, - lv_str TYPE string. + lo_buf TYPE REF TO zcl_abapgit_string_buffer, + lv_str TYPE string. lv_translation = concat_lines_of( table = mt_translation sep = cl_abap_char_utilities=>newline ). @@ -60,7 +60,7 @@ CLASS zcl_abapgit_properties_file IMPLEMENTATION. lo_buf->add( lv_translation ). lv_str = lo_buf->join_w_newline_and_flush( ) && cl_abap_char_utilities=>newline. - rv_data = zcl_abapgit_convert=>string_to_xstring_utf8_bom( lv_str ). + rv_data = zcl_abapgit_convert=>string_to_xstring_utf8( lv_str ). ENDMETHOD. diff --git a/src/objects/zcl_abapgit_object_intf.clas.abap b/src/objects/zcl_abapgit_object_intf.clas.abap index 0ed280a66..fa8a757d2 100644 --- a/src/objects/zcl_abapgit_object_intf.clas.abap +++ b/src/objects/zcl_abapgit_object_intf.clas.abap @@ -520,14 +520,28 @@ CLASS zcl_abapgit_object_intf IMPLEMENTATION. METHOD zif_abapgit_object~deserialize. - DATA: lt_source TYPE rswsourcet, - ls_clskey TYPE seoclskey, - ls_intf TYPE ty_intf. + DATA: + lt_source TYPE rswsourcet, + ls_clskey TYPE seoclskey, + ls_intf TYPE ty_intf, + lt_description TYPE zif_abapgit_oo_object_fnc=>ty_seocompotx_tt, + lt_description_sub TYPE zif_abapgit_oo_object_fnc=>ty_seosubcotx_tt. IF iv_step = zif_abapgit_object=>gc_step_id-abap. " HERE: switch with feature flag between XML and JSON file format IF mv_aff_enabled = abap_true. ls_intf = read_json( ). + + lcl_aff_metadata_handler=>deserialize_translation( + EXPORTING + io_files = mo_files + IMPORTING + et_description = lt_description + et_description_sub = lt_description_sub ). + + APPEND LINES OF lt_description TO ls_intf-description. + APPEND LINES OF lt_description_sub TO ls_intf-description_sub. + ELSE. ls_intf = read_xml( io_xml ). ENDIF. diff --git a/src/objects/zcl_abapgit_object_intf.clas.locals_imp.abap b/src/objects/zcl_abapgit_object_intf.clas.locals_imp.abap index 5edfcdd90..19650a523 100644 --- a/src/objects/zcl_abapgit_object_intf.clas.locals_imp.abap +++ b/src/objects/zcl_abapgit_object_intf.clas.locals_imp.abap @@ -402,14 +402,14 @@ CLASS lcl_aff_type_mapping IMPLEMENTATION. ls_data_aff = iv_data. - lv_classname = iv_object_name. + lv_classname = to_upper( iv_object_name ). set_abapgit_descriptions( EXPORTING is_clsname = lv_classname is_intf_aff = ls_data_aff IMPORTING et_descriptions = ls_data_abapgit-description et_descriptions_sub = ls_data_abapgit-description_sub ). - ls_data_abapgit-vseointerf-clsname = iv_object_name. + ls_data_abapgit-vseointerf-clsname = lv_classname. ls_data_abapgit-vseointerf-descript = ls_data_aff-header-description. ls_data_abapgit-vseointerf-category = ls_data_aff-category. ls_data_abapgit-vseointerf-unicode = ls_data_aff-header-abap_language_version. @@ -511,6 +511,11 @@ CLASS lcl_aff_metadata_handler DEFINITION. IMPORTING iv_data TYPE xstring RETURNING VALUE(rv_result) TYPE zif_abapgit_aff_intf_v1=>ty_main RAISING zcx_abapgit_exception. + CLASS-METHODS deserialize_translation + IMPORTING io_files TYPE REF TO zcl_abapgit_objects_files + EXPORTING et_description TYPE zcl_abapgit_object_intf=>ty_intf-description + et_description_sub TYPE zcl_abapgit_object_intf=>ty_intf-description_sub + RAISING zcx_abapgit_exception. PRIVATE SECTION. CLASS-METHODS: "! For serialization @@ -524,7 +529,11 @@ CLASS lcl_aff_metadata_handler DEFINITION. fill_translation IMPORTING iv_name TYPE seoclsname iv_language TYPE laiso - RETURNING VALUE(rt_result) TYPE zif_abapgit_aff_intf_v1=>ty_main. + RETURNING VALUE(rt_result) TYPE zif_abapgit_aff_intf_v1=>ty_main, + get_string_table + IMPORTING iv_xstring TYPE xstring + RETURNING VALUE(rt_result) TYPE string_table + RAISING zcx_abapgit_exception. ENDCLASS. CLASS lcl_aff_metadata_handler IMPLEMENTATION. @@ -691,4 +700,71 @@ CLASS lcl_aff_metadata_handler IMPLEMENTATION. ENDMETHOD. + + METHOD deserialize_translation. + DATA: lt_data TYPE string_table, + lv_xtranslation TYPE xstring, + lo_ajson TYPE REF TO zcl_abapgit_json_handler, + lx_exception TYPE REF TO cx_static_check, + lv_obj_name TYPE seoclsname, + lv_langu TYPE laiso, + lt_translation_file TYPE zif_abapgit_git_definitions=>ty_files_tt, + ls_translation_file TYPE zif_abapgit_git_definitions=>ty_file, + lo_json_path TYPE REF TO zcl_abapgit_json_path, + ls_aff_data TYPE zif_abapgit_aff_intf_v1=>ty_main, + lo_type_mapper TYPE REF TO zif_abapgit_aff_type_mapping, + ls_ag_data TYPE zcl_abapgit_object_intf=>ty_intf. + + lt_translation_file = io_files->get_i18n_properties_file( ). + CREATE OBJECT lo_json_path. + + LOOP AT lt_translation_file INTO ls_translation_file. + + CLEAR ls_ag_data. + + lt_data = get_string_table( ls_translation_file-data ). + lv_xtranslation = lo_json_path->deserialize( lt_data ). + + CREATE OBJECT lo_ajson. + TRY. + lo_ajson->deserialize( + EXPORTING + iv_content = lv_xtranslation + IMPORTING + ev_data = ls_aff_data ). + CATCH cx_static_check INTO lx_exception. + zcx_abapgit_exception=>raise_with_text( lx_exception ). + ENDTRY. + + FIND FIRST OCCURRENCE OF REGEX '^([\w]+).*\.i18n.([a-z]{2})\.' + IN ls_translation_file-filename SUBMATCHES lv_obj_name lv_langu. + + ls_aff_data-header-original_language = to_upper( lv_langu ). " is target language + + CREATE OBJECT lo_type_mapper TYPE lcl_aff_type_mapping. + lo_type_mapper->to_abapgit( + EXPORTING + iv_data = ls_aff_data + iv_object_name = to_upper( lv_obj_name ) + IMPORTING + es_data = ls_ag_data ). + + APPEND LINES OF ls_ag_data-description TO et_description. + APPEND LINES OF ls_ag_data-description_sub TO et_description_sub. + + ENDLOOP. + + + ENDMETHOD. + + + METHOD get_string_table. + + DATA lv_data TYPE string. + + lv_data = zcl_abapgit_convert=>xstring_to_string_utf8( iv_xstring ). + SPLIT lv_data AT cl_abap_char_utilities=>newline INTO TABLE rt_result. + + ENDMETHOD. + ENDCLASS.