Skip to Content
Author's profile photo Jacques Nomssi Nzali

PlantUML Diagrams

Introduction

Evidence from ABAP blogs suggests developers communicate to their audience using

  • A lot of (technical) text,
  • (ABAP) code blocks,
  • Some (annotated) screenshots,
  • Few pictures,
  • Little or no diagrams.

For most of us, a diagram is easier to consume than text. Each diagram type communicates intent in a special way. It helps to reason about software and eases knowledge transfer. So many disparate notations were created that they had to be standardized.

Nowadays, the Unified Modeling Language (UML) is an established notation to visualize designs using following diagram types:

Hierarchy of UML 2.2 Diagrams, shown as a class diagram

UML Essentials

To decide which diagram type communicates our intent best, we must understand its essential features. I will single out

  • Requirement specifications: we might want to visualize requirements in a use case diagram  (also check: activity diagrams, state diagrams)
  • Modularization elements: we want to provide a bird’s-eye view of the code structure using class diagrams  (object diagrams, composite structure diagram)
  • The dynamic behavior of code: we want to emphasize interfaces in a sequence diagram. (or timing diagram)

and refer to some tutorials, e.g.

How to Create Diagrams

Create a rough sketch on paper by hand, capture the picture with a phone/camera and upload it.

OK, I have to work on my skills and do image pre-processing, but this is probably as easy as creating an annotated screenshot.

Without appropriate tools, creating diagrams (e.g. like this one)

would take too much time, we will see copy/pasted source code in the documentation. Many visualization tools are available, some free (search the web for free UML tools), others with a price tag and easy to use templates. I will propose to select one and get used to it, so the effort needed to create those diagrams is reduced.

Try PlantUML

Let me introduce you to the open source PlantUML tool that creates UML diagrams from plain text. It uses the graph visualization software Graphviz to lay out diagrams of remarkable quality.

