Skip to Content

Testing Z_CODE_LIGHTER2

Testing… 8pt font.

Uncle Bob would be refactoring that render_source( ) method like mad, I feel ūüôā . Or would he reimplement the renderer while writing a comprehensive test suite for it? ūüėõ

Z_CODE_LIGHTER2 is courtessy of Manish Kumar

REPORT z_code_lighter2.

*———————————————————————-*
*      CLASS LCL_SEDI_ADT_HTML_RENDERER DEFINITION
*———————————————————————-*
* Copied from CL_SEDI_ADT_HTML_RENDERER which is missing in 702 release
*———————————————————————-*
CLASS lcl_sedi_adt_html_renderer DEFINITION
  FINAL
  CREATE PRIVATE .

  PUBLIC SECTION.

¬†¬†¬† “! Creates an instance of the renderer
¬†¬†¬† “!
¬†¬†¬† “! @parameter result | Instance of the renderer
    CLASS-METHODS create_instance
      RETURNING
        value(result) TYPE REF TO lcl_sedi_adt_html_renderer .
¬†¬†¬† “! Renders ABAP source code as HTML snippet and sets a CSS class for the following
¬†¬†¬† “! artifacts to allow syntax coloring
¬†¬†¬† “! <ul>
¬†¬†¬† “!¬† <li>keywords</li>
¬†¬†¬† “!¬† <li>comments</li>
¬†¬†¬† “!¬† <li>literals</li>
¬†¬†¬† “!¬† <li>numbers</li>
¬†¬†¬† “! </ul>
¬†¬†¬† “!
¬†¬†¬† “! @parameter source¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬† | source code that shall be rendered as HTML
¬†¬†¬† “! @parameter css_class_keyword | CSS class representing an ABAP keyword
¬†¬†¬† “! @parameter css_class_comment | CSS class representing an ABAP comment
¬†¬†¬† “! @parameter css_class_literal | CSS class representing a literal in ABAP coding
¬†¬†¬† “! @parameter css_class_number¬† | CSS class representing a number in ABAP coding
¬†¬†¬† “! @parameter html_source¬†¬†¬†¬†¬† | source rendered as HTML snippet
    METHODS render_source
      IMPORTING
        !source TYPE rswsourcet
¬†¬†¬†¬†¬†¬†¬† !css_class_keyword TYPE string DEFAULT ‘keyword’
¬†¬†¬†¬†¬†¬†¬† !css_class_comment TYPE string DEFAULT ‘comment’
¬†¬†¬†¬†¬†¬†¬† !css_class_literal TYPE string DEFAULT ‘literal’
¬†¬†¬†¬†¬†¬†¬† !css_class_number TYPE string DEFAULT ‘number’
      RETURNING
        !value(html_source) TYPE rswsourcet .
  PROTECTED SECTION.
  PRIVATE SECTION.
ENDCLASS.¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬† “LCL_SEDI_ADT_HTML_RENDERER DEFINITION

*———————————————————————-*
*      CLASS LCL_SEDI_ADT_HTML_RENDERER IMPLEMENTATION
*———————————————————————-*
*
*———————————————————————-*
CLASS lcl_sedi_adt_html_renderer IMPLEMENTATION.

* <SIGNATURE>—————————————————————————————+
* | Static Public Method LCL_SEDI_ADT_HTML_RENDERER=>CREATE_INSTANCE
* +————————————————————————————————-+
* | [<-()] RESULT                        TYPE REF TO LCL_SEDI_ADT_HTML_RENDERER
* +————————————————————————————–</SIGNATURE>
  METHOD create_instance.
    CREATE OBJECT result.
¬† ENDMETHOD.¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬† “CREATE_INSTANCE

* <SIGNATURE>—————————————————————————————+

* | Instance Public Method LCL_SEDI_ADT_HTML_RENDERER->RENDER_SOURCE
* +————————————————————————————————-+
* | [—>] SOURCE¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬† TYPE¬†¬†¬†¬†¬†¬†¬† RSWSOURCET
* | [—>] CSS_CLASS_KEYWORD¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬† TYPE¬†¬†¬†¬†¬†¬†¬† STRING (default =’keyword’)
* | [—>] CSS_CLASS_COMMENT¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬† TYPE¬†¬†¬†¬†¬†¬†¬† STRING (default =’comment’)
* | [—>] CSS_CLASS_LITERAL¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬† TYPE¬†¬†¬†¬†¬†¬†¬† STRING (default =’literal’)
* | [—>] CSS_CLASS_NUMBER¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬† TYPE¬†¬†¬†¬†¬†¬†¬† STRING (default =’number’)
* | [<—] HTML_SOURCE¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬† TYPE¬†¬†¬†¬†¬†¬†¬† RSWSOURCET
* +————————————————————————————–</SIGNATURE>
  METHOD render_source.
    CONSTANTS:
