AFF: deserialize translation files (#6830)

Co-authored-by: abaplint[bot] <24845621+abaplint[bot]@users.noreply.github.com>
Co-authored-by: Lars Hvam <larshp@hotmail.com>
This commit is contained in:
Albert Mink 2024-03-19 15:27:46 +01:00 committed by GitHub
parent cefc4dde7a
commit d260e3dfbb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 446 additions and 10 deletions

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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 <ls_file> LIKE LINE OF mt_files.
LOOP AT mt_files ASSIGNING <ls_file>.
" TODO: Maybe this should be in zcl_abapgit_filename_logic
FIND FIRST OCCURRENCE OF REGEX 'i18n\.([^.]{2})\.([^.]+)$' IN <ls_file>-filename SUBMATCHES lv_lang lv_ext.
CHECK sy-subrc = 0.
IF sy-subrc = 0 AND lv_ext = `properties`.
APPEND <ls_file> TO rt_result.
mark_accessed(
iv_path = <ls_file>-path
iv_file = <ls_file>-filename
iv_sha1 = <ls_file>-sha1 ).
ENDIF.
ENDLOOP.
ENDMETHOD.
ENDCLASS.

View File

@ -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.

View File

@ -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.

View File

@ -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.