PlantUML (site http://plantuml.com/) is a mature project integrated with many content management systems, text editors and other tools. At least 2 existing blogs reference PlantUML on SCN:

The concept of working with plain text should resonate with programmers. The API can be called from many programming languages. I present an ABAP interface to the PlantUML web service in the appendix.

With the hope that we will see more diagrams, as the costs/effort to create them are reduced.

State Diagram

Real programmers do not read documentation: go to the PlantUML online server, enter:

@startuml
[*] --> ContractHeader 
ContractHeader : Screen 201
ContractHeader --> Partners
Partners : Screens 102, 101, 201
Partners --> ProcessItems
state ProcessItems {
 ItemsOverview --> ItemsDetail
 ItemsOverview : Screens 220, 221
 ItemsDetail : Screens 211 
 ItemsDetail -> ItemsAddData
 ItemsAddData : Screens 212
}
ProcessItems --> [*]
@enduml

and get this!

Activity Diagrams

This diagram type is well suited for the description of processes. ABAP code using FORM as modularization unit is easier to reason about if the code reflects the hierarchy of the abstraction levels (top down decomposition).

@startuml
start
:Init Transaction;
:Query Documents
- Fecth Header 
- Authority Check 
- Fetch all items 
- Additional Data;
if (Entry found?) then (View)
 :ALV Display;
else (Error)
 :Message;
endif
stop
@enduml

Class Diagrams

describe the static structure (topology) of the code. We want classes with single responsibility, where the abstractions are visible in the code structure.

@startuml
LCL_FILTER <|-- LCL_FILTER_NULL
LCL_FILTER <|-- LCL_FILTER_CUSTOM

LCL_FILTER : accepts( )
LCL_FILTER_NULL : accepts( )
LCL_FILTER_CUSTOM : accepts( )
@enduml

You can already generate UML class diagrams using report UML_CLASS_DIAGRAM if you have installed the correct Java OCX plugin on your machine. Find a custom version using PlantUML in the appendix.

Sequence Diagrams

This diagram type visualizes coupling between objects. Code is easier to change when objects working have minimal knowledge about each other.

@startuml
hide footbox
actor User
User -> LCL_PRICE : Process( )
LCL_PRICE -> LCL_INFREC : Transfer( )
LCL_INFREC -> LCL_BDC : PRICE_UPDATE( )

box "Internal Service" #LightBlue
 participant LCL_INFREC
 participant LCL_BDC
end box
@enduml

You can generate sequence diagrams using transaction SAT.

Summary

Diagrams are more expressive than code but tools are needed. Try PlantUML at http://plantuml.com/.

Appendix

  • This program generates UML class diagrams automatically from existing ABAP code.
  • The diagrams are generated and displayed from the SAP GUI without any further settings.
  • Local class LCL_PLANT_UML implements access to the PlantUML web service.

*&---------------------------------------------------------------------*
"! UML Class XMI exporter (inspired by program UML_CLASS_DIAGRAM)
"! Installation of JNET is not required.
REPORT zz_uml_class_export.

*NAME     TEXT
*SO_DEVC  Package
*SO_ROOT  Object Name
*X_AGGR    Aggregations (<>--)
*X_ASOS    Associations (-->)
*X_ATTR    List Attributes
*X_CONS    Display Constants
*X_EXCEP  Exceptions (<<throws>>)
*X_FRND   Friends
*X_FUGR    Analyze Function Groups
*X_LOCL    Analyze Local Objects
*X_METH    List Methods
*X_PACKS  Package Member (~)
*X_PRIV    Private Member  (-)
*X_PROG    Analyze Program
*X_PROT    Protected Member (#)
*X_STRUCT  Structures  (<<data Type>>)
*X_USES    Dependency (<<uses>>)

*SYMBOL  TEXT
*007  Primary Selection Set
*008  Statements UML Scanner
*009  UML Representation Options
*SC1  Selection of Dev. Objects to Be Evaluated
*SC2  Options for Display of UML Class Diagrams
*SC7  Work Instructions for UML Scanner
*P01  PlantUML

TABLES sscrfields.                          " Selection Screen Fields

SELECTION-SCREEN FUNCTION KEY 1.                     "#EC CI_USE_WANTED
SELECTION-SCREEN FUNCTION KEY 2.                     "#EC CI_USE_WANTED

DATA gs_tadir TYPE tadir.
" ----------------------------------------------------------------------------------------------------------------------------------- *
" selection screen definition
SELECTION-SCREEN: BEGIN OF TABBED BLOCK mytab FOR 18 LINES,
                    TAB (40) button1 USER-COMMAND push1 DEFAULT SCREEN 510,
                    TAB (40) button2 USER-COMMAND push2 DEFAULT SCREEN 520,
                    TAB (40) button3 USER-COMMAND push3 DEFAULT SCREEN 530,
                  END OF BLOCK mytab.

" primary selection criteria
SELECTION-SCREEN BEGIN OF SCREEN 510 AS SUBSCREEN.
SELECTION-SCREEN BEGIN OF BLOCK s1 WITH FRAME TITLE text-sc1.
SELECT-OPTIONS: so_root  FOR gs_tadir-obj_name,  " root objects
                so_devc  FOR gs_tadir-devclass.  " root objects dev-class
SELECTION-SCREEN END OF BLOCK s1.
SELECTION-SCREEN END OF SCREEN 510.

" UML scanner parameters
SELECTION-SCREEN BEGIN OF SCREEN 520 AS SUBSCREEN.
SELECTION-SCREEN BEGIN OF BLOCK b0 WITH FRAME TITLE text-sc7.
PARAMETERS: x_locl   TYPE flag AS CHECKBOX DEFAULT 'X',
            x_prog   TYPE flag AS CHECKBOX DEFAULT 'X',
            x_fugr   TYPE flag AS CHECKBOX DEFAULT 'X',
            x_struct TYPE flag AS CHECKBOX DEFAULT ' '.
SELECTION-SCREEN END OF BLOCK b0.
SELECTION-SCREEN END OF SCREEN 520.

" diagram option parameters
SELECTION-SCREEN BEGIN OF SCREEN 530 AS SUBSCREEN.
SELECTION-SCREEN BEGIN OF BLOCK b1 WITH FRAME TITLE text-sc2.
PARAMETERS: x_priv  TYPE flag AS CHECKBOX DEFAULT 'X',
            x_prot  TYPE flag AS CHECKBOX DEFAULT 'X',
            x_packs TYPE flag AS CHECKBOX DEFAULT 'X',
            x_attr  TYPE flag AS CHECKBOX DEFAULT 'X',
            x_cons  TYPE flag AS CHECKBOX DEFAULT 'X',
            x_meth  TYPE flag AS CHECKBOX DEFAULT 'X',
            x_uses  TYPE flag AS CHECKBOX DEFAULT ' ',
            x_frnd  TYPE flag AS CHECKBOX DEFAULT 'X',
            x_excep TYPE flag AS CHECKBOX DEFAULT ' ',
            x_asos  TYPE flag AS CHECKBOX DEFAULT ' ',
            x_aggr  TYPE flag AS CHECKBOX DEFAULT ' '.
SELECTION-SCREEN END OF BLOCK b1.
SELECTION-SCREEN END OF SCREEN 530.

" ----------------------------------------------------------------------------------------------------------------------------------- *
" types/classes/interfaces

TYPES:  BEGIN OF ts_uml_config,
          show_aggregations TYPE flag, " show aggregations
          show_associations TYPE flag, " show associations
          show_uses         TYPE flag, " <<call>> show dependencies
          show_friends      TYPE flag, " show friends
          show_throws       TYPE flag, " show used exceptions
        END OF ts_uml_config.

TYPES:  BEGIN OF ts_scan_config,
          attributes TYPE flag,
          methods TYPE flag,
          constants TYPE flag,
          private_member TYPE flag,
          protected_member TYPE flag,
          packaged_member TYPE flag,

          scan_local_types TYPE flag,
          scan_programs TYPE flag,
          scan_function_groups TYPE flag,
          add_structures TYPE flag,
          scan_used_types TYPE flag,
        END OF ts_scan_config.


TYPES tv_scale TYPE perct.
CONSTANTS c_default_scale TYPE tv_scale VALUE '0.6'.
TYPES: BEGIN OF ts_diagram_config,
         local_path        TYPE string,
         java_jar          TYPE string,
         java_appl         TYPE string,
         server_url        TYPE string,
         output_mode       TYPE char01,
         skip_dialog       TYPE flag,
         scale             TYPE tv_scale,
         handwritten       TYPE flag,
         shadowing         TYPE flag,
         display_source    TYPE flag,
       END OF ts_diagram_config.

*----------------------------------------------------------------------*
*       INTERFACE lif_unit_test
*----------------------------------------------------------------------*
INTERFACE lif_unit_test.
ENDINTERFACE.                    "lif_unit_test

"! Parameter Interface
*----------------------------------------------------------------------*
*       INTERFACE lif_parameter
*----------------------------------------------------------------------*
INTERFACE lif_parameter.
  METHODS get_display_config RETURNING VALUE(rs_cfg) TYPE ts_uml_config.
  METHODS get_scan_config RETURNING VALUE(rs_scan) TYPE ts_scan_config.
  METHODS get_scan_ranges CHANGING ct_so_packages TYPE cl_uml_class_scanner=>uml_range_packages
                                   ct_so_objects  TYPE cl_uml_class_scanner=>uml_range_types.
ENDINTERFACE.                    "lif_parameter

"! Parameter Class
*----------------------------------------------------------------------*
*       CLASS lcl_parameter DEFINITION
*----------------------------------------------------------------------*
CLASS lcl_parameter DEFINITION.
  PUBLIC SECTION.
    INTERFACES lif_parameter.

    "! constructor
    "! @parameter iv_progname | program name
    METHODS constructor IMPORTING iv_progname TYPE progname.

  PRIVATE SECTION.
    TYPES tt_options TYPE RANGE OF text30.
    "! selection screen parameters
    DATA mt_parameters TYPE SORTED TABLE OF rsparamsl_255 WITH NON-UNIQUE KEY selname.

    METHODS get_boolean IMPORTING iv_parameter_name TYPE selname
                        RETURNING VALUE(rv_value)   TYPE flag
                        RAISING   cx_dynamic_check.

    METHODS get_user_input IMPORTING iv_progname TYPE raldb_repo.

    "! get select option table parameter
    "! @parameter rt_so_value | selection option values
    METHODS get_parameter_so_tab IMPORTING iv_parameter_name  TYPE selname
                                 RETURNING VALUE(rt_so_value) TYPE tt_options.
ENDCLASS.                    "lcl_parameter DEFINITION

CLASS lcl_scanner DEFINITION INHERITING FROM cl_uml_class_scanner.
  PUBLIC SECTION.
    METHODS constructor IMPORTING ii_parameter TYPE REF TO lif_parameter.
    METHODS class_scan RETURNING VALUE(rv_objects_selected) TYPE flag.
  PRIVATE SECTION.
    DATA mi_parameter TYPE REF TO lif_parameter.
ENDCLASS.

* Abstract UML code generator
CLASS lcl_uml DEFINITION FRIENDS lif_unit_test.
  PUBLIC SECTION.
    CLASS-METHODS new IMPORTING is_cfg TYPE ts_diagram_config
                      RETURNING VALUE(ro_uml) TYPE REF TO lcl_uml.

    METHODS add IMPORTING iv_code TYPE string.
    METHODS get RETURNING VALUE(rv_diagram) TYPE string.
  PROTECTED SECTION.
    DATA mv_diagram TYPE string.
    METHODS header IMPORTING is_cfg TYPE ts_diagram_config.
    METHODS footer.
ENDCLASS.                    "lcl_uml DEFINITION

*----------------------------------------------------------------------*
*       CLASS lcl_iterator DEFINITION
*----------------------------------------------------------------------*
CLASS lcl_iterator DEFINITION FRIENDS lif_unit_test.
  PUBLIC SECTION.
*   Convert and filter the trace for further processing
    TYPES ts_uml TYPE cl_uml_class_scanner=>uml_line.
    TYPES tt_uml TYPE STANDARD TABLE OF ts_uml WITH KEY name.

    METHODS constructor IMPORTING it_data  TYPE tt_uml
                                  iv_start TYPE sytabix DEFAULT 1
                                  iv_stop  TYPE sytabix OPTIONAL.
    METHODS next RETURNING VALUE(rs_uml) TYPE ts_uml
                 RAISING   cx_dynamic_check.
    METHODS has_next RETURNING VALUE(rv_flag) TYPE xsdboolean.
    METHODS skip IMPORTING iv_count TYPE i DEFAULT 1.
  PROTECTED SECTION.
    DATA mv_idx TYPE sytabix.
    DATA mv_size TYPE sytabix.
    DATA mt_data TYPE tt_uml.
ENDCLASS.                    "lcl_iterator DEFINITION

*----------------------------------------------------------------------*
*       CLASS lcl_uml_class DEFINITION
*----------------------------------------------------------------------*
CLASS lcl_uml_class DEFINITION FRIENDS lif_unit_test.
  PUBLIC SECTION.
    TYPES ts_uml TYPE lcl_iterator=>ts_uml.

    METHODS constructor IMPORTING io_uml        TYPE REF TO lcl_uml
                                  is_uml_config TYPE ts_uml_config.
    METHODS to_uml_text IMPORTING is_uml TYPE ts_uml.
  PRIVATE SECTION.
    METHODS plant_uml_class.

    METHODS uml_fields.
    METHODS uml_methods.
    METHODS uml_events.

    METHODS uml_add
      IMPORTING iv_abstract   TYPE xsdboolean DEFAULT abap_false
                iv_visibility TYPE char01
                iv_class      TYPE xsdboolean
                iv_name       TYPE csequence.
    METHODS uml_reduce IMPORTING it_data   TYPE STANDARD TABLE
                                 iv_sep    TYPE char3
                                 iv_suffix TYPE csequence OPTIONAL
                                 iv_active TYPE xsdboolean DEFAULT abap_true.
    METHODS begin_class.
    METHODS end_class.

    METHODS class_member.
    METHODS visibility IMPORTING iv_visibility TYPE char01.

    METHODS set_data IMPORTING is_uml TYPE ts_uml.

    METHODS get_class RETURNING VALUE(rv_class) TYPE string.

    METHODS get_name IMPORTING iv_name        TYPE csequence
                     RETURNING VALUE(rv_name) TYPE string.
    METHODS get_container RETURNING VALUE(rv_name) TYPE string.

    METHODS escape IMPORTING iv_name TYPE csequence RETURNING VALUE(rv_name) TYPE string.

    DATA mo_uml TYPE REF TO lcl_uml.
    DATA ms_uml TYPE ts_uml.
    DATA mv_name TYPE string.
    DATA ms_config TYPE ts_uml_config.
ENDCLASS.                    "lcl_uml_class DEFINITION

CLASS lcl_file_name DEFINITION FRIENDS lif_unit_test.
  PUBLIC SECTION.
    CLASS-METHODS new IMPORTING iv_mode        TYPE char01
                      RETURNING VALUE(ro_file) TYPE REF TO lcl_file_name.
    METHODS constructor IMPORTING iv_mode TYPE char01.
    METHODS dialog RETURNING VALUE(rv_user_action) TYPE i.
    METHODS get_prefix RETURNING VALUE(rv_name) TYPE string
                       RAISING   cx_dynamic_check.
    METHODS get_fullpath RETURNING VALUE(rv_name) TYPE string.
  PROTECTED SECTION.
    TYPES: BEGIN OF ts_fullpath,
             title  TYPE string,
             name   TYPE string,
             ext    TYPE string,
             path   TYPE string,
             filter TYPE string,
           END OF ts_fullpath.
    DATA ms_file TYPE ts_fullpath.
ENDCLASS.

*----------------------------------------------------------------------*
*       CLASS lcl_file DEFINITION
*----------------------------------------------------------------------*
CLASS lcl_file DEFINITION CREATE PRIVATE.
  PUBLIC SECTION.
    CONSTANTS:
      c_mode_xmi TYPE char01 VALUE 'X',
      c_mode_txt TYPE char01 VALUE space,
      c_mode_png TYPE char01 VALUE 'P'.

    CLASS-METHODS download
      IMPORTING iv_data         TYPE xstring
                io_name         TYPE REF TO lcl_file_name
      RETURNING VALUE(rv_subrc) TYPE sysubrc.
ENDCLASS.                    "lcl_file DEFINITION

CLASS lcl_file_name_dummy DEFINITION INHERITING FROM lcl_file_name FRIENDS lif_unit_test.
  PUBLIC SECTION.
    METHODS dialog REDEFINITION.
ENDCLASS.

*----------------------------------------------------------------------*
*       CLASS lcl_plant_uml DEFINITION
*----------------------------------------------------------------------*
CLASS lcl_plant_uml DEFINITION.
  PUBLIC SECTION.
    CONSTANTS c_plantuml_server TYPE string
      VALUE 'http://www.plantuml.com/plantuml/img/'  ##NO_TEXT.

    METHODS constructor IMPORTING iv_diagram TYPE string.
    METHODS to_url IMPORTING iv_base_url   TYPE string DEFAULT c_plantuml_server
                   RETURNING VALUE(rv_url) TYPE string
                   RAISING   cx_dynamic_check.
    METHODS output IMPORTING is_cfg TYPE ts_diagram_config RAISING cx_dynamic_check.
  PROTECTED SECTION.
    TYPES tv_base64 TYPE c LENGTH 65.
    CONSTANTS:
      c_standard TYPE tv_base64 VALUE 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='  ##NO_TEXT,
      c_plantuml TYPE tv_base64 VALUE '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_0' ##NO_TEXT.
    DATA mv_diagram TYPE string.

    METHODS to_xstring IMPORTING iv_string         TYPE string
                       RETURNING VALUE(rv_xstring) TYPE xstring
                       RAISING   cx_dynamic_check.
    METHODS source IMPORTING iv_display_source TYPE flag
                   RETURNING VALUE(rv_source) TYPE string.

    METHODS png_file_name IMPORTING io_name  TYPE REF TO lcl_file_name
                                    is_cfg   TYPE ts_diagram_config
                          RETURNING VALUE(rv_name) TYPE string.

    METHODS parameter_string IMPORTING io_name         TYPE REF TO lcl_file_name
                                       is_cfg TYPE ts_diagram_config
                             RETURNING VALUE(rv_param) TYPE string.
    METHODS show_html IMPORTING iv_html TYPE string
                      RAISING   cx_dynamic_check.
    METHODS to_png IMPORTING io_name        TYPE REF TO lcl_file_name
                             is_cfg TYPE ts_diagram_config
                   RETURNING VALUE(rv_name) TYPE string.

ENDCLASS.                    "lcl_plant_uml DEFINITION

"! Output interface
*----------------------------------------------------------------------*
*       INTERFACE lif_output DEFINITION
*----------------------------------------------------------------------*
INTERFACE lif_output.
  "! export data
  "! @parameter io_uml_class_scanner | UML class scanner instance
  METHODS output IMPORTING ii_parameter TYPE REF TO lif_parameter
                 RETURNING VALUE(rv_flag) TYPE flag.
ENDINTERFACE.                    "lif_output DEFINITION

CLASS lcl_xmi_output DEFINITION.
  PUBLIC SECTION.
    INTERFACES lif_output.
  PRIVATE SECTION.
    CLASS-METHODS new_xmi IMPORTING io_scanner   TYPE REF TO cl_uml_class_scanner
                                    ii_parameter TYPE REF TO lif_parameter
                          RETURNING VALUE(ro_xmi) TYPE REF TO cl_uml_class_decor_xmi.
ENDCLASS.                    "lcl_xmi_output DEFINITION

CLASS lcl_plantuml_output DEFINITION.
  PUBLIC SECTION.
    INTERFACES lif_output.
ENDCLASS.                    "lcl_plantuml_output DEFINITION

CLASS lcl_null_output DEFINITION.
  PUBLIC SECTION.
    INTERFACES lif_output.
ENDCLASS.                    "lcl_xmi_output DEFINITION

*----------------------------------------------------------------------*
*       CLASS lcl_class_diagram DEFINITION
*----------------------------------------------------------------------*
CLASS lcl_class_diagram DEFINITION INHERITING FROM lcl_iterator
  CREATE PROTECTED FRIENDS lif_unit_test.
  PUBLIC SECTION.
    CLASS-METHODS generate
      IMPORTING it_uml            TYPE tt_uml
                is_output_config  TYPE ts_diagram_config
                is_uml_config     TYPE ts_uml_config
      RETURNING VALUE(rv_diagram) TYPE string.
  PRIVATE SECTION.
    METHODS to_uml_text IMPORTING is_uml_config TYPE ts_uml_config
                                  is_output_config TYPE ts_diagram_config
                        RETURNING VALUE(rv_diagram) TYPE string.
ENDCLASS.                    "lcl_class_diagram DEFINITION

"! export - app
*----------------------------------------------------------------------*
*       CLASS lcl_app DEFINITION
*----------------------------------------------------------------------*
CLASS lcl_app DEFINITION CREATE PRIVATE.
  PUBLIC SECTION.
    CLASS-METHODS init.
    CLASS-METHODS user_command IMPORTING iv_ucomm       TYPE syucomm
                               RETURNING VALUE(rv_flag) TYPE flag.
ENDCLASS.                    "lcl_app DEFINITION

CLASS lcl_configuration DEFINITION CREATE PRIVATE FRIENDS lif_unit_test.
  PUBLIC SECTION.
    CONSTANTS:
      c_mode_aut TYPE char01 VALUE 'T',  " for ABAP Unit Test
      c_mode_url TYPE char01 VALUE 'U',
      c_mode_txt TYPE char01 VALUE space,
      c_mode_xmi TYPE char01 VALUE 'X',
      c_mode_exe TYPE char01 VALUE 'E'.

    CLASS-METHODS:
      get RETURNING VALUE(rs_cfg) TYPE ts_diagram_config,
      query RETURNING VALUE(rs_cfg) TYPE ts_diagram_config,
      class_constructor.
  PRIVATE SECTION.
    TYPES: BEGIN OF ts_param,
             local_path        TYPE localfile,
             java_jar          TYPE localfile,
             java_appl         TYPE localfile,
             server_url        TYPE localfile,
             output_mode       TYPE char01,
             skip_dialog       TYPE flag,
             scale             TYPE perct,
             handwritten       TYPE flag,
             shadowing         TYPE flag,
             display_source    TYPE flag,
           END OF ts_param.
    METHODS get_attributes RETURNING VALUE(rt_attr) TYPE sci_atttab.
    METHODS to_radiobutton.
    METHODS from_radiobutton.
    CLASS-DATA gs_cfg TYPE ts_param.
    DATA: mv_mode_url TYPE flag VALUE 'X',
          mv_mode_exe TYPE flag,
          mv_mode_txt TYPE flag.
    METHODS dialog.
    CLASS-METHODS get_java_path RETURNING VALUE(rv_fullpath) TYPE string.
ENDCLASS.

*--------------------------------------------------------------------------------------------------------*

CLASS lcl_parameter IMPLEMENTATION.

  METHOD constructor.
    get_user_input( iv_progname ).
  ENDMETHOD.                    "constructor

  METHOD get_user_input.
    DATA lt_parameters TYPE TABLE OF rsparams ##needed.
    DATA lt_parameters_255 TYPE TABLE OF rsparamsl_255.

    CALL FUNCTION 'RS_REFRESH_FROM_SELECTOPTIONS'
      EXPORTING
        curr_report         = iv_progname
      TABLES
        selection_table     = lt_parameters
        selection_table_255 = lt_parameters_255
      EXCEPTIONS
        not_found           = 1
        no_report           = 2
        OTHERS              = 3.

    IF sy-subrc <> 0.
      CLEAR mt_parameters.
    ELSE.
      INSERT LINES OF lt_parameters_255 INTO TABLE mt_parameters.
    ENDIF.
  ENDMETHOD.

  METHOD lif_parameter~get_scan_config.
    rs_scan = VALUE #( attributes = get_boolean( 'X_ATTR' )
                       methods = get_boolean( 'X_METH' )
                       constants = get_boolean( 'X_CONS' )
                       private_member = get_boolean( 'X_PRIV' )
                       protected_member = get_boolean( 'X_PROT' )
                       packaged_member = get_boolean( 'X_PACKS' )

                       scan_local_types = get_boolean( 'X_LOCL' )
                       scan_programs = get_boolean( 'X_PROG' )
                       scan_function_groups = get_boolean( 'X_FUGR' )
                       add_structures = get_boolean( 'X_STRUCT' )
                       scan_used_types = get_boolean( 'X_USES' ) ).
  ENDMETHOD.

  METHOD lif_parameter~get_display_config.
    rs_cfg = VALUE #( show_aggregations = get_boolean( 'X_AGGR' )
                      show_associations = get_boolean( 'X_ASOS' )
                      show_uses         = get_boolean( 'X_USES' )  " Display Dependencies
                      show_friends      = get_boolean( 'X_FRND' )  " Display Friend Relationship
                      show_throws       = get_boolean( 'X_EXCEP' ) ).
  ENDMETHOD.                    "lif_parameter~get_config

  METHOD get_boolean.
    rv_value = mt_parameters[ selname = iv_parameter_name ]-low.
  ENDMETHOD.                    "get_boolean

  METHOD get_parameter_so_tab.
    rt_so_value = VALUE #( FOR p IN mt_parameters
        WHERE ( selname EQ iv_parameter_name AND NOT ( low IS INITIAL AND high IS INITIAL ) )
                ( CORRESPONDING #( p ) ) ).
  ENDMETHOD.                    "get_parameter_so_tab

  METHOD lif_parameter~get_scan_ranges.

    ct_so_objects = get_parameter_so_tab( 'SO_ROOT' ).     " others
    ct_so_packages = get_parameter_so_tab( 'SO_DEVC' ).    " packages

    CHECK ct_so_packages IS INITIAL
      AND ct_so_objects IS NOT INITIAL.

    SELECT DISTINCT devclass as low, 'I' as sign, 'EQ' as option FROM tadir
      APPENDING CORRESPONDING FIELDS OF TABLE @ct_so_packages
      WHERE obj_name IN @ct_so_objects.
    CHECK sy-subrc NE 0.

    CLEAR ct_so_objects.
  ENDMETHOD.

ENDCLASS.                    "lcl_parameter IMPLEMENTATION

CLASS lcl_scanner IMPLEMENTATION.

  METHOD constructor.
    super->constructor( ).
    mi_parameter = ii_parameter.
    DATA(ls_scan) = mi_parameter->get_scan_config( ).
    set_scanner_configuration( scan_local_types = ls_scan-scan_local_types
                               scan_programs = ls_scan-scan_programs
                               scan_function_groups = ls_scan-scan_function_groups
                               add_structures = ls_scan-add_structures
                               scan_used_types = ls_scan-scan_used_types ).
  ENDMETHOD.

  METHOD class_scan.
    DATA lt_packages TYPE uml_range_packages.
    DATA lt_objects TYPE uml_range_types.

    rv_objects_selected = abap_false.
    cl_uml_cache=>get_singleton( )->clear_type_cache( ).

    mi_parameter->get_scan_ranges( CHANGING ct_so_packages = lt_packages
                                            ct_so_objects = lt_objects ).
    CHECK NOT ( lt_packages IS INITIAL AND lt_objects IS INITIAL ).
    rv_objects_selected = abap_true.

    execute( scan_packages = lt_packages
             scan_types = lt_objects ).
  ENDMETHOD.                    "lif_parameter~uml_class_scan

ENDCLASS.

*----------------------------------------------------------------------*
*       CLASS lcl_file IMPLEMENTATION
*----------------------------------------------------------------------*
CLASS lcl_file IMPLEMENTATION.

  METHOD download.
    rv_subrc = 1.
    CHECK io_name->dialog( ) NE cl_gui_frontend_services=>action_cancel.

    rv_subrc = cl_uml_utilities=>save_xml_local( xml = iv_data
                                                 filename = io_name->get_fullpath( ) ).
  ENDMETHOD.

ENDCLASS.                    "lcl_file IMPLEMENTATION

CLASS lcl_file_name IMPLEMENTATION.

  METHOD new.
    CASE iv_mode.
      WHEN lcl_configuration=>c_mode_aut.
        ro_file = NEW lcl_file_name_dummy( iv_mode ).
      WHEN OTHERS.
        ro_file = NEW lcl_file_name( iv_mode ).
    ENDCASE.
  ENDMETHOD.

  METHOD constructor.
    CASE iv_mode.
      WHEN lcl_configuration=>c_mode_txt.
        ms_file = VALUE #( title = |Save UML text source|
                           ext = |.txt| ).
      WHEN lcl_configuration=>c_mode_xmi.
        ms_file = VALUE #( title    = |Export XMI file|
                           ext      = |.xmi|
                           filter   = |(*.xmi)\|*.xmi| ).
      WHEN OTHERS.
        ms_file = VALUE #( title = |Save As...|
                           ext = |.txt| ).
    ENDCASE.
  ENDMETHOD.

  METHOD get_prefix.
    rv_name = shift_right( val = ms_file-name
                           places = strlen( ms_file-ext ) ).
  ENDMETHOD.

  METHOD get_fullpath.
    rv_name = ms_file-path.
  ENDMETHOD.

  METHOD dialog.
    DATA lv_path TYPE string ##needed.

    CLEAR rv_user_action.

    cl_gui_frontend_services=>file_save_dialog(
      EXPORTING
        window_title      = ms_file-title           " Window Title
        default_extension = ms_file-ext             " Default Extension
        file_filter       = ms_file-filter
      CHANGING
        filename = ms_file-name          " File Name to Save
        path = lv_path                   " Path to File
        fullpath = ms_file-path          " Path + File Name
        user_action = rv_user_action
" User Action (C Class Const ACTION_OK, ACTION_OVERWRITE etc)
*   file_encoding =
      EXCEPTIONS
        OTHERS = 0 ).
  ENDMETHOD.