¬†¬†¬†¬†¬† “! regex to find numbers
¬†¬†¬†¬†¬† co_regex_numbers¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬† TYPE string VALUE ‘[[:digit:]]+’ ##no_text,
¬†¬†¬†¬†¬† “! regex to match inline declarations: DATA(ABC)
¬†¬†¬†¬†¬† co_regex_inline_decl_data¬†¬†¬†¬†¬† TYPE string VALUE ‘DATA\([^\)[:space:]]+\)’ ##no_text,
¬†¬†¬†¬†¬† “! regex to match inline declarations: FIELD-SYMBOL(<ABC>)
¬†¬†¬†¬†¬† co_regex_inline_decl_field_sym TYPE string VALUE ‘FIELD\-SYMBOL\(<[^\)[:space:]]+>\)’ ##no_text.
    DATA:
      statements                    TYPE STANDARD TABLE OF sstmnt,
      tokens                        TYPE sana_stokesx_tab,
      offset_end_of_token          TYPE i,
¬†¬†¬†¬†¬† “! offset if a token must be splitted (e.g. token for inline declaration DATA(ABC))
      offset_to_split_token        TYPE i,
¬†¬†¬†¬†¬† “! length of second part of a splitted token
      length_rest_of_splitted_token TYPE i,
      in_string_template            TYPE abap_bool VALUE abap_false.
    FIELD-SYMBOLS:
      <statement>  LIKE LINE OF statements,
      <token>      LIKE LINE OF tokens,
      <source_line> LIKE LINE OF source.

    SCAN ABAP-SOURCE source STATEMENTS INTO statements TOKENS INTO tokens WITH ANALYSIS WITH COMMENTS.
    html_source = source.
¬†¬†¬† LOOP AT statements ASSIGNING <statement> WHERE type NA ‘MBE’.¬† “ignore (E)XEC-SQL-statements
¬†¬†¬†¬†¬† CALL FUNCTION ‘RS_QUALIFY_ABAP_TOKENS_STR’
        EXPORTING
          statement_type = <statement>-type
          index_from    = <statement>-from
          index_to      = <statement>-to
        CHANGING
          stokesx_tab    = tokens
        EXCEPTIONS
          OTHERS        = 5.
      IF sy-subrc <> 0.
        CONTINUE.
      ENDIF.
    ENDLOOP.
    SORT tokens BY row DESCENDING col DESCENDING.
¬†¬†¬† DELETE ADJACENT DUPLICATES FROM tokens. “to remove duplicate entries for colon-comma-semantics.
    LOOP AT tokens ASSIGNING <token>.
      TRY.
          READ TABLE html_source INDEX <token>-row ASSIGNING <source_line>.
          IF ( sy-subrc = 0 ).
            offset_end_of_token = <token>-col + <token>-len1.
            IF ( <token>-len1 > 0 ).
              CASE <token>-type.
                WHEN sana_tok_keyword OR sana_tok_word.
                  <source_line> =
                    <source_line>(<token>-col) &&
¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬† |<span class=”{ css_class_keyword }“>| &&
                    escape(
                      val = <source_line>+<token>-col(<token>-len1)
                      format = cl_abap_format=>e_html_text ) &&
¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬† ‘</span>’ && <source_line>+offset_end_of_token.
¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬† IF ( <token>-str = ‘|’ ).
                    in_string_template = boolc( in_string_template = abap_false ).
                  ENDIF.
                WHEN sana_tok_field.
                  IF in_string_template = abap_true AND <token>-len1 > 1.
¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬† <token>-len1 = <token>-len1 2 + count( val = <token>-str regex = ‘[\\{}|]’ ).
                    offset_end_of_token = <token>-col + <token>-len1.
                  ENDIF.
¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬† IF ( ( <token>-str(1) = ”” ) OR ( <token>-str(1) =¬† ‘`’ ) ). “color literals
                    <source_line> =
                      <source_line>(<token>-col) &&
¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬† |<span class=”{ css_class_literal }“>| &&
                      escape(
                        val = <source_line>+<token>-col(<token>-len1)
                        format = cl_abap_format=>e_html_text ) &&
¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬† ‘</span>’ && <source_line>+offset_end_of_token.
                  ELSEIF ( matches( val = <token>-str regex = co_regex_numbers ) ).
                    <source_line> =
                      <source_line>(<token>-col) &&
¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬† |<span class=”{ css_class_number }“>| &&
                      escape(
                        val = <source_line>+<token>-col(<token>-len1)
                        format = cl_abap_format=>e_html_text ) &&
¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬† ‘</span>’ && <source_line>+offset_end_of_token.
                  ELSEIF ( matches( val = <token>-str regex = co_regex_inline_decl_data ) ).
¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬† ” handle inline declaration DATA(abc) …
                    offset_end_of_token          = <token>-col + strlen( <token>-str ).
                    offset_to_split_token        = <token>-col + 4.
                    length_rest_of_splitted_token = strlen( <token>-str ) 4.
                    <source_line> =
                      <source_line>(<token>-col) &&
¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬† |<span class=”{ css_class_keyword }“>| &&
                      escape(
                        val = <source_line>+<token>-col(4)
                        format = cl_abap_format=>e_html_text ) &&
¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬† ‘</span>’ &&
                      escape(
                        val = <source_line>+offset_to_split_token(length_rest_of_splitted_token)
                        format = cl_abap_format=>e_html_text ) &&
                    <source_line>+offset_end_of_token.
                  ELSE.
¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬† ” escape each sub-token separately
                    DATA: lv_off TYPE i,
                          lv_len TYPE i.
                    lv_off = <token>-col + <token>-off3.
                    lv_len = <token>-len3.
                    <source_line> = replace( val = <source_line>  off = lv_off  len = lv_len
                                            with = escape(
                                              val = <source_line>+lv_off(lv_len)
                                              format = cl_abap_format=>e_html_text ) ) .
                    lv_off = <token>-col + <token>-off2.
                    lv_len = <token>-len2.
                    <source_line> = replace( val = <source_line>  off = lv_off  len = lv_len
                                            with = escape(
                                              val = <source_line>+lv_off(lv_len)
                                              format = cl_abap_format=>e_html_text ) ) .
                    lv_off = <token>-col.
                    lv_len = <token>-len1.
                    <source_line> = replace( val = <source_line>  off = lv_off  len = lv_len
                                            with = escape(
                                                val = <source_line>+lv_off(lv_len)
                                                format = cl_abap_format=>e_html_text ) ) .
                  ENDIF.
                WHEN sana_tok_field_def.
                  IF ( matches( val = <token>-str regex = co_regex_inline_decl_field_sym ) ).
¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬† “handle inline declaration field-symbol(abc) …
                    offset_end_of_token          = <token>-col + strlen( <token>-str ).
                    offset_to_split_token        = <token>-col + 12.
                    length_rest_of_splitted_token = strlen( <token>-str ) 12.
                    <source_line> =
                      <source_line>(<token>-col) &&
¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬† |<span class=”{ css_class_keyword }“>| &&
                      escape(
                        val = <source_line>+<token>-col(12)
                        format = cl_abap_format=>e_html_text ) &&
¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬† ‘</span>’ &&
                      escape(
                        val = <source_line>+offset_to_split_token(length_rest_of_splitted_token)
                        format = cl_abap_format=>e_html_text ) &&
                      <source_line>+offset_end_of_token.
                  ELSE.
                    <source_line> =
                      <source_line>(<token>-col) &&
                      escape(
                        val = <source_line>+<token>-col(<token>-len1)
                        format = cl_abap_format=>e_html_text ) &&
                        <source_line>+offset_end_of_token.
                  ENDIF.
¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬† WHEN ‘C’. “comment
                  <source_line> =
                    <source_line>(<token>-col) &&
¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬† |<span class=”{ css_class_comment }“>| &&
                    escape(
                      val = <source_line>+<token>-col
                      format = cl_abap_format=>e_html_text ) &&
¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬† ‘</span>’.
                WHEN OTHERS.
                  <source_line> =
                    <source_line>(<token>-col) &&
                    escape(
                      val = <source_line>+<token>-col(<token>-len1)
                      format = cl_abap_format=>e_html_text ) &&
                    <source_line>+offset_end_of_token.
              ENDCASE.
            ELSE.
              <source_line> =
                <source_line>(<token>-col) &&
                escape(
                  val = <source_line>+<token>-col(<token>-len1)
                  format = cl_abap_format=>e_html_text ) &&
                  <source_line>+offset_end_of_token.
            ENDIF.
          ENDIF.
        CATCH cx_sy_range_out_of_bounds.
          CONTINUE.
      ENDTRY.
    ENDLOOP.
¬† ENDMETHOD.¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬† “RENDER_SOURCE
ENDCLASS.¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬†¬† “LCL_SEDI_ADT_HTML_RENDERER IMPLEMENTATION

DATA:
  go_dock        TYPE REF TO cl_gui_docking_container,
  go_container_1 TYPE REF TO cl_gui_container,
  go_editor_1    TYPE REF TO cl_gui_textedit,
  go_container_2 TYPE REF TO cl_gui_container,
  go_editor_2    TYPE REF TO cl_gui_textedit,
  gt_code        TYPE STANDARD TABLE OF text1024.

SELECTION-SCREEN BEGIN OF SCREEN 2000.
SELECTION-SCREEN END OF SCREEN 2000.

START-OF-SELECTION.

  CALL SELECTION-SCREEN 2000.

AT SELECTION-SCREEN.
  FIELD-SYMBOLS <gs_code> LIKE LINE OF gt_code.
  DATA gv_xml        TYPE string.
  DATA go_root        TYPE REF TO cx_root.
  DATA gt_rswsourcet  TYPE rswsourcet.

  REFRESH gt_code.
  CALL METHOD go_editor_1->get_text_as_r3table
    IMPORTING
      table = gt_code.

¬† “below method call will put css styles at correct positions of code
  gt_rswsourcet = gt_code.
  gt_rswsourcet = lcl_sedi_adt_html_renderer=>create_instance( )->render_source(
      source = gt_rswsourcet
  ).
  gt_code = gt_rswsourcet.

¬† “each code line is a paragraph in html with fixed width font
  LOOP AT gt_code ASSIGNING <gs_code>.
    CONCATENATE

¬†¬†¬†¬†¬† ‘<span style=”font-family: courier new,courier; font-size: 8pt;”>’

¬†¬†¬†¬†¬† <gs_code> ‘</span><br/>’

      INTO <gs_code>.
  ENDLOOP.

¬† “wrap root node to create valid xml
¬† “it will also insert quote in jive text box
¬† INSERT ‘<pre class=”jive_text_macro jive_macro_quote” jivemacro=”quote”><p>’

    INTO gt_code INDEX 1.
¬† APPEND ‘</p></pre>’ TO gt_code.

¬† “convert source table to string and do xslt transformation
¬† “the transformation will do css inlining
¬† “resulting code will have css style as well as inline style
¬† “it is also possible be remove css completely but doing so will reduce readability
  gv_xml = concat_lines_of( gt_code ).
  TRY .
      CALL TRANSFORMATION z_code_lighter2
        SOURCE XML gv_xml
        RESULT XML gt_code.
    CATCH cx_root INTO go_root.
¬†¬†¬†¬†¬† gv_xml = go_root->get_text( ).¬† “view exception text in debug
  ENDTRY.

  CALL METHOD go_editor_2->set_text_as_stream
    EXPORTING
      text            = gt_code
    EXCEPTIONS
      error_dp        = 1
      error_dp_create = 2
      OTHERS          = 3.

  CALL SELECTION-SCREEN 2000.

AT SELECTION-SCREEN OUTPUT.
  CHECK go_dock IS NOT BOUND.
  CREATE OBJECT go_dock
    EXPORTING
      parent = cl_gui_container=>screen0
      side  = cl_gui_docking_container=>dock_at_left
      ratio  = 90.

  DATA go_splitter TYPE REF TO cl_gui_splitter_container.

  CREATE OBJECT go_splitter
    EXPORTING
      parent  = go_dock
      rows    = 1
      columns = 2.

  CHECK go_container_1 IS NOT BOUND.
  CALL METHOD go_splitter->get_container
    EXPORTING
      row      = 1
      column    = 1
    RECEIVING
      container = go_container_1.

  CHECK go_editor_1 IS NOT BOUND.

  CREATE OBJECT go_editor_1
    EXPORTING
      parent                    = go_container_1
      wordwrap_mode              = cl_gui_textedit=>wordwrap_at_fixed_position
      wordwrap_position          = 1024
      wordwrap_to_linebreak_mode = cl_gui_textedit=>true.

  CHECK go_container_2 IS NOT BOUND.
  CALL METHOD go_splitter->get_container
    EXPORTING
      row      = 1
      column    = 2
    RECEIVING
      container = go_container_2.

  CHECK go_container_2 IS BOUND.
  CHECK go_editor_2 IS NOT BOUND.

  CREATE OBJECT go_editor_2
    EXPORTING
      parent                    = go_container_2
      wordwrap_mode              = cl_gui_textedit=>wordwrap_at_fixed_position
      wordwrap_position          = 1024
      wordwrap_to_linebreak_mode = cl_gui_textedit=>true.

  IF go_editor_2 IS BOUND.
    CALL METHOD go_editor_2->set_readonly_mode.
  ENDIF.

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