abapGit/src/repo/utils/zcl_abapgit_version.clas.abap
Christian Günter bf76fedf19
Do a syntax check before accessing version constant (#6733)
Co-authored-by: Marc Bernard <59966492+mbtools@users.noreply.github.com>
2024-01-07 12:42:02 +01:00

281 lines
7.6 KiB
ABAP

CLASS zcl_abapgit_version DEFINITION
PUBLIC
FINAL
CREATE PUBLIC .
PUBLIC SECTION.
CLASS-METHODS normalize
IMPORTING
!iv_version TYPE string
RETURNING
VALUE(rv_version) TYPE string .
CLASS-METHODS conv_str_to_version
IMPORTING
!iv_version TYPE csequence
RETURNING
VALUE(rs_version) TYPE zif_abapgit_definitions=>ty_version
RAISING
zcx_abapgit_exception .
CLASS-METHODS check_dependant_version
IMPORTING
!is_current TYPE zif_abapgit_definitions=>ty_version
!is_dependant TYPE zif_abapgit_definitions=>ty_version
RAISING
zcx_abapgit_exception .
CLASS-METHODS compare
IMPORTING
!iv_a TYPE string OPTIONAL
!iv_b TYPE string OPTIONAL
!is_a TYPE zif_abapgit_definitions=>ty_version OPTIONAL
!is_b TYPE zif_abapgit_definitions=>ty_version OPTIONAL
RETURNING
VALUE(rv_result) TYPE i .
CLASS-METHODS get_version_constant_value
IMPORTING
iv_version_constant TYPE string
RETURNING
VALUE(rv_version) TYPE string
RAISING
zcx_abapgit_exception.
PROTECTED SECTION.
PRIVATE SECTION.
CLASS-METHODS version_to_numeric
IMPORTING
!iv_version TYPE string
RETURNING
VALUE(rv_version) TYPE i.
ENDCLASS.
CLASS zcl_abapgit_version IMPLEMENTATION.
METHOD check_dependant_version.
CONSTANTS: lc_message TYPE string VALUE 'Current version is older than required'.
IF is_dependant-major > is_current-major.
zcx_abapgit_exception=>raise( lc_message ).
ELSEIF is_dependant-major < is_current-major.
RETURN.
ENDIF.
IF is_dependant-minor > is_current-minor.
zcx_abapgit_exception=>raise( lc_message ).
ELSEIF is_dependant-minor < is_current-minor.
RETURN.
ENDIF.
IF is_dependant-patch > is_current-patch.
zcx_abapgit_exception=>raise( lc_message ).
ELSEIF is_dependant-patch < is_current-patch.
RETURN.
ENDIF.
IF is_current-prerelase IS INITIAL.
RETURN.
ENDIF.
CASE is_current-prerelase.
WHEN 'rc'.
IF is_dependant-prerelase = ''.
zcx_abapgit_exception=>raise( lc_message ).
ENDIF.
WHEN 'beta'.
IF is_dependant-prerelase = '' OR is_dependant-prerelase = 'rc'.
zcx_abapgit_exception=>raise( lc_message ).
ENDIF.
WHEN 'alpha'.
IF is_dependant-prerelase = '' OR is_dependant-prerelase = 'rc' OR is_dependant-prerelase = 'beta'.
zcx_abapgit_exception=>raise( lc_message ).
ENDIF.
ENDCASE.
IF is_dependant-prerelase = is_current-prerelase AND is_dependant-prerelase_patch > is_current-prerelase_patch.
zcx_abapgit_exception=>raise( lc_message ).
ENDIF.
ENDMETHOD.
METHOD compare.
DATA: ls_version_a TYPE zif_abapgit_definitions=>ty_version,
ls_version_b TYPE zif_abapgit_definitions=>ty_version.
TRY.
IF is_a IS NOT INITIAL.
ls_version_a = is_a.
ELSE.
ls_version_a = conv_str_to_version( iv_a ).
ENDIF.
IF is_b IS NOT INITIAL.
ls_version_b = is_b.
ELSE.
ls_version_b = conv_str_to_version( iv_b ).
ENDIF.
CATCH zcx_abapgit_exception.
rv_result = 0.
RETURN.
ENDTRY.
IF ls_version_a = ls_version_b.
rv_result = 0.
ELSE.
TRY.
check_dependant_version( is_current = ls_version_a
is_dependant = ls_version_b ).
rv_result = 1.
CATCH zcx_abapgit_exception.
rv_result = -1.
RETURN.
ENDTRY.
ENDIF.
ENDMETHOD.
METHOD conv_str_to_version.
DATA: lt_segments TYPE STANDARD TABLE OF string,
lt_parts TYPE STANDARD TABLE OF string,
lv_segment TYPE string.
SPLIT iv_version AT '-' INTO TABLE lt_segments.
READ TABLE lt_segments INTO lv_segment INDEX 1. " Version
IF sy-subrc <> 0. " No version
RETURN.
ENDIF.
SPLIT lv_segment AT '.' INTO TABLE lt_parts.
LOOP AT lt_parts INTO lv_segment.
TRY.
CASE sy-tabix.
WHEN 1.
rs_version-major = lv_segment.
WHEN 2.
rs_version-minor = lv_segment.
WHEN 3.
rs_version-patch = lv_segment.
ENDCASE.
CATCH cx_sy_conversion_no_number.
zcx_abapgit_exception=>raise( 'Incorrect format for Semantic Version' ).
ENDTRY.
ENDLOOP.
READ TABLE lt_segments INTO lv_segment INDEX 2. " Pre-release Version
IF sy-subrc <> 0. " No version
RETURN.
ENDIF.
SPLIT lv_segment AT '.' INTO TABLE lt_parts.
LOOP AT lt_parts INTO lv_segment.
CASE sy-tabix.
WHEN 1.
rs_version-prerelase = lv_segment.
TRANSLATE rs_version-prerelase TO LOWER CASE.
WHEN 2.
rs_version-prerelase_patch = lv_segment.
ENDCASE.
ENDLOOP.
IF rs_version-prerelase <> 'rc' AND rs_version-prerelase <> 'beta' AND rs_version-prerelase <> 'alpha'.
zcx_abapgit_exception=>raise( 'Incorrect format for Semantic Version' ).
ENDIF.
ENDMETHOD.
METHOD normalize.
" Internal program version should be in format "XXX.XXX.XXX" or "vXXX.XXX.XXX"
CONSTANTS:
lc_version_pattern TYPE string VALUE '^v?(\d{1,3}\.\d{1,3}\.\d{1,3})\s*$',
lc_prerelease_pattern TYPE string VALUE '^((rc|beta|alpha)\.\d{1,3})\s*$'.
DATA: lv_version TYPE string,
lv_prerelease TYPE string,
lv_version_n TYPE string,
lv_prerelease_n TYPE string.
SPLIT iv_version AT '-' INTO lv_version lv_prerelease.
FIND FIRST OCCURRENCE OF REGEX lc_version_pattern
IN lv_version SUBMATCHES lv_version_n.
IF lv_prerelease IS NOT INITIAL.
FIND FIRST OCCURRENCE OF REGEX lc_prerelease_pattern
IN lv_prerelease SUBMATCHES lv_prerelease_n.
ENDIF.
IF lv_version_n IS INITIAL.
RETURN.
ENDIF.
rv_version = lv_version_n.
IF lv_prerelease_n IS NOT INITIAL.
CONCATENATE rv_version '-' lv_prerelease_n INTO rv_version.
ENDIF.
ENDMETHOD.
METHOD version_to_numeric.
DATA: lv_major TYPE n LENGTH 4,
lv_minor TYPE n LENGTH 4,
lv_release TYPE n LENGTH 4.
SPLIT iv_version AT '.' INTO lv_major lv_minor lv_release.
" Calculated value of version number, empty version will become 0 which is OK
rv_version = lv_major * 1000000 + lv_minor * 1000 + lv_release.
ENDMETHOD.
METHOD get_version_constant_value.
DATA: lv_version_class TYPE seoclsname,
lv_version_component TYPE string.
FIELD-SYMBOLS: <lv_version> TYPE string.
IF iv_version_constant NP '*=>*'.
zcx_abapgit_exception=>raise( 'Version constant needs to use the format CLASS=>CONSTANT' ).
ENDIF.
SPLIT iv_version_constant AT '=>' INTO lv_version_class lv_version_component.
IF sy-subrc <> 0 OR lv_version_class IS INITIAL OR lv_version_component IS INITIAL.
zcx_abapgit_exception=>raise( 'Version constant cannot be parsed' ).
ENDIF.
" You should remember that accessing a class or an interface with syntax errors
" gives us a shortdump. Therefore we do a syntax check here.
zcl_abapgit_oo_factory=>make_by_name( lv_version_class )->syntax_check( lv_version_class ).
ASSIGN (lv_version_class)=>(lv_version_component) TO <lv_version>.
IF sy-subrc = 0.
rv_version = <lv_version>.
ELSE.
zcx_abapgit_exception=>raise( |Could not access version at class { lv_version_class } component | &&
|{ lv_version_component }| ).
ENDIF.
ENDMETHOD.
ENDCLASS.