ENDCLASS.

CLASS lcl_file_name_dummy IMPLEMENTATION.

  METHOD dialog.
    ms_file-path = |test.txt|.
    rv_user_action = cl_gui_frontend_services=>action_cancel.
  ENDMETHOD.

ENDCLASS.

*----------------------------------------------------------------------*
*       CLASS lcl_plant_uml IMPLEMENTATION
*----------------------------------------------------------------------*
CLASS lcl_plant_uml IMPLEMENTATION.

  METHOD constructor.
    mv_diagram = iv_diagram.
  ENDMETHOD.                    "constructor

  METHOD source.
    CLEAR rv_source.
    CHECK iv_display_source EQ abap_true.
    rv_source = |<p>{ mv_diagram }</p>|.
  ENDMETHOD.

  METHOD show_html.
    cl_abap_browser=>show_html( html_string = iv_html
                                size = cl_abap_browser=>xlarge
                                context_menu = abap_true ).
  ENDMETHOD.

  METHOD output.
    CASE is_cfg-output_mode.
      WHEN lcl_configuration=>c_mode_url.
        show_html( |<img src="{ to_url( ) }"/>\n{ source( is_cfg-display_source ) }| ).

      WHEN lcl_configuration=>c_mode_exe.
        DATA(lo_name) = lcl_file_name=>new( lcl_file=>c_mode_txt ).
        IF lcl_file=>download( iv_data = to_xstring( mv_diagram )
                               io_name = lo_name ) IS INITIAL.
          show_html( |<img src="{ to_png( io_name = lo_name
                                          is_cfg = is_cfg ) }"/>\n{ source( is_cfg-display_source ) }| ).
        ENDIF.

      WHEN OTHERS.
*       export data as PlantUML source
        lcl_file=>download( io_name = lcl_file_name=>new( is_cfg-output_mode )
                            iv_data = to_xstring( mv_diagram ) ).
    ENDCASE.
  ENDMETHOD.                    "output

  METHOD to_url.
    DATA lv_bin TYPE xstring.
*   for PlantUML Server: Convert to UTF-8, then deflate, then encode (base64 variant)
    cl_abap_gzip=>compress_binary(
      EXPORTING
        raw_in         = to_xstring( mv_diagram )   " UTF-8
        compress_level = 9
      IMPORTING
        gzip_out       = lv_bin ).

    rv_url = iv_base_url &&
             translate( val = cl_http_utility=>encode_x_base64( lv_bin )
                        from = c_standard
                        to =   c_plantuml ).
  ENDMETHOD.                    "to_url

  METHOD to_xstring.
    cl_abap_conv_out_ce=>create( encoding = 'UTF-8' )->convert( EXPORTING data = iv_string
                                                                IMPORTING buffer = rv_xstring ).
  ENDMETHOD.                    "to_xstring

  METHOD parameter_string.
    rv_param = |-jar { is_cfg-java_jar } -o { is_cfg-local_path } "{ io_name->get_fullpath( ) }"|.
  ENDMETHOD.

  METHOD png_file_name.
    TRY.
        rv_name = |{ is_cfg-local_path }{ io_name->get_prefix( ) }.png|.
      CATCH cx_dynamic_check.
        CLEAR rv_name.
    ENDTRY.
  ENDMETHOD.

  METHOD to_png.
    CLEAR rv_name.
    cl_gui_frontend_services=>execute(
      EXPORTING application = is_cfg-java_appl
                parameter = parameter_string( io_name = io_name
                                              is_cfg = is_cfg )
                synchronous = 'X'
      EXCEPTIONS OTHERS = 1 ).
    IF sy-subrc EQ 0.
      rv_name = png_file_name( io_name = io_name
                               is_cfg = is_cfg ).
    ENDIF.
  ENDMETHOD.

ENDCLASS.                    "lcl_plant_uml IMPLEMENTATION

*----------------------------------------------------------------------*
*       CLASS lcl_uml_class IMPLEMENTATION
*----------------------------------------------------------------------*
CLASS lcl_uml_class IMPLEMENTATION.

  METHOD escape.
    IF iv_name CA '/'.
      rv_name = |"{ iv_name }"|.
    ELSE.
      rv_name = iv_name.
    ENDIF.
  ENDMETHOD.

  METHOD constructor.
    super->constructor( ).
    mo_uml = io_uml.
    ms_config = is_uml_config.
  ENDMETHOD.                    "constructor

  METHOD to_uml_text.
    CHECK is_uml-kind CA 'OFP'.
    set_data( is_uml ).
    plant_uml_class( ).
  ENDMETHOD.                    "to_uml_text

  METHOD set_data.
    ms_uml = is_uml.
    mv_name = get_name( is_uml-name ).
  ENDMETHOD.                    "set_data

  METHOD begin_class.
    mo_uml->add( |{ get_class( ) } \{\n| ).
  ENDMETHOD.                    "begin_class

  METHOD end_class.
    mo_uml->add( |\}\n| ).
  ENDMETHOD.                    "end_class

  METHOD class_member.
    mo_uml->add( |\{static\}| ).
  ENDMETHOD.                    "class_member

  METHOD plant_uml_class.
    CHECK mv_name IS NOT INITIAL.
    begin_class( ).
    uml_fields( ).
    uml_methods( ).
    end_class( ).

    IF ms_uml-supertype IS NOT INITIAL.
      mo_uml->add( |{ escape( get_name( ms_uml-supertype ) ) } <\|-- { escape( mv_name ) }\n| ).
    ENDIF.

    uml_reduce( it_data = ms_uml-t_implementations
                iv_sep = '-->' ).
    uml_reduce( it_data = ms_uml-t_user
                iv_sep = '..o'
                iv_active = ms_config-show_uses ).
    uml_reduce( it_data = ms_uml-t_exceptions
                iv_sep = '..'
                iv_active = ms_config-show_throws ).
    uml_reduce( it_data = ms_uml-t_friends
                iv_sep = '..>'
                iv_suffix = | : friend |
                iv_active = ms_config-show_friends ).
  ENDMETHOD.                    "plant_uml_class

  METHOD get_name.
"    CHECK substring_before( val = iv_name regex = '\\TYPE=' ) EQ space.
    rv_name = substring_after( val = iv_name regex = '\\(CLASS|INTERFACE)=' ).
    CHECK rv_name IS INITIAL.
    rv_name = substring_after( val = iv_name regex = '\\FUGR=' ).
    CHECK rv_name IS NOT INITIAL.
    rv_name = rv_name && | <<FUGR>>| .
  ENDMETHOD.                    "get_name

  METHOD get_container.
    rv_name = substring_after( val = ms_uml-container regex = '\\(CLASS-POOL|PROGRAM|FUNCTION-POOL)=' ).
  ENDMETHOD.                    "get_container

  METHOD get_class.
    DATA lv_prefix TYPE string.
    IF find( val = ms_uml-name sub = '\INTERFACE=' ) GE 0.
      lv_prefix = |interface|.
    ELSEIF ms_uml-is_abstract IS NOT INITIAL.
      lv_prefix = |abstract class|.
    ELSE.
      lv_prefix = |class|.
    ENDIF.
    rv_class = |{ lv_prefix } { mv_name }|.
    IF ms_uml-container IS NOT INITIAL AND ms_uml-is_local EQ abap_true.
      rv_class = |{ lv_prefix } "{ mv_name }\\n({ get_container( ) })" as { mv_name }|.
    ENDIF.
  ENDMETHOD.                    "get_class

  METHOD visibility.
    CONSTANTS:
      c_vis_package   TYPE char01 VALUE 'I',
      c_vis_private   TYPE char01 VALUE 'P',
      c_vis_protected TYPE char01 VALUE 'O',
      c_vis_public    TYPE char01 VALUE 'U'.

    DATA lv_vis TYPE char01.

    CASE iv_visibility.
      WHEN c_vis_private. lv_vis = '-'.
      WHEN c_vis_protected. lv_vis = '#'.
      WHEN c_vis_package. lv_vis = '~'.
      WHEN c_vis_public. lv_vis = '+'.
    ENDCASE.
    mo_uml->add( |{ lv_vis }| ).
  ENDMETHOD.                    "visibility

  METHOD uml_add.
    IF iv_abstract EQ abap_true.
      mo_uml->add( |\{abstract\}| ).
    ENDIF.
    visibility( iv_visibility ).
    IF iv_class EQ abap_true.
      class_member( ).
    ENDIF.
    mo_uml->add( |{ iv_name }\n| ).
  ENDMETHOD.                    "uml_add

  METHOD uml_reduce.
    FIELD-SYMBOLS <lv_line> TYPE csequence.

    CHECK iv_active EQ abap_true.
    LOOP AT it_data ASSIGNING <lv_line>.
      mo_uml->add( |{ escape( mv_name ) } { iv_sep } { escape( get_name( <lv_line> ) ) }{ iv_suffix }\n| ).
    ENDLOOP.
  ENDMETHOD.                    "uml_reduce

  METHOD uml_events.
*      mo_uml->add( |{ mv_name } ..o { get_name( ls_event-name ) }\n| ).
    LOOP AT ms_uml-t_events INTO DATA(ls_event).
      uml_add( iv_visibility = ls_event-visibility
               iv_class = ls_event-is_static
               iv_name = |{ ls_event-name }( )| ).
    ENDLOOP.
  ENDMETHOD.                    "uml_events

  METHOD uml_fields.
    LOOP AT ms_uml-t_attributes INTO DATA(ls_field) WHERE is_constant EQ space.
      uml_add( iv_visibility = ls_field-visibility
               iv_class = ls_field-is_class
               iv_name = ls_field-name ).
    ENDLOOP.
  ENDMETHOD.                    "uml_fields

  METHOD uml_methods.
    LOOP AT ms_uml-t_methods INTO DATA(ls_method).
      uml_add( iv_abstract = ls_method-is_abstract
               iv_visibility = ls_method-visibility
               iv_class = ls_method-is_class
               iv_name = |{ ls_method-name }( )| ).
    ENDLOOP.
  ENDMETHOD.                    "uml_methods

ENDCLASS.                    "lcl_uml_class IMPLEMENTATION

*----------------------------------------------------------------------*
*       CLASS lcl_class_diagram IMPLEMENTATION
*----------------------------------------------------------------------*
CLASS lcl_class_diagram IMPLEMENTATION.

  METHOD generate.
    rv_diagram = NEW lcl_class_diagram( it_uml )->to_uml_text( is_output_config = is_output_config
                                                               is_uml_config = is_uml_config ).
  ENDMETHOD.                    "generate

  METHOD to_uml_text.
    DATA(lo_uml) = lcl_uml=>new( is_output_config ).
    DATA(lo_class) = NEW lcl_uml_class( io_uml        = lo_uml
                                        is_uml_config = is_uml_config ).
    WHILE has_next( ).
      lo_class->to_uml_text( next( ) ).
    ENDWHILE.

    rv_diagram = lo_uml->get( ).
  ENDMETHOD.                    "to_uml_text

ENDCLASS.                    "lcl_class_diagram IMPLEMENTATION

*----------------------------------------------------------------------*
*       CLASS lcl_uml IMPLEMENTATION
*----------------------------------------------------------------------*
CLASS lcl_uml IMPLEMENTATION.

  METHOD new.
    ro_uml = NEW #( ).
    ro_uml->header( is_cfg ).
  ENDMETHOD.                    "new

  METHOD add.
    mv_diagram = mv_diagram && iv_code.
  ENDMETHOD.                    "add

  METHOD get.
    footer( ).
    rv_diagram = mv_diagram.
  ENDMETHOD.                    "get

  METHOD header.
    add( |@startuml\n| ).   " Header
    add( |scale { is_cfg-scale }\n| ).   " Reduce the size of the output image
  ENDMETHOD.                    "header

  METHOD footer.
    add( |hide <<FUGR>> circle\n| ).
    add( |@enduml\n| ).
  ENDMETHOD.                    "footer

ENDCLASS.                    "lcl_uml IMPLEMENTATION

*----------------------------------------------------------------------*
*       CLASS lcl_iterator IMPLEMENTATION
*----------------------------------------------------------------------*
CLASS lcl_iterator IMPLEMENTATION.

  METHOD constructor.
    CLEAR mv_idx.
    mt_data = it_data.    " never changed
    IF iv_stop IS INITIAL.
      mv_size = lines( mt_data ).
    ELSE.
      mv_size = iv_stop.
    ENDIF.
    skip( iv_start - 1 ).
  ENDMETHOD.                    "constructor

  METHOD next.
    skip( ).
    rs_uml = mt_data[ mv_idx ].
  ENDMETHOD.                    "next

  METHOD has_next.
    rv_flag = xsdbool( mv_idx < mv_size ).
  ENDMETHOD.                    "has_next

  METHOD skip.
    ADD iv_count TO mv_idx.
  ENDMETHOD.                    "skip

ENDCLASS.                    "lcl_iterator IMPLEMENTATION

*----------------------------------------------------------------------*
*       CLASS lcl_xmi_output IMPLEMENTATION
*----------------------------------------------------------------------*
CLASS lcl_xmi_output IMPLEMENTATION.

  METHOD new_xmi.
    ro_xmi = NEW cl_uml_class_decor_xmi( io_scanner ).

    DATA(ls_scan) = ii_parameter->get_scan_config( ).
    ro_xmi->set_decorator_configuration( attributes = ls_scan-attributes
                                         methods = ls_scan-methods
                                         constants = ls_scan-constants
                                         private_member = ls_scan-private_member
                                         protected_member = ls_scan-protected_member
                                         packaged_member = ls_scan-packaged_member ).
    DATA(ls_cfg) = ii_parameter->get_display_config( ).
    ro_xmi->set_xmi_configuration( aggregations    = ls_cfg-show_aggregations
                                   associations    = ls_cfg-show_associations
                                   uses            = ls_cfg-show_uses
                                   friends         = ls_cfg-show_friends
                                   show_exceptions = ls_cfg-show_throws ).
  ENDMETHOD.                    "lif_parameter~new_xmi

  METHOD lif_output~output.
    DATA lr_data TYPE REF TO data.
    FIELD-SYMBOLS <lv_data> TYPE xstring.

    rv_flag = abap_false.
    DATA(lo_scanner) = NEW lcl_scanner( ii_parameter ).

    CHECK lo_scanner->class_scan( ).
    new_xmi( io_scanner = lo_scanner
             ii_parameter = ii_parameter )->get_diagram( CHANGING c_data = lr_data ).
    ASSIGN lr_data->* TO <lv_data> CASTING.
    CHECK <lv_data> IS ASSIGNED.

    lcl_file=>download( iv_data = <lv_data>
                        io_name = lcl_file_name=>new( lcl_file=>c_mode_xmi ) ).
    rv_flag = abap_true.
  ENDMETHOD.                    "lif_output~output

ENDCLASS.                    "lcl_xmi_output IMPLEMENTATION

*----------------------------------------------------------------------*
*       CLASS lcl_plantuml_output IMPLEMENTATION
*----------------------------------------------------------------------*
CLASS lcl_plantuml_output IMPLEMENTATION.

  METHOD lif_output~output.
    DATA lr_data TYPE REF TO data.
    DATA lr_uml_tab TYPE REF TO cl_uml_class_scanner=>uml_tab.

    rv_flag = abap_false.
    DATA(lo_scanner) = NEW lcl_scanner( ii_parameter ).

    CHECK lo_scanner->class_scan( ).
    "DATA lv_kind TYPE c VALUE cl_uml_class_scanner=>c_kind_uml_tab.
    lo_scanner->get_diagram( "IMPORTING e_kind = lv_kind
                             CHANGING c_data = lr_data ).
    TRY.
        lr_uml_tab ?= lr_data.
        CHECK lr_uml_tab IS BOUND.
        DATA(ls_cfg) = lcl_configuration=>get( ).
        NEW lcl_plant_uml( lcl_class_diagram=>generate( it_uml = lr_uml_tab->*
                                                        is_uml_config = ii_parameter->get_display_config( )
                                                        is_output_config = ls_cfg )
                                                         )->output( ls_cfg ).
        rv_flag = abap_true.
      CATCH cx_dynamic_check.                           "#EC NO_HANDLER
    ENDTRY.
  ENDMETHOD.                    "lif_output~output

ENDCLASS.                    "lcl_plantuml_output IMPLEMENTATION

CLASS lcl_null_output IMPLEMENTATION.

  METHOD lif_output~output.
    rv_flag = abap_false.
  ENDMETHOD.

ENDCLASS.

CLASS lcl_configuration IMPLEMENTATION.

  METHOD class_constructor.
    gs_cfg = VALUE #( java_appl = get_java_path( )
*                     PlantUML jar file and output path
                      local_path = `C:\Temp\Dokumente\UML\`
                      java_jar = `C:\Temp\Dokumente\UML\plantuml.jar`
*                     PlantUML server URL
                      server_url = `http://www.plantuml.com/plantuml/img/` ##NO_TEXT
                      output_mode = c_mode_url
                      skip_dialog = space
                      scale = c_default_scale
                      handwritten = abap_false
                      shadowing = abap_false
                      display_source = abap_true ).
*   Windows: Local Java installation
    IF gs_cfg-java_appl IS INITIAL.
      gs_cfg-java_appl = `C:\Windows\System32\java`.
    ENDIF.
  ENDMETHOD.

  METHOD get_java_path.
    CONSTANTS c_registry_java_base_key TYPE string
      VALUE 'SOFTWARE\JavaSoft\Java Runtime Environment'  ##NO_TEXT.
    DATA lv_path TYPE string.

    CLEAR rv_fullpath.
    cl_gui_frontend_services=>registry_get_value(
      EXPORTING
        root = cl_gui_frontend_services=>hkey_local_machine
        key = c_registry_java_base_key
        value = 'CurrentVersion'
      IMPORTING
        reg_value = lv_path
      EXCEPTIONS
        OTHERS               = 5 ).
    CHECK sy-subrc EQ 0.
    cl_gui_frontend_services=>registry_get_value(
      EXPORTING
        root = cl_gui_frontend_services=>hkey_local_machine
        key = |{ c_registry_java_base_key }\\{ lv_path }|
        value = 'JavaHome'
      IMPORTING
        reg_value = lv_path
      EXCEPTIONS
        OTHERS               = 5 ).
    CHECK sy-subrc EQ 0.
    rv_fullpath = |{ lv_path }\\bin\\java|.
  ENDMETHOD.

  METHOD get_attributes.
    DEFINE fill_att.
      INSERT VALUE #( ref = REF #( &1 )
                      text = &2
                      kind = &3 ) INTO TABLE rt_attr.
    END-OF-DEFINITION.
    DEFINE fill_radio.
      INSERT VALUE #( ref = REF #( &1 )
                      text = &2
                      kind = 'R'
                      button_group = &3 ) INTO TABLE rt_attr.
    END-OF-DEFINITION.
* Table Type has type 'T' - patterns SCI_PATTERN
*                     ' ' - ?? private attributes?
*                     'I' - ?? Integer?
    fill_att   gs_cfg-skip_dialog 'Remember my settings'(c00)     'C'.

    fill_att:   sy-index      'PlantUML Execution Mode'(c10) 'G'.   " Group
    fill_radio: mv_mode_url   'PlantUML web service'(c11)  'MOD',
                mv_mode_txt   'Save text file'(c12)        'MOD',
                mv_mode_exe   'Local PlantUML '(c13)       'MOD'.

    fill_att: ''              'PlantUML Settings'(c20)         'G',
              gs_cfg-scale       'Scale '(c21)                 'S'.
    fill_att: gs_cfg-server_url  'PlantUML Server'(c25)         'S',
              gs_cfg-local_path  'Local PlantUML path'(c26)     'S',
              gs_cfg-java_jar    'Local PlantUML jar file'(c27) ' ',
              gs_cfg-java_appl   'Local Java path'(c28)         'S'.  " Select-Options
    fill_att: gs_cfg-handwritten       'Handwritten '(c30)           'C',
              gs_cfg-shadowing         'Shadowing '(c31)             'C',
              gs_cfg-display_source    'Display source '(c32)        'C'.
  ENDMETHOD.

  METHOD to_radiobutton.
    mv_mode_url = xsdbool( gs_cfg-output_mode EQ c_mode_url ).
    mv_mode_exe = xsdbool( gs_cfg-output_mode EQ c_mode_exe ).
    mv_mode_txt = xsdbool( gs_cfg-output_mode EQ c_mode_txt ).
  ENDMETHOD.

  METHOD from_radiobutton.
    IF mv_mode_url EQ abap_true.
      gs_cfg-output_mode = c_mode_url.
    ELSEIF mv_mode_exe EQ abap_true.
      gs_cfg-output_mode = c_mode_exe.
    ELSEIF mv_mode_txt EQ abap_true.
      gs_cfg-output_mode = c_mode_txt.
    ENDIF.
  ENDMETHOD.

  METHOD get.
    rs_cfg = CORRESPONDING #( gs_cfg ).
  ENDMETHOD.

  METHOD query.
    DATA(lo_config) = NEW lcl_configuration( ).
    lo_config->dialog( ).
    rs_cfg = lo_config->get( ).
  ENDMETHOD.

  METHOD dialog.
    to_radiobutton( ).
    CHECK gs_cfg-skip_dialog EQ abap_false.
    CHECK cl_ci_query_attributes=>generic(
        p_name       = CONV #( sy-repid )                    " unique screen ID
        p_title      = 'Class Diagram Parameters'            " Screen title
        p_attributes = get_attributes( )                     " Screen fields
        p_display    = abap_false                            " Edit / Display only
       ) EQ abap_false.   " Do not cancel
    from_radiobutton( ).
  ENDMETHOD.

ENDCLASS.

*----------------------------------------------------------------------*
*       CLASS lcl_app IMPLEMENTATION
*----------------------------------------------------------------------*
CLASS lcl_app IMPLEMENTATION.

  METHOD init.
    button1 = text-007.
    button2 = text-008.
    button3 = text-009.
*   pushbutton in the application toolbar
    sscrfields-functxt_01 = VALUE smp_dyntxt( icon_id = '@4Y@'        " ICON_BUSINAV_ENTITY
                                              icon_text = text-p01    " PlantUML
                                              quickinfo = text-p01
                                              text = text-p01 ).
    sscrfields-functxt_02 = VALUE smp_dyntxt( icon_id = '@BX@'        " ICON_CONFIGURATION
                                              icon_text = text-p02    " PlantUML Configuration
                                              quickinfo = text-p02
                                              text = text-p02 ).
  ENDMETHOD.

  METHOD user_command.
    DATA li_export TYPE REF TO lif_output.

    CASE iv_ucomm.
      WHEN 'FC01'.
*       PlantUML Output
        li_export = NEW lcl_plantuml_output( ).
      WHEN 'ONLI'.
        li_export = NEW lcl_xmi_output( ).
      WHEN 'FC02'.
        lcl_configuration=>query( ).
        rv_flag = abap_true.
        RETURN.
      WHEN OTHERS.
        li_export = NEW lcl_null_output( ).
    ENDCASE.

    rv_flag = li_export->output( NEW lcl_parameter( iv_progname = sy-repid ) ).
  ENDMETHOD.                    "execute

ENDCLASS.                    "lcl_app IMPLEMENTATION

" ----------------------------------------------------------------------------------------------------------------------------------- *
" program events

INITIALIZATION.
  lcl_app=>init( ).

AT SELECTION-SCREEN.
  IF lcl_app=>user_command( sscrfields-ucomm ).
    CLEAR sscrfields-ucomm.
  ENDIF.

START-OF-SELECTION.
  lcl_app=>user_command( sy-ucomm ).

Assigned Tags

      15 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Paul Hardy
      Paul Hardy

      I have always been told, since day one of dealing with OO programming that you START with a UML diagram and then write your code. Some would say that generating a UML diagram when you have finished is the 100% wrong time to do it...

       

      In languages like Java (and many others) you can auto-generate a code skeleton for classes and  the like from a UL diagram in Eclipse (for example).

       

      Several attempts have been made at this in ABAP (generating ABAP classes etc..) but none really got anywhere

       

      There just does not seem to be a demand....

      Author's profile photo Jacques Nomssi Nzali
      Jacques Nomssi Nzali
      Blog Post Author

      Hello Paul,

       

       

      the journey starts with an idea, a concept. The diagram is (just) a useful notation of the concept.

      We have to be aware of the essential concepts a diagram type can communicate well. Consider e.g. the concept Redirected Recursion described by Jason Mc. Smith in his book Elemental Design Patterns. The intent is

      • To perform a singular action on multiple objects and have the objects be responsible for distribution and invocation of that action.

      How do you visualize this? The author creates a structure diagram with the participants in this concept (Recursor, redirectTarget, operation) using a new notation called PIN to communicate the concept.

      You can create a diagram at any time, i.e.g. before, while of after writing the code with the intent of communicating a concept with your peers. This is modelling for discussion.

      An architect will create early drafts of a building, but we also expect a detailed, accurate blueprint after the house is completed. This is documentation for knowledge transfer.

      I think generated diagrams can help us recognize concepts in code. In the case of PlantUML, because the diagram source text is available, it can be used as starting point to create the documetation.

      JNN

       

      Author's profile photo David Kunz
      David Kunz

      Hi!

      Have a look at https://www.structurizr.com/ ?

      Author's profile photo Rainer Winkler
      Rainer Winkler

      Hello Jacques,

      thank you very much. You made an awesome program! I just tried it:

      I agree with you. We will see more diagrams as the costs to generate and maintain them are reduced.

      I would be happy to mention and show your tool when I speak 10 June 2017 at the SAP Inside Track in Hamburg about "Do Visualizations help during development?" Is this OK for you?

      Cheers,
      Rainer

      Author's profile photo Jacques Nomssi Nzali
      Jacques Nomssi Nzali
      Blog Post Author

      Hello Rainer,

      I am happy you took the time to try out the code, feel free to share or/and demo the tool (for the record, it started here). 

      From your diagram, your code structure is meaningful because the design is sound. To get there, I proposed a process in this blog.

      Thanks a lot for your feedback. Keep the Joy of Coding.

      best regards,

      JNN

      Author's profile photo Rainer Winkler
      Rainer Winkler

      Hello Jacques,

      I just enhanced my session proposal for the SAP Inside Track in Hamburg 2017 (https://wiki.scn.sap.com/wiki/display/events/SAP+Inside+Track+Hamburg+2017+-+%23SITHH). I will be happy to show the extractors to class and sequence diagrams.

      Thank you,

      Rainer

      Author's profile photo Former Member
      Former Member

      I have a Problem with the code. In thies lines eG.:


      SPAN {
      font-family: "Courier New";
      font-size: 10pt;
      color: #000000;
      background: #FFFFFF;
      }
      .L0S33 {
      color: #4DA619;
      }
      .L0S52 {
      color: #0000FF;
      }
      .L0S55 {
      color: #800080;
      }
      rs_scan VALUE #   attributes get_boolean'X_ATTR' )

      I think i must Change the code because there is an Display mistake ...

      How is it right, ore can I find on another place the right code to test it for me.

      Author's profile photo Jacques Nomssi Nzali
      Jacques Nomssi Nzali
      Blog Post Author

      Hello Jürgen,

       

      if the current code won't work, extract an older version of the code from this link: https://wiki.scn.sap.com/wiki/pages/viewpage.action?pageId=464687275

      Make sure to scroll down and start at REPORT ZZ_UML_CLASS_EXPORT.

       

      hope this helps,

       

      JNN

      Author's profile photo Juan Lee
      Juan Lee

      Jacques,

      Your link doesn't work.  Can you please update link ?

      I am looking for  a version that works  for ABAP 7.30 or below. The program posted on this site only works for ABAP 7.40 or above.

      thanks

      Author's profile photo Jacques Nomssi Nzali
      Jacques Nomssi Nzali
      Blog Post Author

      Check

      https://github.com/nomssi/ABAP-to-PlantUML/tree/master/class%20diagram/702

      Author's profile photo Juan Lee
      Juan Lee

      Jacques.

      I am not able to run the plantuml online tool for large file.  I am assuming this is a limitation of the website?I am getting request error return.

      For XMI file, what simple tools/program can I download to convert to UML? Does programs like starUML or plantUML allows you import XMI file?

      thanks

       

      Author's profile photo Jacques Nomssi Nzali
      Jacques Nomssi Nzali
      Blog Post Author

      Hello Juan,

      I have uploaded a new 7.02 down port, please try it and adjust the parameters H-Pages/V-Pages for multiple output of larger diagrams.

      Also try to restrict the selected objects as much as possible.

      JNN

      Author's profile photo Patrick Van Nierop
      Patrick Van Nierop

      Thanks for sharing this blog!

      And fyi:

      • you can add PlantUML to eclipse
      • there is a function in SAP already to generate an UML (based on all the classes of a package for example).. I've used it only a few times for documentation purposes and if I remember well I ended up exporting the result to StarUML before creating the final document(s)

       

       

      Author's profile photo Loed Despuig
      Loed Despuig

      Hi Jacques Nomssi

      Is your program going to generate the code that will be used in PlantUML?

      http://www.plantuml.com/plantuml/umla/SoWkIImgAStDuNBAJrBGjLDmpCbCJbMmKiX8pSd9vt98pKi1IW80

      Or what will I do with the generated UML using your program? Can I still use it in PlantUML so I can have the GRAPHICAL VIEW of the program?

      Thanks.

      Loed

      Author's profile photo Jacques Nomssi Nzali
      Jacques Nomssi Nzali
      Blog Post Author

      Hello Loed,

       

      the program generates the URL and starts an HTML-Viewer where the image/graphics is displayed.

      Go to https://github.com/nomssi/ABAP-to-PlantUML and check the forks. Some can be installed directly using abapGit.

       

      best regards

      JNN