custom expression types and action types · 2019. 11. 12. · custom expression types and action...
TRANSCRIPT
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
1
Custom Expression Types and Action Types A Step-by-Step Guide with Example
CUSTOMER
Document Version: 1.0 - 15 October 2013
SAP NetWeaver Business Decision Service Management and
SAP NetWeaver Business Rule Framework plus
© 2013 SAP AG or an SAP affiliate company. All rights reserved.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
2
About The Authors
Christian Auth is a senior developer in the SAP NetWeaver Business Rule Framework plus project.
He joined SAP in 2002. Before his assignment in BRFplus, he worked in various development groups in SAP Business ByDesign.
Michael Baer is a senior developer in the SAP NetWeaver Business Rule Framework plus project.
Michael joined SAP in 2008. In BRFplus, he has been working on various back-end topics.
Piyush Deora is a senior developer in SAP Netweaver Business Rule Framework plus project.
Piyush joined SAP in 2008. He has been working on various user interface and third party integration topics.
Hanno Holzheuser is a knowledge architect, currently supporting the SAP Netweaver Business Rule Framework plus team.
Hanno joined SAP in 1990 and has worked for many units in SAP NetWeaver, SAP Business Suite, and SAP Cloud managing KM teams and projects.
Marco Wuest is a senior developer in the BRFplus team. Having mainly worked on UI topics, he created the architecture of the UI layer and implemented the UI infrastructure.
Marco joined SAP in 2003. He previously worked in the Records and Case Management project.
Carsten Ziegler is the chief product owner of SAP NetWeaver Decision Service Management and SAP NetWeaver Business Rule Framework plus.
He joined SAP in 2000. Since then he has been working in various projects as a developer, development architect, project lead and product owner.
2013-07-29
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
3
Table of Contents
1 Introduction ........................................................................................................................................... 6
1.1 Overview ................................................................................................................................................. 6
1.2 Expression Types and Action Types ...................................................................................................... 6
2 Specification/Design .......................................................................................................................... 10
3 Database Layout ................................................................................................................................. 12
4 Transport Objects ............................................................................................................................... 17
5 Interface Creation ............................................................................................................................... 21
6 Class Creation ..................................................................................................................................... 26
7 New Methods ....................................................................................................................................... 30
7.1 LOAD_BUFFER_DB ............................................................................................................................. 30
7.2 LOAD_BUFFER .................................................................................................................................... 32
7.3 SET_BUFFER ....................................................................................................................................... 33
7.4 SAVE_BUFFER_DB ............................................................................................................................. 33
7.5 Method in Action Type or Expression Type Specific Interfaces ........................................................... 34
7.5.1 IF_FDT_ACTN_EMAIL=>GET_BODY ............................................................................................ 34
7.5.2 IF_FDT_ACTN_EMAIL=>GET_RECIPIENTS ................................................................................. 35
7.5.3 IF_FDT_ACTN_EMAIL=>GET_PARAMETERS ............................................................................. 35
7.5.4 IF_FDT_ACTN_EMAIL=>GET_SUBJECT ...................................................................................... 35
7.5.5 IF_FDT_ACTN_EMAIL=>SET_BODY ............................................................................................ 35
7.5.6 IF_FDT_ACTN_EMAIL=>SET_RECIPIENTS ................................................................................. 36
7.5.7 IF_FDT_ACTN_EMAIL=>SET_PARAMETERS .............................................................................. 36
7.5.8 IF_FDT_ACTN_EMAIL=>SET_SUBJECT ...................................................................................... 37
8 Redefined Methods ............................................................................................................................. 39
8.1 Class CL_FDT_MAINTENANCE .......................................................................................................... 39
8.1.1 DELETE_FROM_DB ....................................................................................................................... 39
8.1.2 CHECK ............................................................................................................................................ 39
8.1.3 COPY ............................................................................................................................................... 39
8.1.4 RESTORE_BUFFER ....................................................................................................................... 40
8.1.5 SAVE_WITH_VERSION .................................................................................................................. 40
8.1.6 TRANSPORT ................................................................................................................................... 41
8.1.7 GET_CHANGE_DETAILS ............................................................................................................... 42
8.2 Interface IF_FDT_EXPRESSION ......................................................................................................... 43
8.2.1 GET_USED_EXPRESSIONS.......................................................................................................... 44
8.2.2 GET_DSM_AVAILABILITY .............................................................................................................. 45
8.2.3 GET_CONTEXT_DATA_OBJECTS ................................................................................................ 45
8.3 Interface IF_FDT_ADMIN_DATA ......................................................................................................... 46
8.3.1 TO_STRING .................................................................................................................................... 46
8.4 Interface IF_FDT_DATA_EXCHANGE_INTERNAL ............................................................................. 47
9 Check Methods ................................................................................................................................... 48
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
4
10 Process Methods ................................................................................................................................ 50
11 Methods for Code Generation ........................................................................................................... 54
11.1 IS_GENERATING ............................................................................................................................ 54
11.2 GENERATE_PROCESS ................................................................................................................. 54
11.2.1 IF_FDT_DATA_OBJECT->GENERATE_CREATE_DATA_REF .................................................... 55
11.2.2 IF_FDT_DATA_OBJECT=>GENERATE_CONVERT_TO .............................................................. 55
11.2.3 CL_FDT_EXPR_SERVICES=> GENERATE_MOVE_FROM_EXTERNAL ................................... 56
11.2.4 CL_FDT_EXPR_SERVICES=> GENERATE_MOVE_TO_EXTERNAL ......................................... 57
11.2.5 CL_FDT_EXPR_SERVICES=> GENERATE_GET_CONTEXT_NAME ......................................... 58
11.2.6 CL_FDT_EXPR_SERVICES=> GEN_GET_NEXT_VARIABLE_NAME ......................................... 58
11.2.7 CL_FDT_EXPR_SERVICES=> GEN_GET_NAME_FOR_GENERATION .................................... 59
11.2.8 CL_FDT_EXPR_SERVICES=> GEN_GENERATE_TRACE .......................................................... 59
11.2.9 CL_FDT_EXPR_SERVICES=>GENERATE_RANGE_PROCESS ................................................ 60
11.2.10 CL_FDT_EXPR_SERVICES=> GENERATE_SIMPLE_VALUE_PROCESS ................................. 60
11.2.11 CL_FDT_EXPRESSION=>GENERATE_GET_VALUE .................................................................. 61
11.2.12 CL_FDT_EXPRESSION=> GENERATE_TRACE_SUB_EXPRESSION ....................................... 62
11.3 Tips for Generation .......................................................................................................................... 63
11.4 Tools for supporting the generation development and maintenance .............................................. 63
12 Methods for Lean Trace Display ....................................................................................................... 64
12.1 CHANGE_TRACE_NODE ............................................................................................................... 64
12.2 CREATE_TRACE_CHILD_NODES ................................................................................................ 65
13 Data Exchange .................................................................................................................................... 67
13.1 EXPORT_XML ................................................................................................................................. 67
13.2 IMPORT_XML ................................................................................................................................. 68
13.3 Implementation for User Defined Expression Types ....................................................................... 70
13.3.1 GET_DTD ........................................................................................................................................ 70
13.3.2 GET_ELEMENT_DOMAIN_LIST .................................................................................................... 75
13.3.3 GET_VERSION_CHANGE_LIST .................................................................................................... 76
13.3.4 MODIFY_XML_VERSION_CHANGES ........................................................................................... 76
14 Creation of the Action Type or Expression Type as an Object in BRFplus ................................. 78
15 Testing the Action Type or Expression Type .................................................................................. 80
16 Creation of the User Interface ........................................................................................................... 81
16.1 Overview about the Components of the User Interface .................................................................. 81
16.2 Web Dynpro Component ................................................................................................................. 82
16.2.1 Implementation of the BRFplus WD Interfaces ............................................................................... 82
16.2.2 Definition of the Context .................................................................................................................. 82
16.2.3 Exception Handling in the UI ........................................................................................................... 83
16.2.4 Implementation of Interface FDT_IWD_OBJECT/FDT_IWD_COMPONENT ................................. 85
16.3 Model Class ..................................................................................................................................... 90
16.3.1 Implementation of Model Class ....................................................................................................... 90
16.3.2 Detecting if the object has changed ................................................................................................ 96
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
5
16.3.3 Reference ........................................................................................................................................ 96
16.4 UI Class ........................................................................................................................................... 98
16.4.1 HAS_WD_ABAP_UI ........................................................................................................................ 99
16.4.2 GET_WD_ABAP_MODEL_CLASS ................................................................................................. 99
16.4.3 GET_WD_ABAP_COMPONENT .................................................................................................... 99
16.4.4 GET_WD_ABAP_VERSION .......................................................................................................... 100
16.5 Reuse Components and Service in the UI Layer .......................................................................... 100
16.5.1 Creating a new BRFplus Object .................................................................................................... 100
16.5.2 Selecting an Existing BRFplus Object ........................................................................................... 102
16.5.3 Navigating to Other BRFplus Objects ............................................................................................ 104
16.5.4 Object Menu .................................................................................................................................. 104
16.5.5 Other Services ............................................................................................................................... 108
16.6 Testing ........................................................................................................................................... 108
17 Related Content ................................................................................................................................ 110
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
6
1 Introduction
1.1 Overview
Purpose
This guide applies to SAP NetWeaver Decision Service Management (any release) or SAP NetWeaver Business Rule Framework plus (BRFplus) shipped with SAP NetWeaver 7.0 Enhancement Package 3 or SAP NetWeaver 7.3 Enhancement Package 1 or higher. This is a technical document for developers and consultants who seek to enhance the capabilities of BRFplus.
Abstract
The guide uses an example of an action type for sending an email to illustrate how to create a custom action type or expression type. The example covers all aspects such as specification/design, database table creation, code generation, and user interface. After reading this guide, you should be able to create custom action types and expression types.
Caution
SAP delivers the content of this guide on the SAP Community Network (SCN) for use with SAP NetWeaver Decision Service Management or Business Rule Framework plus.
SAP does not provide support for customers using this content to develop their own action types and expression types; all risk is assumed by the customer. SAP is not liable for consequences of or damages resulting from the customer’s use of the information contained in this document.
1.2 Expression Types and Action Types
Expression Types
Expression types define the computational power of BRFplus. Each expression type is a self-contained computational unit with a well-defined logic. In general, it uses input (context data objects or nested expressions) to calculate, determine, or derive output (result). Expression types define the ABAP object class and the ABAP object interface, as well as the user interface for expressions. An expression is considered an instance of an expression type behaving in accordance with to the expression type's logic.
Expressions form the building blocks of rules in BRFplus. An expression is assigned to a function; whenever the function is processed, the expression and its nested expressions are evaluated. Expressions can be used in rulesets. Rulesets contain rules that subscribe to function processing (publish-subscribe). When functions are processed, registered rules/expressions are evaluated and actions are performed.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
7
Action Types
Action types define the interactive part of BRFplus. They are a special expression types with no output (except of the ID of the action performed). However, they have side effects. Instead of the output they perform the action defined in the logic of the action type. Each action type can have an arbitrary number of follow up actions to define an action chain. Similar to expressions, actions can use nested expressions and context data as input. Usually, action types are more application-dependent than expression types; therefore only a few action types are provided in the standard. Corresponding to the available expression types, additional action types can be created.
Class and Interface Hierarchy
The following diagram illustrates the class and interface hierarchy used for action types and expression types.
It shows that action type classes are sub classes of the generic action type class CL_FDT_ACTION. Same as with expression types that are sub classes to the generic expression type class CL_FDT_EXPRESSION.
Finally, action types are a special category of expression types. This becomes obvious in the class and interface diagram as CL_FDT_ACTION inherits from CL_FDT_EXPRESSION same as IF_FDT_ACTION includes IF_FDT_EXPRESSION. CL_FDT_MAINTENANCE with its interfaces IF_FDT_TRANSACTION and IF_FDT_ADMIN_DATA ensures that all action types and expression types provide a consistent and unique set of methods and attributes.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
8
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
9
Need for Custom Expression Types and Action Types
Although BRFplus provides a set of action types and expression types such as Decision Table, Formula, Message Log, there are good reasons to develop your own action types or expression types.
User interface simplification Generic BRFplus action types and expression types may provide too many features making the UI more complex in specific use cases.
Compound action types and expression types You can combine several expression types or action types into one compound type that enables better UI usability and integration.
Functionality not (yet) available BRFplus action types and expression types do not provide special features that may be needed in the customer situation. Usually, this is true for action types because actions are usually more dependent on the specific environment.
Building Blocks of an Expression and Action Type Creation
The process of creating expression types and action types comprises the following steps:
Specification/Design
Database Layout
Transport Objects
Interface Creation
Class Creation
New Methods
Redefined Methods
Check Methods
Process Methods
Methods for Code Generation
Methods for Lean Trace Display
Data Exchange
Creation of the Action Type or Expression Type as an Object in BRFplus
Testing the Action Type or Expression Type
Creation of the User Interface
Differences between action types and expression types are illustrated in the text.
Note
Look at the code and the database tables of BRFplus standard action types and expression types.
Action type classes implement IF_FDT_ACTION, while expression type classes implement IF_FDT_EXPRESSION.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
10
2 Specification/Design The first step is to define the capabilities of the new action type or expression type. What is it supposed to do? How should the user interface look like? What parameters should be dynamic? This question is important for the database layout. Dynamic parameters enable you to reference an expression or context data object that returns the value to be used. In this case the action type or expression type may be more complex to use and develop.
The example custom action type in this document is the email action type. With this action type, it is possible to create email actions. An email is an action that sends an email. It includes the following input fields:
Recipient(s)
Subject
Body
Recipient parameters
Message parameters
A user interface may look like this:
&1& is a placeholder for a dynamic value. An expression or context data object may be nested to enrich the message with a value from the rules processing.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
11
Note
The configuration of an application server and prerequisites for sending emails are described in SAP Note 455140.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
12
3 Database Layout You must create database tables in order to save the attributes of the action type or expression type. The database tables follow strict conventions to benefit automatically from features BRFplus provides, such as transport or version management. Look at the tables starting with FDT_ACTN for action types or FDT_EXPR for expression types to see the database tables of the BRFplus standard action types and expression types.
Procedure
1. A good example for our email action type is database table, FDT_ACTN_1010:
Structure FDT_INC_C_KEY_0002 is used for the key. This is a generic key structure used by all customizing and local action and expression tables. It includes the object ID and a version field. You can use it for the new email action table. When there is no additional key needed, create a structure for the attributes and include it in the new table. It is a good idea to use the same abbreviation in the include and in the table name, such as ACTN_1010 in the previous example. If additional key fields are needed, a new include for these key fields is created and added to the table.
2. An example is database table, FDT_ACTN_1110:
Also note that the Group field is used. This group information is not used currently but may be used in the future. You maintain it as illustrated in the example.
3. Our new include, FDT_INC_ACTN_6000_DATA, for the attributes:
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
13
4. Our new database table, FDT_ACTN_6000:
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
14
5. For your tables, use another name, but not FDT_EXPR* or FDT_ACTN* which is reserved for objects in the BRFplus standard.
Define it as a customizing table. BRFplus supports customizing objects with customizing database tables and other objects.
Object Category
Database Table Delivery Class
Database Table Name
Customizing C – Customizing Ending with a number required (Example: FDT_ACTN_6000)
System S – System Ending with a number + S required (Example: FDT_ACTN_6000S)
Master Data A – Application Ending with a number + A required (Example: FDT_ACTN_6000A)
BCF S – System Ending with a number + T required (Example: FDT_ACTN_6000T)
Note
The naming conventions for database tables as given in the previous table are mandatory and must be observed. Some internal mechanisms of BRFplus rely on this naming convention. Any deviations from the given naming convention results in errors, and the custom expression type cannot be integrated into the BRFplus framework.
Create a table for each object category with BCF being optional.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
15
6. BCF stands for Business Content Framework. This is a tool for shipping content in SAP Business ByDesign. If you have not heard of BCF so far or had to work with it, you probably do not need to create a table for this object category.
Since customizing and master data are client-dependent, you must include the client in the key. For the other object categories, create client independent-tables. You replace the key include FDT_INC_C_KEY_0002 with FDT_INC_KEY_0002:
The following configuration applies to the system table FDT_ACTN_6000S:
Data class: APPL2
Size category: 0
Buffering activated
Buffering type: Single Records Buffering
Number of key fields: 0
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
16
The following configuration applies to the system table, FDT_ACTN_6000A:
Data class: APPL0
Size category: 3
Buffering not allowed
Buffering type: None of the options is selected
Number of key fields: 0
Technical settings from BRFplus tables should be analyzed when creating custom action types and expression types, especially with respect to buffering configuration.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
17
4 Transport Objects Customizing, system and BCF table content can be transported. Therefore you need to assign the database table to transport objects. For the BRFplus standard tables, the following standard transport objects are used:
If you defined tables for your custom action type or expression type, you must create new transport objects and assign them to the customer action type or expression type definition.
An entry for BCF is optional – it applies only to Business Content Framework.
You can use transaction SOBJ to see the definition of the standard BRFplus transport objects and all database tables assigned to it.
Note
There are three standard transport objects. Click Position… and enter FDT to find the BRFplus
standard transport objects. Double-click on the name of a transport object to see how it is defined. Select the line and click Piece List to see all tables assigned to the transport object.
Object Category Transport Object
Customizing FDT0000
System FDT0001
BCF (optional) FDT0002
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
18
Guidelines
You must define your custom transport objects in a similar way as the standard transport objects with the help of transaction SOBJ. The following guidelines apply to the definition of your custom transport objects.
You only can reuse standard transport objects in custom action types or expression types when the new action type or expression type is a redefinition of an existing action type or expression type and does not include new tables.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
19
Assign only the new tables for your custom action type or expression type to your transport objects. You must not assign tables shipped by the BRFplus tool to your transport objects.
Use only one transport object per action type or expression type.
Like the standard transport objects, the new custom transport objects need to be type T.
The definition of a primary table in SOBJ is not mandatory for your custom transport objects. So the setting of the primary table can be performed as follows:
If there is a database table that contains one entry for each ID stored in your set of tables, this table should be marked as the primary table. However, this is for information purposes only. The information is not needed within the transport environment.
Such a table will exist, when you use your transport object for one expression type or action type. Assume you have a header and an item table for this expression type; the header table should be the primary table.
If there is no such table, you do not need to define a primary table.
The standard BRFplus “before export” and “after import” methods must be used in the custom transport objects.
For C tables: AFTER_IMP FDT_AFTER_IMPORT_C Cross Object = X BEFORE_EXP FDT_BEFORE_EXPORT_C Cross Object = X ZDMPRELOCK FDT_PRELOCK Cross Object = ‘ ‘
For S and T tables: AFTER_IMP FDT_AFTER_IMPORT Cross Object = X BEFORE_EXP FDT_BEFORE_EXPORT Cross Object = X ZDMPRELOCK FDT_PRELOCK Cross Object = X
The ZDMPRELOCK method is new since release SAP_BASIS 7.40.
Starting with release SAP_BASIS 7.40 you also need to apply the following settings in the AIM Details section, which can be reached from the method definition area in transaction SOBJ.
Define the corresponding BRFplus transport object as “Reference to Other AIM”.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
20
You assign the transport objects that have been defined in transaction SOBJ to the new action type and expression type under Transport Objects using transaction BRFplus.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
21
5 Interface Creation You must create an ABAP objects interface for the specific attributes of the action type or expression type.
Action type interfaces need to include IF_FDT_ACTION.
Expression type interfaces need to include IF_FDT_EXPRESSION.
The inclusion of these interfaces will introduce many methods to the new action type or expression type. In the example of the email action the prefix IF_FDT_ACTN is used, which should not be used for custom action types. Instead you must use a special customer namespace (for example, /CUS/IF_FDT_ACTN*) or the local namespace (for example, ZIF_FDT_ACTN*).
Note
Look at the comprehensive interfaces IF_FDT_ACTION and IF_FDT_EXPRESSION to find examples from other action types and expression types.
Procedure
1. Include a forward declaration for IF_FDT_TYPES.
IF_FDT_TYPES contains many type definitions you can reuse in your types, signature, and code.
2. On the Interfaces tab, insert IF_FDT_ACTION.
In case of an expression type interface, insert IF_FDT_EXPRESSION.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
22
3. We recommend you to create some types in the interface. These types are found easily and are decoupled from the database.
TYPES:
BEGIN OF s_parameter,
POSITION TYPE n LENGTH 1,
parameter_id TYPE if_fdt_types=>id,
END OF s_parameter .
TYPES:
ts_parameter TYPE SORTED TABLE OF s_parameter WITH UNIQUE KEY position .
TYPES BODY TYPE FDT_ACTN_6000-BODY .
TYPES SUBJECT TYPE FDT_ACTN_6000-SUBJECT .
TYPES RECIPIENT TYPE FDT_ACTN_6000-RECIPIENTS .
TYPES:
t_recipient TYPE STANDARD TABLE OF recipient WITH NON-UNIQUE KEY table_line .
4. Create SET and GET methods for the expression type or action type specific attributes. You may need to create additional methods in accordance with the action type or expression type involved.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
23
5. To support versioning of the message properties (look at the database layout), you need to offer an optional timestamp as an importing parameter for the GET methods.
If an attribute should be returned it may be returned for the last (current) version. This is the case when no timestamp is supplied. It may be returned for a specific version when a timestamp is supplied. Then the correct version for the timestamp will be determined and is used for the database read. The timestamp may not find a version. In this case a CX_FDT_INPUT exception should be returned.
Interface section of the GET methods for the Email action type:
METHODS get_body
IMPORTING
!iv_timestamp TYPE if_fdt_types=>timestamp OPTIONAL
RETURNING
value(rv_body) TYPE body
RAISING
cx_fdt_input .
METHODS get_parameters
IMPORTING
!iv_timestamp TYPE if_fdt_types=>timestamp OPTIONAL
EXPORTING
!ets_message_parameter TYPE ts_parameter
!ets_recipient_parameter TYPE ts_parameter
RAISING
cx_fdt_input .
METHODS get_recipients
IMPORTING
!iv_timestamp TYPE if_fdt_types=>timestamp OPTIONAL
RETURNING
value(rt_recipient) TYPE t_recipient
RAISING
cx_fdt_input .
METHODS get_subject
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
24
IMPORTING
!iv_timestamp TYPE if_fdt_types=>timestamp OPTIONAL
RETURNING
value(rv_subject) TYPE subject
RAISING
cx_fdt_input .
Interface section of the SET methods for the Email action type:
METHODS set_body
IMPORTING
!iv_body TYPE body
RAISING
cx_fdt_input .
METHODS set_parameters
IMPORTING
!its_message_parameter TYPE ts_parameter OPTIONAL
!its_recipient_parameter TYPE ts_parameter OPTIONAL
PREFERRED PARAMETER its_recipient_parameter
RAISING
cx_fdt_input .
METHODS set_recipients
IMPORTING
!it_recipient TYPE t_recipient
RAISING
cx_fdt_input .
METHODS set_subject
IMPORTING
!iv_subject TYPE subject
RAISING
cx_fdt_input .
Note
In the code examples, the following naming conventions are used.
o R = Returning o E = Exporting o I = Importing o RT = Returning table o RS = Returning structure o IF = Interface o CL = Class
6. Create several aliases (that will be used in the following code or even by generic BRFplus methods).
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
25
Interface Component Alias
IF_FDT_ADMIN_DATA~MV_CUSTOMIZING_OBJECT MV_CUSTOMIZING_OBJECT
IF_FDT_ADMIN_DATA~MV_ID MV_ID
IF_FDT_ADMIN_DATA~MV_LOCAL_OBJECT MV_LOCAL_OBJECT
IF_FDT_ADMIN_DATA~MV_MASTERDATA_OBJECT MV_MASTERDATA_OBJECT
IF_FDT_ADMIN_DATA~MV_OBJECT_TYPE MV_OBJECT_TYPE
IF_FDT_ADMIN_DATA~MV_SYSTEM_OBJECT MV_SYSTEM_OBJECT
IF_FDT_EXPRESSION~MV_ACTION MV_ACTION
IF_FDT_EXPRESSION~MV_EXPRESSION_TYPE_ID MV_EXPRESSION_TYPE_ID
IF_FDT_EXPRESSION~MV_RULE MV_RULE
In the example of the email action type, you do not need any attributes at this point. For other action type or expression types, attributes may be used.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
26
6 Class Creation You must create an ABAP objects class for the action type or expression type.
Action type classes need to inherit from CL_FDT_ACTION.
Expression type classes need to inherit from CL_FDT_EXPRESSION.
Inheritance ensures that many features do not need to be implemented manually. In the example of the email action, the prefix, CL_FDT_ACTN, must not be used for custom action types. Instead a special customer namespace (for example, /CUS/CL_FDT_ACTN*) or the local namespace (for example, ZCL_FDT_ACTN*) should be used.
Note
Look at the classes inheriting from CL_FDT_ACTION and CL_FDT_EXPRESSION to find examples from other action types and expression types.
All macros used in the sample code can be copied.
FDT_INCL_CHANGE_HISTORY_MACRO
FDT_INCL_MESSAGE_MACROS
FDT_INCL_TRACE_MACROS
The includes must not be used directly, but macros have to be copied.
Procedure
1. Include a forward declaration for IF_FDT_TYPES. IF_FDT_TYPES contains many type definitions you can reuse in your types, signature and code.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
27
You include the corresponding interface to the interface list.
2. Define friendship to CL_FDT_FACTORY.
In combination with a non-public instantiation, this ensures only CL_FDT_FACTORY can create an instance.
3. Create a private type S_BUFFER.
This type is used at many places in the class. It should contain all attributes of the specific action type or expression type. The email action type S_BUFFER is defined as follows:
INTERFACE if_fdt_actn_email LOAD .
TYPES:
BEGIN OF s_buffer,
t_recipient TYPE if_fdt_actn_email=>t_recipient,
subject TYPE if_fdt_actn_email=>subject,
body TYPE if_fdt_actn_email=>body,
ts_message_parameter TYPE if_fdt_actn_email=>ts_parameter,
ts_rec_parameter TYPE if_fdt_actn_email=>ts_parameter,
END OF s_buffer.
Note
You may need to add a forward declaration to the interface as shown in the example. You can do this in the section for forward declarations in the class properties.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
28
Some methods and attributes need to be provided to follow a specific implementation pattern that makes use of many concepts in BRFplus. Big parts of the code for the implementation can be copied and are provided with the following pages of this document.
4. Create the following methods (private section):
METHODS load_buffer
IMPORTING
!iv_timestamp TYPE if_fdt_types=>timestamp OPTIONAL
!iv_version TYPE if_fdt_types=>version OPTIONAL
PREFERRED PARAMETER iv_version
RETURNING
value(rs_buffer) TYPE s_buffer
RAISING
cx_fdt_input .
METHODS load_buffer_db
IMPORTING
!iv_version TYPE if_fdt_types=>version OPTIONAL
RETURNING
value(rs_buffer) TYPE s_buffer .
METHODS save_buffer_db .
METHODS set_buffer
IMPORTING
!is_buffer TYPE s_buffer .
Note
You can define a preferred parameter in the section directly or you navigate to the parameter on the method tab. Place your cursor on the parameter to be switched to preferred. Then press the detail view button and check the preferred field in the upcoming dialog.
5. Define the following attributes:
Attribute Level Visibility Associated Type
Initial Value
MS_BUFFER Instance Attribute
Private Type
S_BUFFER
MV_MS_BUFFER_LOADED Instance Attribute
Private Type
ABAP_BOOL
MS_BUFFER_DB Instance Attribute
Private Type
S_BUFFER
MV_MS_BUFFER_DB_LOADED Instance Attribute
Private Type
ABAP_BOOL
GC_ACTN_6000 Constant Private Type
TABNAME FDT_ACTN_6000
In the last entry (GC_ACTN_6000), the constant (FDT_ACTN_6000) must be replaced by the specific database table name of the action type or expression type. Other action types and expression types have more constants defined. For each database table, a constant is necessary. It is important to use the customizing table name (not system or master data) for the constant value.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
29
Note
When you look at the standard action types and expression types, you find constants for the database tables in the class. You can find which tables belong to a specific action type or expression type.
6. Create a public alias for the object ID:
Interface Component Alias Visible
IF_FDT_ADMIN_DATA~MV_ID MV_ID Public
7. Define your required macros in the macro section of the class:
INCLUDE: fdt_incl_message_macros,
fdt_incl_trace_macros,
fdt_incl_change_history_macro.
These macros will be used in many methods of nearly all action and expression types. These includes are currently not in the package interface. Therefore, you may need to copy the macros that are needed by you directly into the macro section of your class.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
30
7 New Methods This chapter deals with the implementation of the new methods you created. All the methods follow specific patterns that have been applied to all action types and expression types provided in the BRFplus standard. We strongly recommend you to follow these patterns; by doing so, you automatically benefit from many features. Also, it can help when you compare your code with existing and similar classes.
7.1 LOAD_BUFFER_DB The purpose of the method is to load the object data from the database into the object buffer (returning parameter RS_BUFFER). Not all data are loaded; only the data for the specified version (IV_VERSION) or the latest version, which is the working version. The method makes use of a helper object (IF_FDT_PERSISTENCE) that is responsible for reading data
Of the correction object ID
In the correct version
In the correct database table
All of the above works when the patterns are applied. The method supports a buffering mechanism so it may return data from the database buffer (MS_BUFFER_DB). For newly created action and expression types it is good practice to copy the method from an existing class and adjust with the correct data structure and constants for database table names.
No data from other tables for texts, documentation, name have to be read. They will be read by super classes. In this class, the focus is on the application or expression type specific attributes. That is true for this method and for all methods of the class.
For the email action type, create the following code:
DATA: lo_persistence TYPE REF TO if_fdt_persistence,
lv_version TYPE if_fdt_types=>version,
ls_actn_6000 TYPE fdt_actn_6000s,
ls_parameter TYPE if_fdt_actn_email=>s_parameter,
lv_last_db_version TYPE if_fdt_types=>version.
DEFINE db_to_buffer_format.
if &1 is not initial.
ls_parameter-position = &2.
ls_parameter-parameter_id = &1.
insert ls_parameter into table &3.
clear ls_parameter.
endif.
END-OF-DEFINITION.
For each database table from which you wish to read data, you create a variable. When the data are flat, create a structure. When data are deep, create a table. For the latter case, look at other classes to reference the pattern.
lo_persistence = get_persistence( ).
lo_persistence->get_versions(
IMPORTING ev_last_db_version = lv_last_db_version ).
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
31
IF mv_ms_buffer_db_loaded EQ abap_true AND
( iv_version IS NOT SUPPLIED OR
iv_version EQ lv_last_db_version ).
* shortcut: we do not read the data again that is already in the buffer
rs_buffer = ms_buffer_db.
RETURN. ">>>
ENDIF.
IF iv_version IS SUPPLIED.
lv_version = iv_version.
ELSE.
lv_version = lv_last_db_version.
ENDIF.
This part deals with initialization and buffer management. It can be copied without any modification.
* FDT_ACTN_6000
lo_persistence->read_single( EXPORTING iv_version = lv_version
iv_tabname = gc_actn_6000
IMPORTING es_value = ls_actn_6000 ).
SPLIT ls_actn_6000-recipients AT space INTO TABLE rs_buffer-t_recipient.
rs_buffer-subject = ls_actn_6000-subject.
rs_buffer-body = ls_actn_6000-body.
"Add position and format it and add to buffer.
db_to_buffer_format ls_actn_6000-rec_param_1 1 rs_buffer-ts_rec_parameter.
db_to_buffer_format ls_actn_6000-rec_param_2 2 rs_buffer-ts_rec_parameter.
db_to_buffer_format ls_actn_6000-rec_param_3 3 rs_buffer-ts_rec_parameter.
db_to_buffer_format ls_actn_6000-rec_param_4 4 rs_buffer-ts_rec_parameter.
db_to_buffer_format ls_actn_6000-msg_param_1 1 rs_buffer-ts_message_parameter.
db_to_buffer_format ls_actn_6000-msg_param_2 2 rs_buffer-ts_message_parameter.
db_to_buffer_format ls_actn_6000-msg_param_3 3 rs_buffer-ts_message_parameter.
db_to_buffer_format ls_actn_6000-msg_param_4 4 rs_buffer-ts_message_parameter.
db_to_buffer_format ls_actn_6000-msg_param_5 5 rs_buffer-ts_message_parameter.
db_to_buffer_format ls_actn_6000-msg_param_6 6 rs_buffer-ts_message_parameter.
db_to_buffer_format ls_actn_6000-msg_param_7 7 rs_buffer-ts_message_parameter.
db_to_buffer_format ls_actn_6000-msg_param_8 8 rs_buffer-ts_message_parameter.
In this part we perform a read of a single record from our database table. Use the constant we have created for the database table. LO_PERSISTENCE will ensure the right database table is used for reading (customizing, system) and the select is done with the correction version and identifier. When the data has been read, it is moved into our buffer structure. In the example, a conversion of the recipients list from the database format to buffer format is done. This part depends on the specific attributes of the action or expression type. Many action and expression types read data from more than one class. Some set default values in case there is no value in DB.
* fill the buffer if appropriate
IF mv_ms_buffer_db_loaded EQ abap_false AND
( iv_version IS NOT SUPPLIED OR iv_version EQ lv_last_db_version ).
ms_buffer_db = rs_buffer.
mv_ms_buffer_db_loaded = abap_true.
ENDIF.
The rest of the method can be overtaken without any change. This part finalizes the buffer management.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
32
7.2 LOAD_BUFFER The purpose of this method is to return the correct buffer information in accordance with the version or timestamp provided. Data may be returned from:
The object buffer MS_BUFFER
The database buffer (see LOAD_BUFFER_DB) or
The database (see LOAD_BUFFER_DB)
The code of the method is always the same. There is no action type or expression type specific aspect.
For the email action type, copy the following code:
DATA: lts_version TYPE if_fdt_admin_data=>ts_version,
ls_message TYPE if_fdt_types=>s_message,
lt_message TYPE if_fdt_types=>t_message,
lv_version TYPE if_fdt_types=>version,
lv_no_version TYPE abap_bool,
lv_ctext TYPE c LENGTH 20,
lv_i TYPE i.
FIELD-SYMBOLS <ls_version> TYPE if_fdt_admin_data=>s_version.
ASSERT NOT ( iv_timestamp IS SUPPLIED AND iv_version IS SUPPLIED ). ">>>
IF iv_timestamp IS NOT SUPPLIED AND iv_version IS NOT SUPPLIED.
* when we have the last version we can read from the buffer
IF mv_ms_buffer_loaded EQ abap_false.
* load the buffer with the last version
ms_buffer = load_buffer_db( ).
mv_ms_buffer_loaded = abap_true.
ENDIF.
rs_buffer = ms_buffer.
RETURN. ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
ELSEIF iv_timestamp IS SUPPLIED. "get the right version for the tmstmp
if_fdt_admin_data~get_active_version(
EXPORTING iv_timestamp = iv_timestamp
IMPORTING ev_version = lv_version
ev_no_version = lv_no_version ).
IF lv_no_version EQ abap_true.
WRITE iv_timestamp TO lv_ctext. "Convert to external format
DATA: lv_name TYPE if_fdt_types=>name.
DATA: lv_str_name TYPE string.
cl_fdt_services=>get_texts_for_id( EXPORTING iv_id = mv_id
IMPORTING ev_name = lv_name ).
lv_str_name = lv_name.
MESSAGE x006(fdt_core) WITH lv_str_name lv_ctext INTO ls_message-text.
message_exception ls_message lt_message cx_fdt_input.
ENDIF.
ELSEIF iv_version IS SUPPLIED. "use this version
lv_version = iv_version.
ENDIF.
* do we have a valid version now?
if_fdt_admin_data~get_versions( IMPORTING ets_version = lts_version ).
READ TABLE lts_version ASSIGNING <ls_version>
WITH TABLE KEY version = lv_version.
IF sy-subrc NE 0.
MESSAGE x002(fdt_core) WITH iv_version INTO ls_message-text.
message_exception ls_message lt_message cx_fdt_input.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
33
ENDIF.
lv_i = lines( lts_version ).
READ TABLE lts_version INDEX lv_i ASSIGNING <ls_version>.
IF <ls_version>-version EQ lv_version.
* when we have the last version we can read from the buffer
IF mv_ms_buffer_loaded EQ abap_false.
* load the buffer with the last version
ms_buffer = load_buffer_db( ).
mv_ms_buffer_loaded = abap_true.
ENDIF.
rs_buffer = ms_buffer.
ELSE.
* requested version has to be loaded from DB
rs_buffer = load_buffer_db( lv_version ).
ENDIF.
7.3 SET_BUFFER The purpose of the method is to copy data to the object buffer.
For our example, copy the code:
ms_buffer = is_buffer.
7.4 SAVE_BUFFER_DB The purpose of the method is to write data to the database. It corresponds to LOAD_BUFFER_DB and follows similar ideas by using interface IF_FDT_PERSISTENCE to ensure writing data
For the correct identifier
In the correction version
To the correct database table
The best approach for this method is to simply copy from a good example and adjust in accordance with the specific attributes of the action type or expression type.
For the email action type, create the following code:
DATA: lo_persistence TYPE REF TO if_fdt_persistence,
ls_actn_6000 TYPE fdt_actn_6000s,
ls_parameter TYPE if_fdt_actn_email=>s_parameter.
For each database table a structure or an internal table needs to be created. Internal tables are needed in case of additional key fields; there may be many entries in the database for one identifier version combination. Look at other classes and patterns applied if you need an internal table. IF_FDT_PERSISTENCE offers additional features to divide the data into lines written into the database and lines that need to be deleted from database.
lo_persistence = get_persistence( ).
This line is following the standard pattern:
* FDT_ACTN_6000
ls_actn_6000-body = ms_buffer-body.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
34
ls_actn_6000-subject = ms_buffer-subject.
LOOP AT ms_buffer-ts_message_parameter INTO ls_parameter.
CASE ls_parameter-position.
WHEN 1.
ls_actn_6000-msg_param_1 = ls_parameter-parameter_id.
WHEN 2.
ls_actn_6000-msg_param_2 = ls_parameter-parameter_id.
WHEN 3.
ls_actn_6000-msg_param_3 = ls_parameter-parameter_id.
WHEN 4.
ls_actn_6000-msg_param_4 = ls_parameter-parameter_id.
WHEN 5.
ls_actn_6000-msg_param_5 = ls_parameter-parameter_id.
WHEN 6.
ls_actn_6000-msg_param_6 = ls_parameter-parameter_id.
WHEN 7.
ls_actn_6000-msg_param_7 = ls_parameter-parameter_id.
WHEN 8.
ls_actn_6000-msg_param_8 = ls_parameter-parameter_id.
ENDCASE.
ENDLOOP.
LOOP AT ms_buffer-ts_rec_parameter INTO ls_parameter.
CASE ls_parameter-position.
WHEN 1.
ls_actn_6000-rec_param_1 = ls_parameter-parameter_id.
WHEN 2.
ls_actn_6000-rec_param_2 = ls_parameter-parameter_id.
WHEN 3.
ls_actn_6000-rec_param_3 = ls_parameter-parameter_id.
WHEN 4.
ls_actn_6000-rec_param_4 = ls_parameter-parameter_id.
ENDCASE.
ENDLOOP.
CONCATENATE LINES OF ms_buffer-t_recipient INTO ls_actn_6000-recipients
SEPARATED BY space.
lo_persistence->write_single( iv_tabname = gc_actn_6000
is_value = ls_actn_6000 ).
This part deals with data conversion from buffer to database structure. Subsequently, data are written into the database.
ms_buffer_db = ms_buffer.
mv_ms_buffer_loaded = abap_true.
mv_ms_buffer_db_loaded = abap_true.
The remainder follows a generic pattern and you can copy it without modification.
7.5 Method in Action Type or Expression Type Specific Interfaces
The following methods are specific to the action type or expression type. The implementation is explained with reference to the new email action type.
7.5.1 IF_FDT_ACTN_EMAIL=>GET_BODY
This is a simple GET method for the email body. All GET methods follow the same pattern.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
35
1. The correct buffer data are loaded.
2. The data are returned.
DATA ls_buffer TYPE s_buffer.
IF iv_timestamp IS SUPPLIED.
ls_buffer = load_buffer( iv_timestamp = iv_timestamp ).
ELSE.
ls_buffer = load_buffer( ).
ENDIF.
rv_body = ls_buffer-body.
7.5.2 IF_FDT_ACTN_EMAIL=>GET_RECIPIENTS
DATA ls_buffer TYPE s_buffer.
IF iv_timestamp IS SUPPLIED.
ls_buffer = load_buffer( iv_timestamp = iv_timestamp ).
ELSE.
ls_buffer = load_buffer( ).
ENDIF.
rt_recipient = ls_buffer-t_recipient.
7.5.3 IF_FDT_ACTN_EMAIL=>GET_PARAMETERS
DATA ls_buffer TYPE s_buffer.
IF iv_timestamp IS SUPPLIED.
ls_buffer = load_buffer( iv_timestamp = iv_timestamp ).
ELSE.
ls_buffer = load_buffer( ).
ENDIF.
ets_message_parameter = ls_buffer-ts_message_parameter.
ets_recipient_parameter = ls_buffer-ts_rec_parameter.
7.5.4 IF_FDT_ACTN_EMAIL=>GET_SUBJECT
DATA ls_buffer TYPE s_buffer.
IF iv_timestamp IS SUPPLIED.
ls_buffer = load_buffer( iv_timestamp = iv_timestamp ).
ELSE.
ls_buffer = load_buffer( ).
ENDIF.
rv_subject = ls_buffer-subject.
7.5.5 IF_FDT_ACTN_EMAIL=>SET_BODY
This is a simple SET method for the email body. All SET methods follow the same pattern.
1. The current buffer values are read.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
36
2. The value is checked and changed in the buffer.
3. Method NOTIFY_CHANGE is called.
The call of notify_change is very important since several generic features provided by super
classes depend on the information that changes occurred.
DATA: ls_buffer TYPE s_buffer.
ls_buffer = load_buffer( ).
CHECK ls_buffer-body NE iv_body. ">>>
ls_buffer-body = iv_body.
set_buffer( ls_buffer ).
notify_change( ).
7.5.6 IF_FDT_ACTN_EMAIL=>SET_RECIPIENTS
DATA: ls_buffer TYPE s_buffer,
lt_recipient LIKE it_recipient.
ls_buffer = load_buffer( ).
CHECK ls_buffer-t_recipient NE it_recipient. ">>>
* remove empty lines
lt_recipient = it_recipient.
DELETE lt_recipient WHERE table_line IS INITIAL.
ls_buffer-t_recipient = lt_recipient.
set_buffer( ls_buffer ).
notify_change( ).
7.5.7 IF_FDT_ACTN_EMAIL=>SET_PARAMETERS
DATA: ls_buffer TYPE s_buffer,
ls_parameter TYPE if_fdt_actn_email=>s_parameter,
lts_parameter TYPE if_fdt_actn_email=>ts_parameter,
ls_msg TYPE if_fdt_types=>s_message,
lt_msg TYPE if_fdt_types=>t_message,
lv_unknown TYPE abap_bool.
ls_buffer = load_buffer( ).
CHECK ( its_message_parameter IS SUPPLIED AND
its_message_parameter NE ls_buffer-ts_message_parameter ) OR
( its_recipient_parameter IS SUPPLIED AND
its_recipient_parameter NE ls_buffer-ts_rec_parameter ).
" check for the allowed number of paramters.
IF lines( its_message_parameter ) > 8.
MESSAGE e316(fdt_expressions) INTO ls_msg-text.
message_exception ls_msg lt_msg cx_fdt_input.
ELSEIF lines( its_recipient_parameter ) > 4.
MESSAGE e317(fdt_expressions) INTO ls_msg-text.
message_exception ls_msg lt_msg cx_fdt_input.
ENDIF.
IF its_message_parameter IS SUPPLIED.
LOOP AT its_message_parameter INTO ls_parameter.
* ls_parameter-position = sy-tabix.
* INSERT ls_parameter INTO TABLE lts_parameter.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
37
" check for valid ID
IF ls_parameter-parameter_id IS NOT INITIAL.
cl_fdt_admin_data=>check_id( EXPORTING iv_id = ls_parameter-parameter_id
IMPORTING ev_id_unknown = lv_unknown ).
IF lv_unknown EQ abap_true.
get_name ls_parameter-parameter_id ls_msg-msgv1.
MESSAGE e320(fdt_expressions) WITH ls_parameter-parameter_id ls_parameter-
position INTO ls_msg-text.
append_message ls_msg lt_msg.
ENDIF.
ENDIF.
ENDLOOP.
ls_buffer-ts_message_parameter = its_message_parameter.
ENDIF.
CLEAR lts_parameter.
IF its_recipient_parameter IS SUPPLIED.
LOOP AT its_recipient_parameter INTO ls_parameter.
ls_parameter-position = sy-tabix.
INSERT ls_parameter INTO TABLE lts_parameter.
" check for valid ID
IF ls_parameter-parameter_id IS NOT INITIAL.
cl_fdt_admin_data=>check_id( EXPORTING iv_id = ls_parameter-parameter_id
IMPORTING ev_id_unknown = lv_unknown ).
IF lv_unknown EQ abap_true.
get_name ls_parameter-parameter_id ls_msg-msgv1.
MESSAGE e320(fdt_expressions) WITH ls_parameter-parameter_id ls_parameter-
position INTO ls_msg-text.
append_message ls_msg lt_msg.
ENDIF.
ENDIF.
ENDLOOP.
ls_buffer-ts_rec_parameter = lts_parameter.
ENDIF.
set_buffer( ls_buffer ).
notify_change( ).
7.5.8 IF_FDT_ACTN_EMAIL=>SET_SUBJECT
DATA: ls_buffer TYPE s_buffer.
ls_buffer = load_buffer( ).
CHECK ls_buffer-subject NE iv_subject. ">>>
ls_buffer-subject = iv_subject.
set_buffer( ls_buffer ).
notify_change( ).
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
38
Note o SET methods allow also invalid data. Checking data validity takes
place during activation; before activation, invalid data go into the buffer and DB, which gives freedom when building the content.
o Return an exception (CX_FDT_INPUT) when an input is wrong, such as an ID that is neither an expression nor a data object. Use method GET_ID_INFORMATION in class CL_FDT_FACTORY to check.
o There are some methods that are specific to the custom action and expression. These are not described in detail; if you are in doubt, check the system.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
39
8 Redefined Methods This chapter deals with the mandatory redefinition of methods.
8.1 Class CL_FDT_MAINTENANCE 8.1.1 DELETE_FROM_DB
The purpose of this method is to delete an object (identifier with data) from the database. The implementation follows a simple pattern that can be copied. Services from CL_FDT_PERSISTENCE are used.
Our example contains the following code:
DATA lo_persistence TYPE REF TO if_fdt_persistence.
ASSERT iv_transport_request IS NOT SUPPLIED. ">>>not supported anymore
super->delete_from_db( ).
lo_persistence = get_persistence( ).
lo_persistence->delete( iv_tabname = gc_actn_6000 ).
You only need to use the constant with the database name from which the data are to be deleted. Class inheritance ensures data from other tables are deleted by super classes.
8.1.2 CHECK
The purpose of this method is to check the object. It is discussed in chapter 9 Check Methods.
8.1.3 COPY
This method copies the buffer of the class. This involves only the attributes added by the class, not the attributes copied by the super class. The method follows a pattern used by all action types and expression types in the BRFplus standard.
In our example, the following code is created:
DATA: lo_email TYPE REF TO cl_fdt_actn_email,
ls_buffer TYPE s_buffer.
FIELD-SYMBOLS: <ls_parameter> TYPE if_fdt_actn_email=>s_parameter.
Adapt the class name to create a copy with the correct type:
lo_email ?= super->copy( iv_deep = iv_deep
iv_application_id = iv_application_id ).
IV_DEEP enables creation of a deep copy. When the action or expression uses other objects such as expressions, action, data objects, which are nearly always the case, those objects will be copied in case the
deep flag is set to ABAP_TRUE. In detail, the deep object management works as follows:
Used unnamed objects are copied always
Used named objects in the same application are copied only when IV_DEEP = ABAP_TRUE
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
40
Used named objects in other applications are not copied
Any deep operation stops at the application border
When another application is provided (IV_APPLICATION_ID), the copied objects are created in this application (with reference to this application).
* copy the attributes of this class
ls_buffer = load_buffer( ).
LOOP AT ls_buffer-ts_message_parameter ASSIGNING <ls_parameter>.
<ls_parameter>-parameter_id = copy_on_request(
iv_id = <ls_parameter>-parameter_id
iv_deep = iv_deep
iv_application_id = iv_application_id ).
ENDLOOP.
LOOP AT ls_buffer-ts_rec_parameter ASSIGNING <ls_parameter>.
<ls_parameter>-parameter_id = copy_on_request(
iv_id = <ls_parameter>-parameter_id
iv_deep = iv_deep
iv_application_id = iv_application_id ).
ENDLOOP.
lo_email->set_buffer( ls_buffer ).
ro_copy ?= lo_email.
Using the COPY_ON_REQUEST method, you ensure each object is copied once.
Example
There is an object A using objects B and C. Object B uses object C, too.
Now we copy A to A*. This results in A passing the copy to B and C which results in copies B* and C*.
However, B also passes the copy to C, which must not result in another copy of C (C**) but in C*. COPY_ON_REQUEST detects that C was already copied to C* and returns C* instead of creating another copy.
8.1.4 RESTORE_BUFFER
This method rolls back the object buffer to the database buffer. The method follows a generic pattern and can be copied without any changes.
super->restore_buffer( iv_buffer_db ).
IF iv_buffer_db EQ abap_true.
CLEAR ms_buffer_db.
mv_ms_buffer_db_loaded = abap_false.
ms_buffer_db = load_buffer_db( ).
ENDIF.
CLEAR ms_buffer.
mv_ms_buffer_loaded = abap_false.
ms_buffer = load_buffer( ).
8.1.5 SAVE_WITH_VERSION
The purpose of the method is to distribute the save information in the layers of the inheritance hierarchy. The method follows a generic pattern and can be copied without any changes.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
41
IF iv_transport_request IS SUPPLIED.
super->save_with_version( iv_version = iv_version
iv_last_db_version = iv_last_db_version
iv_transport_request = iv_transport_request ).
ELSE.
super->save_with_version( iv_version = iv_version
iv_last_db_version = iv_last_db_version ).
ENDIF.
* we only need to save the data of this class when there is a change in
* the attributes or when the version needs to be changed
CHECK iv_version NE iv_last_db_version OR ms_buffer NE ms_buffer_db.
save_buffer_db( ).
8.1.6 TRANSPORT
This method writes an object to a transport request. The implementation follows a simple pattern that you can copy. Services from IF_FDT_PERSISTENCE are used.
Our example contains the following code:
DATA lo_persistence TYPE REF TO if_fdt_persistence.
super->transport( iv_transport_request = iv_transport_request ).
lo_persistence = get_persistence( ).
lo_persistence->transport( iv_transport_request = iv_transport_request
iv_tabname = gc_actn_6000 ).
You only need to use our constant with the database name from which the data is to be transported. Class inheritance ensures data from other tables are written to transport by super classes.
Note
The following is important with regard to the TRANSPORT method:
You must not create transport piece list entries manually for the table entries of self-implemented expression types.
o You can use report FDT_TRANS to record existing instances or applications on a transport.
o If the expression types were designed correctly, the tables created for these expression types should take part in automated change recording which is done via the folllowing interface methods: - IF_FDT_TRANSACTION-SAVE
(saving and recording of changes) - IF_FDT_TRANSACTION-TRANSPORT
(creating new piece lists for instances)
Caution
Please review your implementation of the TRANSPORT method before you start transporting your content.
We recommend that you first use of a transport copies to a test system to ensure your expression types are implemented so they can be transported without problems. This will avoid the possibility of broken data being transported to the whole system landscape.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
42
8.1.7 GET_CHANGE_DETAILS
This method returns information on changes for two given versions or timestamps. Some of the changes are processing relevant (for example, changed email subject) and some are not processing relevant (for example, changed action text).
Method Parameters
Parameter Type Purpose
IV_1ST_VRS_TMSTMP Importing The first timestamp for the comparison. If initial, it implies the second timestamp, which has been supplied, is the first version of the object.
IV_2ND_VRS_TMSTMP Importing The second timestamp for the comparison. Together, the first and second timestamps define the time range within which user wants to locate the changes performed on the object.
IV_SUB_OBJ_1ST_VRS_TMSTMP Importing The first timestamp for the comparison of the sub- object changes. If initial, it implies the second timestamp, which has been supplied, is the first version of the sub-object.
IV_SUB_OBJ_2ND_VRS_TMSTMP Importing The second timestamp for the comparison of the sub- object changes. Together the first and second timestamps define the range between which the changes in the sub-objects has to be analyzed. The changes are retrieved for the second timestamp of the parent object.
IV_COMPARE_ONLY_SUB_OBJECT Importing A Boolean parameter, when ABAP_TRUE passed, only the sub objects are compared; when ABAP_FALSE is passed, the main object and sub- objects are compared.
IV_SUPERIOR_ID Importing ID of the object referencing the called object.
IV_DETAILED Importing This parameter defines whether all detail information should be added or if summary of changes is required
ETS_PROCESSING_CHANGE Exporting This parameter contains the details of processing changes for the called object and sub objects (IV_COMPARE_ONLY_SUB_OBJECT= ABAP_FALSE).
ETS_NON_PROCESSING_CHANGE Exporting This parameter contains the details of non-processing changes for the called object and sub objects (IV_COMPARE_ONLY_SUB_OBJECT= ABAP_FALSE).
CTS_CHANGE_TIMESLICE Changing This parameter carries the time slices of the object that has been covered. In case of a recursive loop, the changes on the time slices already covered are not analyzed.
CTS_CHANGE_SUMMARY Changing This parameter carries the change summary of the objects that have been covered. While analyzing the sub-objects, this parameter must be passed.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
43
The code in class CL_FDT_ACTN_EMAIL will help you understand how the method should work. You can copy and adapt code from there.
Note
You can create an application with versioning switched on by default to test the method for your custom type and how it works in combination with nested objects.
8.2 Interface IF_FDT_EXPRESSION You must implement the following methods:
PROCESS
GENERATE_PROCESS
IS_GENERATING
A successful check is required to activate an object. The result of the method is a list of information, warnings, or error messages.
This method is implemented to reuse the specific check methods that are used in the SET methods. Detailed checks are performed such as validation of the email addresses supplied or UUIDs used. In this example, the code of the check method is as follows:
DATA: ls_msg TYPE if_fdt_types=>s_message,
lt_msg TYPE if_fdt_types=>t_message,
lt_recipient TYPE if_fdt_actn_email=>t_recipient,
lv_body TYPE if_fdt_actn_email=>body,
lv_subject TYPE if_fdt_actn_email=>subject,
lts_rec_param TYPE if_fdt_actn_email=>ts_parameter,
ls_rec_param TYPE if_fdt_actn_email=>s_parameter,
lts_msg_param TYPE if_fdt_actn_email=>ts_parameter,
ls_msg_param TYPE if_fdt_actn_email=>s_parameter.
rt_message = super->check( iv_activation_check ).
* IV_ACTIVATION_CHECK set to true means that the active version of used
* objects has to be included into the check.
* IV_ACTIVATION_CHECK set to false means that the latest version of used
* objects has to be included into the check.
lt_recipient = if_fdt_actn_email~get_recipients( ).
lv_subject = if_fdt_actn_email~get_subject( ).
lv_body = if_fdt_actn_email~get_body( ).
if_fdt_actn_email~get_parameters( IMPORTING ets_message_parameter = lts_msg_param
ets_recipient_parameter = lts_rec_param ).
* No initial parameter ids in the param tables
LOOP AT lts_msg_param INTO ls_msg_param WHERE parameter_id IS INITIAL.
MESSAGE e321(fdt_expressions) WITH 'message body'(005) ls_msg_param-
position INTO ls_msg-text. append_message ls_msg rt_message.
ENDLOOP.
LOOP AT lts_rec_param INTO ls_rec_param WHERE parameter_id IS INITIAL.
MESSAGE e321(fdt_expressions) WITH 'recipients'(006) ls_rec_param-
position INTO ls_msg-text. append_message ls_msg rt_message.
ENDLOOP.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
44
* body and subject both must not be initial.
IF lv_body IS INITIAL AND lv_subject IS INITIAL.
MESSAGE e311(fdt_expressions) INTO ls_msg-text. append_message ls_msg rt_message.
ENDIF.
lt_msg = CHECK_RECIPIENTS( it_recipient = lt_recipient
its_parameter = lts_rec_param
iv_activation_check = abap_true ).
INSERT LINES OF lt_msg INTO TABLE rt_message.
lt_msg = CHECK_PARAMETERS( iv_subject = lv_subject
iv_body = lv_body
its_rec_param = lts_rec_param
its_msg_param = lts_msg_param
iv_activation_check = abap_true ).
INSERT LINES OF lt_msg INTO TABLE rt_message.
See class CL_FDT_ACTN_EMAIL for the check methods:
CHECK_RECIPIENTS
CHECK_PARAMETERS
8.2.1 GET_USED_EXPRESSIONS
The purpose of this method is to return all UUIDs of expressions which are used by this object. In the example of the Email action only the parameters hold UUIDs, which could be data objects or expressions. So you get those UUIDs and check them for expressions.
DATA: lv_obj_type TYPE if_fdt_types=>object_type,
lts_msg_parameter TYPE if_fdt_actn_email=>ts_parameter,
lts_rec_parameter TYPE if_fdt_actn_email=>ts_parameter,
ls_parameter TYPE if_fdt_actn_email=>s_parameter.
IF iv_timestamp IS SUPPLIED.
rts_expression_id = super-
>if_fdt_expression~get_used_expressions( iv_timestamp = iv_timestamp ).
ELSE.
rts_expression_id = super->if_fdt_expression~get_used_expressions( ).
ENDIF.
* ------------------------------------------------------
IF iv_timestamp IS SUPPLIED.
if_fdt_actn_email~get_parameters(
EXPORTING iv_timestamp = iv_timestamp
IMPORTING ets_message_parameter = lts_msg_parameter
ets_recipient_parameter = lts_rec_parameter ).
ELSE.
if_fdt_actn_email~get_parameters(
IMPORTING ets_message_parameter = lts_msg_parameter
ets_recipient_parameter = lts_rec_parameter ).
ENDIF.
LOOP AT lts_msg_parameter INTO ls_parameter WHERE parameter_id IS NOT INITIAL.
CLEAR lv_obj_type.
cl_fdt_admin_data=>check_id( EXPORTING iv_id = ls_parameter-parameter_id
IMPORTING ev_object_type = lv_obj_type ).
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
45
IF lv_obj_type EQ if_fdt_constants=>gc_object_type_expression.
INSERT ls_parameter-parameter_id INTO TABLE rts_expression_id.
ENDIF.
ENDLOOP.
LOOP AT lts_rec_parameter INTO ls_parameter WHERE parameter_id IS NOT INITIAL.
CLEAR lv_obj_type.
cl_fdt_factory=>get_id_information(
EXPORTING iv_id = ls_parameter-parameter_id
IMPORTING ev_object_type = lv_obj_type ).
IF lv_obj_type EQ if_fdt_constants=>gc_object_type_expression.
INSERT ls_parameter-parameter_id INTO TABLE rts_expression_id.
ENDIF.
ENDLOOP.
8.2.2 GET_DSM_AVAILABILITY
The purpose of this method is to define the release from which you want to start supporting your own expression type or action types. Specify the start release you want in the returning parameter.
When you want your defined expression type or action type to be available from SAP_BASIS 7.02, enter 702
in the field for the returning parameter. All managed systems with an SAP NetWeaver release equal to or greater than 7.02 will support your expression type.
METHOD if_fdt_expression~get_dsm_availability.
rv_start_release = 702.
ENDMETHOD.
Caution
For your defined expression type or action types, you need a working implementation for the generation mode with no switch to interpretation mode.
You define a start release, and all higher releases must support your expression types or action types. You cannot define gaps for releases that do not support your defined expression types or action types.
As owner of your defined expression types or action types you are responsible for ensuring that the generated code works correctly in the managed system and does not contain syntax errors.
Note
Relevant for SAP NetWeaver Decision Service Management only.
You must retrieve metadata such as database tables from the managed system, not your local system.
Review CL_FDT_TYPEDESCR to implement the retrieval of metadata. Focus on the uses of the class and sub-classes in the standard expression types and action types such as CL_FDT_ACTN_MESSAGE_LOG.
8.2.3 GET_CONTEXT_DATA_OBJECTS
The purpose of this method is to return all UUIDs of data objects which are used by this object. In the example of the email action, only the parameters hold UUIDs, which could be data objects or expressions.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
46
DATA: lv_obj_type TYPE if_fdt_types=>object_type,
lts_msg_parameter TYPE if_fdt_actn_email=>ts_parameter,
lts_rec_parameter TYPE if_fdt_actn_email=>ts_parameter,
ls_parameter TYPE if_fdt_actn_email=>s_parameter.
IF iv_timestamp IS SUPPLIED.
rts_object_id = super->if_fdt_expression~get_context_data_objects(
iv_timestamp = iv_timestamp
iv_deep = iv_deep
iv_used = iv_used ).
ELSE.
rts_object_id = super->if_fdt_expression~get_context_data_objects(
iv_deep = iv_deep
iv_used = iv_used ).
ENDIF.
* ------------------------------------------------------
CHECK iv_used = abap_true.
IF iv_timestamp IS SUPPLIED.
if_fdt_actn_email~get_parameters(
EXPORTING iv_timestamp = iv_timestamp
IMPORTING ets_message_parameter = lts_msg_parameter
ets_recipient_parameter = lts_rec_parameter ).
ELSE.
if_fdt_actn_email~get_parameters(
IMPORTING ets_message_parameter = lts_msg_parameter
ets_recipient_parameter = lts_rec_parameter ).
ENDIF.
LOOP AT lts_msg_parameter INTO ls_parameter.
CHECK ls_parameter-parameter_id IS NOT INITIAL.
CLEAR lv_obj_type.
cl_fdt_admin_data=>check_id( EXPORTING iv_id = ls_parameter-parameter_id
IMPORTING ev_object_type = lv_obj_type ).
IF lv_obj_type EQ if_fdt_constants=>gc_object_type_data_object.
INSERT ls_parameter-parameter_id INTO TABLE rts_object_id.
ENDIF.
ENDLOOP.
LOOP AT lts_rec_parameter INTO ls_parameter.
CHECK ls_parameter-parameter_id IS NOT INITIAL.
CLEAR lv_obj_type.
cl_fdt_admin_data=>check_id( EXPORTING iv_id = ls_parameter-parameter_id
IMPORTING ev_object_type = lv_obj_type ).
IF lv_obj_type EQ if_fdt_constants=>gc_object_type_data_object.
INSERT ls_parameter-parameter_id INTO TABLE rts_object_id.
ENDIF.
ENDLOOP.
8.3 Interface IF_FDT_ADMIN_DATA 8.3.1 TO_STRING
This method returns a string that describes the object. The result of the TO_STRING method should give you a basic idea about what the object is. This method is needed for unnamed objects. When a name is available, it appears, but this does not apply to the TO_STRING output.
The result of TO_STRING for a constant expression with a value returns the value of the constant.
The result of TO_STRING for a case expression gives you a basic idea about the results for different test parameter values.
There are two modes for the TO_STRING (see constants IF_FDT_CONSTANTS => GC_TO_STRING*).
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
47
Mode Description
Brief The result is a brief representation of the object. It does not return additional details such as the name of the object. This is the default mode.
Complete The result of the method is the complete description of the object with details such as object name.
If there is no valid information provided, you can return a default string such as “Action Type ...”. Review and debug the code in other action types and expression types for examples.
Add the following piece of code at the end of the method to truncate the string to the maximum length.
CONDENSE rv_string.
* Truncate the description string length to the max
* length allowed if applicable.
IF iv_max_length LE strlen( rv_string ).
truncate_string( EXPORTING iv_max_length = iv_max_length
CHANGING cv_string = rv_string ).
ENDIF.
8.4 Interface IF_FDT_DATA_EXCHANGE_INTERNAL
You need to implement two methods:
EXPORT_XML
IMPORT_XML
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
48
9 Check Methods
CHECK
This method is called to check the object. A successful check is required to activate an object. The result of the method is a list of information, warnings, or error messages.
It is important to understand the meaning of the importing parameter IV_ACTIVATION_CHECK. When set to ABAP_TRUE the check has to be performed as an activation check; the active versions of the referenced objects must be included within the check, (for example, a case expression that has a result data object). The result data object may be numeric in its active version and Boolean in its inactive version, which is the latest state of the object. To activate the result data object used, it must be Boolean, or an error message is returned.
Usually, this method is implemented to reuse the specific check methods used in the SET methods. Detailed checks are performed such as validation of the email addresses supplied or UUIDs used. In this example, the code of the check method looks as follows:
DATA: ls_msg TYPE if_fdt_types=>s_message,
lt_msg TYPE if_fdt_types=>t_message,
lt_recipient TYPE if_fdt_actn_email=>t_recipient,
lv_body TYPE if_fdt_actn_email=>body,
lv_subject TYPE if_fdt_actn_email=>subject,
lts_rec_param TYPE if_fdt_actn_email=>ts_parameter,
ls_rec_param TYPE if_fdt_actn_email=>s_parameter,
lts_msg_param TYPE if_fdt_actn_email=>ts_parameter,
ls_msg_param TYPE if_fdt_actn_email=>s_parameter.
rt_message = super->check( iv_activation_check ).
* IV_ACTIVATION_CHECK set to true means that the active version of used
* objects has to be included into the check.
* IV_ACTIVATION_CHECK set to false means that the latest version of used
* objects has to be included into the check.
lt_recipient = if_fdt_actn_email~get_recipients( ).
lv_subject = if_fdt_actn_email~get_subject( ).
lv_body = if_fdt_actn_email~get_body( ).
if_fdt_actn_email~get_parameters( IMPORTING ets_message_parameter = lts_msg_param
ets_recipient_parameter = lts_rec_param ).
* No initial parameter ids in the param tables
LOOP AT lts_msg_param INTO ls_msg_param WHERE parameter_id IS INITIAL.
MESSAGE e321(fdt_expressions) WITH 'message body'(005) ls_msg_param-
position INTO ls_msg-text.
append_message ls_msg rt_message.
ENDLOOP.
LOOP AT lts_rec_param INTO ls_rec_param WHERE parameter_id IS INITIAL.
MESSAGE e321(fdt_expressions) WITH 'recipients'(006) ls_rec_param-
position INTO ls_msg-text.
append_message ls_msg rt_message.
ENDLOOP.
* body and subject both must not be initial.
IF lv_body IS INITIAL AND lv_subject IS INITIAL.
MESSAGE e311(fdt_expressions) INTO ls_msg-text.
append_message ls_msg rt_message.
ENDIF.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
49
lt_msg = CHECK_RECIPIENTS( it_recipient = lt_recipient
its_parameter = lts_rec_param
iv_activation_check = abap_true ).
INSERT LINES OF lt_msg INTO TABLE rt_message.
lt_msg = CHECK_PARAMETERS( iv_subject = lv_subject
iv_body = lv_body
its_rec_param = lts_rec_param
its_msg_param = lts_msg_param
iv_activation_check = abap_true ).
INSERT LINES OF lt_msg INTO TABLE rt_message.
See class CL_FDT_ACTN_EMAIL for the check methods:
CHECK_RECIPIENTS
CHECK_PARAMETERS
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
50
10 Process Methods
PROCESS
This method is the heart of the expression type since it defines its expressiveness, that is, what you can do with it as an end user.
In BRFplus we have two different modes for processing a service:
1. Interpretation
Interpretation mode is used for test purposes or when generation mode is not available. It is slower than generation mode, but it works in all cases without preparation.
2. Generation
Generation mode is a performance-optimized way to process the service. A class is generated that represents the service. The generation can take time, but this can be done beforehand and the processing is not affected.
The method IF_FDT_EXPRESSION~PROCESS has to be redefined to enable the processing. The following table describes all parameters available for processing.
Parameter Description
IO_CONTEXT Context object
IV_TIMESTAMP Processing timestamp for the definitions to take
IO_PROCESSOR Processor instance to resolve nested objects
IO_TRACE Technical trace object
RO_RESULT Result object
CX_FDT Exception handling
In this method, the definitions of the object for the supplied timestamp (IV_TIMESTAMP) use the given context (IO_CONTEXT) to produce a result (RO_RESULT). When a trace is requested (IO_TRACE) execution steps have to be logged in the trace. When the object uses other objects (for example, use of variables in an email body), those objects can be processes with help of the processor (IO_PRCESSOR).
The PROCESS method in the action type email consists of several steps.
DATA: lo_message TYPE REF TO cl_bcs_message,
lx_bcs TYPE REF TO cx_bcs,
lr_data TYPE REF TO data,
lv_id TYPE if_fdt_types=>id,
ls_buffer TYPE s_buffer,
lv_value TYPE string,
lv_main_doc TYPE string,
lv_param TYPE string,
lt_message TYPE if_fdt_types=>t_message,
ls_message TYPE if_fdt_types=>s_message,
ls_parameter TYPE if_fdt_actn_email=>s_parameter,
lv_rec_trace TYPE string,
lo_do_type TYPE if_fdt_types=>data_object_type,
lo_typedescr TYPE REF TO cl_abap_typedescr.
DATA: lv_msg_1 TYPE string,
lv_msg_2 TYPE string,
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
51
lv_msg_3 TYPE string,
lv_msg_4 TYPE string.
FIELD-SYMBOLS: <lv_string> TYPE string,
<lv_value> TYPE any,
<la_data> TYPE any,
<lt_data> TYPE ANY TABLE,
<ls_recipient> TYPE LINE OF s_buffer-t_recipient.
First the definitions have to be loaded for the provided timestamp.
ls_buffer = load_buffer( iv_timestamp = iv_timestamp ).
With help of method GET_VALUE, you can resolve UUIDs used in the definitions of the object. The email action type, for example, may use variable returned by other expressions or contained in the context.
LOOP AT ls_buffer-ts_message_parameter INTO ls_parameter.
CONCATENATE '&' ls_parameter-position INTO lv_param.
get_value( EXPORTING iv_id = ls_parameter-parameter_id
io_context = io_context
iv_timestamp = iv_timestamp
io_processor = io_processor
IMPORTING er_value = lr_data ).
ASSIGN lr_data->* TO <la_data>.
lv_value = get_string_from_data( <la_data> ).
REPLACE ALL OCCURRENCES OF lv_param IN ls_buffer-subject WITH lv_value.
REPLACE ALL OCCURRENCES OF lv_param IN ls_buffer-body WITH lv_value.
ENDLOOP.
REPLACE ALL OCCURRENCES OF '\&&' IN ls_buffer-subject WITH '&&'.
REPLACE ALL OCCURRENCES OF '\&&' IN ls_buffer-body WITH '&&'.
Now everything is ready to send the email.
TRY.
CREATE OBJECT lo_message.
lo_message->set_subject( ls_buffer-subject ).
lv_main_doc = ls_buffer-body.
lo_message->set_main_doc( lv_main_doc ).
LOOP AT ls_buffer-t_recipient ASSIGNING <ls_recipient>.
lo_message->add_recipient( <ls_recipient> ).
IF lv_rec_trace IS NOT INITIAL .
CONCATENATE lv_rec_trace <ls_recipient> INTO lv_rec_trace SEPARATED BY ';'.
ELSE.
MOVE <ls_recipient> TO lv_rec_trace.
ENDIF.
ENDLOOP.
LOOP AT ls_buffer-ts_rec_parameter INTO ls_parameter.
get_value( EXPORTING iv_id = ls_parameter-parameter_id
io_context = io_context
iv_timestamp = iv_timestamp
io_processor = io_processor
IMPORTING er_value = lr_data ).
cl_fdt_expression=>get_type( EXPORTING iv_id = ls_parameter-parameter_id
IMPORTING ev_data_object_type = lo_do_type ).
IF lo_do_type EQ if_fdt_constants=>gc_data_object_type_element.
ASSIGN lr_data->* TO <la_data>.
lv_value = get_string_from_data( <la_data> ).
lt_message = check_recipients( iv_recipient = lv_value ).
IF cl_fdt_services=>contains_eax_message( lt_message ) EQ abap_false.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
52
lo_message->add_recipient( lv_value ).
IF lv_rec_trace IS NOT INITIAL .
CONCATENATE lv_rec_trace lv_value INTO lv_rec_trace SEPARATED BY ';'.
ELSE.
MOVE lv_value TO lv_rec_trace.
ENDIF.
ELSE.
write_trace_info_1 '222' lv_value lv_id.
ENDIF.
CLEAR lt_message.
ELSE.
ASSIGN lr_data->* TO <lt_data>.
LOOP AT <lt_data> ASSIGNING <lv_value>.
lo_typedescr = cl_abap_typedescr=>describe_by_data( <lv_value> ).
IF lo_typedescr->kind EQ cl_abap_typedescr=>kind_struct.
ASSIGN COMPONENT 1 OF STRUCTURE <lv_value> TO <lv_string>.
ELSEIF lo_typedescr->kind EQ cl_abap_typedescr=>kind_elem.
ASSIGN <lv_value> TO <lv_string>.
ENDIF.
lt_message = check_recipients( iv_recipient = <lv_string> ).
IF cl_fdt_services=>contains_eax_message( lt_message ) EQ abap_false.
lo_message->add_recipient( <lv_string> ).
ELSE.
write_trace_info_1 '222' <lv_string> lv_id.
ENDIF.
CLEAR lt_message.
ENDLOOP.
ENDIF.
ENDLOOP.
"lo_message->set_update_task( abap_true ).
IF cl_fdt_function_gen_process=>gv_action_in_update_task EQ abap_true.
lo_message->set_update_task( abap_true ).
ENDIF.
lo_message->send( ).
" Record Trace with the detail of recipient. <PD>
split_recipient_string(
EXPORTING iv_string = lv_rec_trace
IMPORTING ev_msg_1 = lv_msg_1
ev_msg_2 = lv_msg_2
ev_msg_3 = lv_msg_3
ev_msg_4 = lv_msg_4 ).
write_trace_info_4 '221' lv_msg_1 space lv_msg_2 space lv_msg_3 space
lv_msg_4 space.
In case of an exception, convert it into exception type CX_FDT_PROCESSING.
CATCH cx_bcs INTO lx_bcs.
MESSAGE e315(fdt_expressions) INTO ls_message-text.
append_message ls_message lt_message.
RAISE EXCEPTION TYPE cx_fdt_processing
EXPORTING
previous = lx_bcs
mt_message = lt_message.
ENDTRY.
Finally, write the result and process the follow up actions. Actions write their UUIDs as a result. In this case you can copy the following lines. Expressions write values defined with data objects. You may check the
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
53
processing methods of expression type classes such as CL_FDT_CASE or CL_FDT_RANGE to learn more about this.
ro_result = create_and_set_result( iv_timestamp = iv_timestamp ).
process_followup_actions( io_context = io_context
iv_timestamp = iv_timestamp
io_processor = io_processor
io_result = ro_result ).
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
54
11 Methods for Code Generation
11.1 IS_GENERATING For every expression type the method IF_FDT_EXPRESSION~IS_GENERATING has to be implemented.
If the expression type supports generation, the implementation looks as follows:
rv_generating = abap_false.
11.2 GENERATE_PROCESS The generation of an expression (method IF_FDT_EXPRESSION~GENERATE_PROCESS) is only
performed if the method IF_FDT_EXPRESSION~IS_GENERATING returns abap_true. To implement the
generation of an expression, the environment of code generation of BRFplus functions must be understood.
The coding is generated for a complete function. The coding for the complete function [all the used expressions (except the function call expression) and the assigned rulesets] is included in one generated class. In a specific time period, where the function and all used objects (including rulesets) did not change, two generated classes may exist. One class is for processing without lean trace and one with lean trace. The generation occurs when the function is processed and no generation is available for the requested timestamp and lean trace usage. (The generation can be started via tool report).
The generation of a function is started by the BRFplus function object. Each BRFplus object is responsible for the generation of its own functionality. An object using other objects delegates the generation of the respective sub-object. During generation of an expression, it is not determined if the coding is included into the surrounding coding of the parent object or if it will be placed in its own method. Therefore, the generation of each object has to follow some rules so that each code piece can coexist with other code pieces in one method. The coding for an entire function is composed by combining the coding snippets for each used expression into one class (usually into one method). In this case, you must ensure that the variable names for helper variables are unique for the complete generated method. To ensure this, the following technique must be used:
To ensure a unique variable name for each generated data, the declaration method CL_FDT_EXPR_SERVICES=>GEN_GET_NEXT_VARIABLE_NAME is called. This method uses the input string to find a variable name, which is derived from the name and is unique in the generated class (if the string was not used before the string is returned, a number is added which makes the string unique). If the ID of the object is passed for which the helper variable is used, a check is made to determine whether the object is a constant expression. If it is, a check runs to see if a variable for the constant was declared before; if it was, this variable name is returned. Variables for constant expressions are defined as class constants.
At some places a generation manager object is needed. When the method IF_FDT_EXPRESSION~GENERATE_PROCESS of your expression is called an instance of the generation manager is present as an importing parameter and you pass it to the desired parameter list.
If you want to use the lean trace feature your generated coding needs to support this. Respective IF statements in your coding are necessary to distinguish between the coding generated for processing with or without the lean trace feature. The generation infrastructure ensures basic information is added to the trace (such as result) without any expression type or action type specific code. We recommend avoiding trace-specific code in custom action types and expression types.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
55
Code Generation Techniques
Several techniques exist to generate the coding:
1. You can concatenate each line from strings and variable content and append the line into the coding table.
2. You can use a generation template and manipulate it with your own generation tool to obtain the required coding.
3. A tool called code-composer can be used. In BRFplus, there are few places where the code composer is used. You will see more usages of it in older releases (SAP NW 701 and lower).
Recommendation
We recommend using the first technique. This is the one used in the current BRFplus code generation.
We discourage using the code composer (CL_CMP_COMPOSER) based on performance.
The following methods from the BRFplus standard can be used during code generation.
11.2.1 IF_FDT_DATA_OBJECT->GENERATE_CREATE_DATA_REF
This method creates a data statement for a BRFplus data object. It can be used with data objects of type element, structure or table.
Explanation of the parameters:
Parameter Description
IV_VARIABLE_NAME Data object name used in generated coding
IV_TIMESTAMP Generation timestamp
IO_GENERATION_MNGR Generation manager instance
ET_SOURCE_CODE Generated coding
Code Example
Data definition for mail body
lo_mail_body_do->generate_create_data_ref(
EXPORTING iv_variable_name = lv_mail_body_name
iv_timestamp = lv_timestamp
io_generation_mngr = io_generation_mngr
IMPORTING et_source_code = lt_data_def_code ).
APPEND LINES OF lt_data_def_code TO et_source_code.
11.2.2 IF_FDT_DATA_OBJECT=>GENERATE_CONVERT_TO
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
56
This method generates coding to convert the data from one data object to another data object. It can be used with data objects of type element, structure, or table.
Explanation of the parameters:
Parameter Description
IV_VARIABLE_NAME_SOURCE Source data object name used in generated coding
IO_TARGET_DATA_OBJECT Target data object instance
IV_VARIABLE_NAME_TARGET Target data object name used in generated coding
IV_TIMESTAMP Generation timestamp
IO_GENERATION_MNGR Generation manager instance
ET_SOURCE_CODE Generated coding
Code Example
Conversion from temporary result to result:
lo_tmp_result_do->generate_convert_to(
EXPORTING iv_variable_name_source = lv_tmp_result_name
io_target_data_object = lo_result_do
iv_variable_name_target = lv_result_do_name
iv_timestamp = lv_timestamp
io_generation_mngr = io_generation_mngr
IMPORTING et_source_code = lt_conv_code ).
APPEND LINES OF lt_conv_code TO et_source_code.
11.2.3 CL_FDT_EXPR_SERVICES=> GENERATE_MOVE_FROM_EXTERNAL
This method generates the coding to move data from an external format (for example, DDIC) to BRFplus format. The external format can be BRFplus.
Explanation of the parameters:
Parameter Description
IV_DOBJ_ID BRFplus data object ID
IV_SOURCE_NAME Source data object name used in generated coding
IV_TARGET_NAME Target data object name used in generated coding
IV_TIMESTAMP Generation timestamp
IV_USE_DOBJ_NAME_S Use source data object name (optional)
IV_USE_DOBJ_NAME_T Use target data object name (optional)
IV_SOURCE_DDIC_NAME Source DDIC name (optional)
ITS_ID_NAME ID name mapping (optional)
ET_SOURCE_CODE Generated coding
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
57
Code Example
Move from data of an exporting parameter after a static method call:
Cl_fdt_expr_services=>generate_move_from_external(
EXPORTING iv_dobj_id = lv_dobj_id
iv_source_name = iv_source_do_name
iv_target_name = iv_target_do_name
iv_timestamp = lv_timestamp
IMPORTING et_source_code = lt_move_code ).
APPEND LINES OF lt_move_code TO et_source_code.
11.2.4 CL_FDT_EXPR_SERVICES=> GENERATE_MOVE_TO_EXTERNAL
This method generates the coding to move data from BRFplus to an external format. The external format can be BRFplus.
Explanation of the parameters:
Parameter Description
IV_DOBJ_ID BRFplus data object ID
IV_SOURCE_NAME Source data object name used in generated coding
IV_TARGET_NAME Target data object name used in generated coding
IV_TIMESTAMP Generation timestamp
IV_TARGET_DDIC_NAME Target DDIC name (optional)
IV_USE_DOBJ_NAME Use source data object name (optional)
IV_USE_DNAME_IN_TGT Use target data object name (optional)
ITS_ID_NAME ID name mapping (optional)
ET_SOURCE_CODE Generated coding
Code Example
Move from data of an importing parameter after a static method call:
Cl_fdt_expr_services=>generate_move_to_external(
EXPORTING iv_dobj_id = lv_dobj_id
iv_source_name = iv_source_do_name
iv_target_name = iv_target_do_name
iv_timestamp = lv_timestamp
IMPORTING et_source_code = lt_move_code ).
APPEND LINES OF lt_move_code TO et_source_code.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
58
11.2.5 CL_FDT_EXPR_SERVICES=> GENERATE_GET_CONTEXT_NAME
This method returns the variable name for a context object.
Explanation of the parameters:
Parameter Description
IV_ID BRFplus data object ID (should be present in the context )
EV_NAME Data object name
EV_UNKNOWN True: Context object is not known
Code Example
Get the context name for a recipient:
Cl_fdt_expr_services=>generate_get_context_name(
EXPORTING iv_id = lv_recipient_id
IMPORTING ev_name = lv_recipient_name
ev_unknown = lv_unknown ).
11.2.6 CL_FDT_EXPR_SERVICES=> GEN_GET_NEXT_VARIABLE_NAME
This method returns a unique name for a variable.
Explanation of the parameters:
Parameter Description
IV_DESIRED_NAME Desired variable name
RV_EFFECTIVE_NAME Actual created variable name
Code Example
Get the variable name for the email body and create the data declaration:
lv_body_name =
Cl_fdt_expr_services=>generate_get_context_name(iv_desired_name = ‘lv_body’ ).
lv_line = ` DATA ` && lv_body_name && ` TYPE string.`.
append lv_line to et_source_code.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
59
11.2.7 CL_FDT_EXPR_SERVICES=> GEN_GET_NAME_FOR_GENERATION
This method returns the common name for timestamp / trace object used in generation.
Explanation of the parameters:
Parameter Description
IV_SYMBOLIC_NAME Name of the particular object
Trace: GC_ATTR_NAME_TRACE_OBJ
Timestamp: GC_TIMESTAMP_PARA_NAME
RV_GENERATION _NAME Common name used in generation
Code Example
Get the trace object name to clear its work area:
lv_trace_name = Cl_fdt_expr_services=>gen_get_Name_for_generation
(iv_symbolic_name = ‘GC_ATTR_NAME_TRACE_OBJ’ ).
lv_line = ` CLEAR ` && lv_trace_name && `->ms_record-value.
append lv_line to et_source_code.
11.2.8 CL_FDT_EXPR_SERVICES=> GEN_GENERATE_TRACE
This method returns if the generation takes place with lean trace or without.
Explanation of the parameter:
Parameter Description
EV_GENERATE_TRACE TRUE: Generation with lean trace object
Code Example
Ensure generation occurs with the tracing option; clear the record work area:
Cl_fdt_expr_services=>gen_generate_trace(
IMPORTING ev_generate_trace = lv_trace_used )
IF lv_trace_used EQ ABAP_TRUE.
lv_trace_name = Cl_fdt_expr_services=>gen_get_Name_for_generation
(iv_symbolic_name = ‘GC_ATTR_NAME_TRACE_OBJ’ ).
lv_line = ` CLEAR ` && lv_trace_name && `->ms_record-value.
append lv_line to et_source_code.
ENDIF.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
60
11.2.9 CL_FDT_EXPR_SERVICES=>GENERATE_RANGE_PROCESS
This method generates the coding for a used implicit range.
Explanation of the parameters:
Parameter Description
IV_FUNCTION_ID Function ID
IV_EXPRESSION_ID Calling expression ID
IV_VARIABLE_NAME Variable name for the range result (optional)
IV_CREATE_VARIABLE Create a data declaration for the result
IV_TIMESTAMP Generation timestamp
IV_TEST_PARAMETER Test parameter ID
ITS_RANGE Range table
IV_CASE_SENSITIVITY TRUE: Generate case sensitive (optional)
IV_IS_SV_RANGE TRUE: Range is a simple value range (optional)
ET_SOURCE_CODE Generated source code
EV_DEEP_TRACED TRUE: Deep tracing took place
CTS_USED_CONTEXT_ID Table of used context IDs
Code Example
Implicit range for a condition inside an expression:
cl_fdt_expr_services=>generate_range_process(
EXPORTING iv_function_id = iv_function_id
iv_expression_id = mv_id
iv_variable_name = lv_cond_ok
iv_create_variable = abap_false
iv_timestamp = lv_timestamp
iv_test_parameter = <ls_cond>-s_param_range-parameter_id
its_range = <ls_cond>-s_param_range-ts_range
iv_is_sv_range = abap_true
IMPORTING et_source_code = lt_condition_code
CHANGING cts_used_context_id = cts_used_context_id ).
APPEND LINES OF lt_condition_code TO et_source_code.
11.2.10 CL_FDT_EXPR_SERVICES=> GENERATE_SIMPLE_VALUE_PROCESS
This method generates the coding for a direct value usage inside the expression.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
61
Explanation of the parameters:
Parameter Description
IV_VARIABLE_NAME Variable name where the direct value should be stored
IV_CREATE_VARIABLE Create a data declaration for the storage of the direct value (optional)
IS_VALUE Value
IV_TIMESTAMP Generation timestamp
ET_SOURCE_CODE Generated source code
EV_DIRECT_RESULT_NAME Generated class constant name
Code Example
Direct value for a default result inside an expression:
cl_fdt_expr_services=>generate_simple_value_process(
EXPORTING iv_variable_name = lv_def_res_name
iv_create_variable = abap_false
is_value = s_default_result_value
iv_timestamp = lv_timestamp
IMPORTING et_source_code = lt_def_res_code ). APPEND LINES OF lt_def_res_code TO et_source_code.
11.2.11 CL_FDT_EXPRESSION=>GENERATE_GET_VALUE
This method generates the coding for any subexpression or data object used in the expression.
Explanation of the parameters:
Parameter Description
IV_FUNCTION_ID Function ID
IV_ID (Sub)expression or data object ID
IV_VARIABLE_NAME Variable name of result from subexpression OR variable name for the data object
IV_CREATE_VARIABLE Create a data declaration for the result or the data object
IV_TIMESTAMP Generation timestamp
IO_GENERATION_MNGR Generation manager instance
IV_CALLING_EXPR_ID Expression ID (from the calling expression)
IV_TARGET_DO_ID Target data object (optional, triggers a convert_to)
IV_TARGET_ELEMENT_TYPE Target data object element type (optional)
ET_SOURCE_CODE Generated source code
EV_DIRECT_RESULT_NAME Generated class constant name
EV_DEEP_TRACED TRUE: Expression was deep traced
EV_CONTEXT_OR_CONST TRUE: IV_ID belongs to a constant or to a context ID
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
62
CTS_USED_CONTEXT_ID Table of used context IDs
Code Example
Generate parameter code for an email parameter:
generate_get_value( EXPORTING iv_function_id = lv_function_id
iv_id = <ls_para>-parameter_id
iv_variable_name = lv_parm_var_name
iv_create_variable = abap_true
iv_timestamp = lv_timestamp
io_generation_mngr = io_generation_mngr
iv_calling_expr_id = mv_id
IMPORTING et_source_code = lt_parm_code
ev_direct_result_name = lv_direct_parm_name
CHANGING cts_used_context_id = cts_used_context_id ). APPEND LINES OF lt_parm_code TO et_source_code.
11.2.12 CL_FDT_EXPRESSION=> GENERATE_TRACE_SUB_EXPRESSION
This method generates the coding for tracing any subexpressions.
Explanation of the parameters:
Parameter Description
IV_POSITION Trace position
IV_POSITION_NAME Trace position name (optional)
IV_RESULT_NAME Name of result variable
IV_ID Subexpression ID
IV_PARENT_ID Expression ID
IV_WITH_VALUE TRUE: Export trace value to data buffer (optional)
IV_SKIP_TRUE TRUE: Skip all subexpressions with Boolean result, that evaluate to true during runtime (optional)
IV_SKIP_FALSE TRUE: Skip all subexpressions with Boolean result, that evaluate to false during runtime (optional)
IV_IS_SV_RANGE TRUE: Range is a simple value range (optional)
ET_SOURCE_CODE Generated source code
Code Example
Generate trace coding for an embedded procedure call:
IF io_generation_mngr->mv_trace_generation EQ abap_true.
lv_position = <Create a new unique name for this position. For already existing
position names check the attributes of class CL_FDT_TRACE>.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
63
generate_trace_sub_expression(
EXPORTING iv_position_name = lv_trace_position
iv_result_name = lv_result_name
iv_id = <ID of the Procedure Call Expression>
iv_parent_id = mv_id
iv_with_value = abap_true
IMPORTING et_source_code = lt_trace_code ).
APPEND LINES OF lt_trace_code TO et_source_code.
ev_deep_traced = abap_true.
ENDIF.
Note: lv_result_name is the variable, which stores during code generation the name of the variable, which contains in the generated code the result of the Procedure Call.
For more examples, check out the usages of CL_FDT_EXPRESSION=>GENERATE_TRACE_SUB_EXPRESSION in your system.
11.3 Tips for Generation
Recommendation
The names of variables you define need to be unique in the coding. Use method CL_FDT_EXPR_SERVICES=>GEN_GET_NEXT_VARIABLE_NAME to get the final variable name.
Your expression might be called in a loop. Therefore the variables you declared might have a value in the second loop passing. Clear variable if they are not set to a value.
If your expression allows calling other expressions or data objects, ensure the types of the data objects fit the data types you expect or convert the data object to the type you require. (Use method IF_FDT_DATA_OBJECT->GENERATE_CONVERT_TO.)
For reusable methods, see CL_FDT_SERVICES or CL_FDT_EXPR_SERVICES.
Additionally, you can look up package interfaces FDT and FDT_EXTENDED to access all released functionality.
11.4 Tools for supporting the generation development and maintenance
During implementation of this functionality it might be helpful to start the generation directly. You can do this using the report FDT_GENERATION_TOOL. Please check its documentation.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
64
12 Methods for Lean Trace Display
In CL_FDT_EXPRESSION=> GENERATE_TRACE_SUB_EXPRESSION, you created lean trace records for subexpressions used by your expression type. In Testing the Action Type or Expression Type, you will learn about testing your expression and how to save the lean trace. This chapter deals with some methods you can use to manipulate the appearance of your lean trace records in the BRFplus Workbench. You do not have to implement these methods. If you don’t, the lean trace records will be displayed in a standardized way as nodes of an expandible tree by the lean trace Web Dynpro component, FDT_WD_LEAN_TRACE. This component is included in the Show Trace functionality of the function in the BRFplus Workbench or in the Lean Trace Tool. However, if you want to display the lean trace nodes in a specific order or in a structured way, or if you want to extend the description for a particular node, you may create the following:
CHANGE_TRACE_NODE
CREATE_TRACE_CHILD_NODES
12.1 CHANGE_TRACE_NODE This method changes the name of the trace node after its default is set. The interface of the method must contain the following parameters:
Parameter Type Typing Method
Associated Type Description
IO_LEAN_TRACE_HELPER
Importing TYPE REF TO CL_FDT_WD_LEAN_ TRACE_HELPER
Reference to a Lean Trace Helper class
IV_TIMESTAMP Importing TYPE IF_FDT_TYPES=> TIMESTAMP
The execution timestamp of the trace IS_RECORD
IS_RECORD Importing TYPE IF_FDT_LEAN_TRACE=>S_RECORD
One particular record from the trace
CS_NODE Changing TYPE FDTS_WD_TRACE_ MODEL
The UI node created from IS_RECORD
An example for the implementation follows.
Note
You should only change the name of the trace node.
All other fields may be overwritten by the standard methods.
Code Example
METHOD CHANGE_TRACE_NODE.
DATA: lv_name TYPE string.
FIELD-SYMBOLS: <lv_bool> TYPE abap_bool.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
65
TRY.
“Change the name of the node
IF cs_node-value_valueref IS BOUND.
ASSIGN cs_node-value_valueref->* TO <lv_bool>.
IF <lv_bool> = abap_true.
MESSAGE i316(fdt_wd_tools) WITH cs_node-name INTO lv_name.
ELSE.
MESSAGE i317(fdt_wd_tools) WITH cs_node-name INTO lv_name.
ENDIF.
cs_node-name = lv_name.
ENDIF.
CATCH cx_sy_assign_error.
"Keep the node name as it is.
ENDTRY.
ENDMETHOD.
12.2 CREATE_TRACE_CHILD_NODES This method arranges the records traced within your expression in a certain manner. The interface of the method must contain the following parameters:
Parameter Type Typing Method
Associated Type Description
IO_LEAN_TRACE_ HELPER
Importing TYPE REF TO
CL_FDT_WD_LEAN_ TRACE_HELPER
Reference to a Lean Trace Helper class
IS_PARENT_NODE Importing TYPE FDTS_WD_TRACE_ MODEL
Parent node for the child nodes in the UI
ET_NODE Exporting TYPE CL_FDT_WD_LEAN_ TRACE_MODEL=> TS_TRACE_VALUE
Child nodes
CTS_RECORD Changing TYPE IF_FDT_LEAN_TRACE=>TS_RECORD
Records belonging to the parent node
The following example illustrates how to sort nodes and extend their default names.
Note
To create a node, use IO_LEAN_TRACE_HELPER->create_node(…). It contains all relevant fields by default.
To nest nodes, create a node and use it as parent node of another node.
To create nodes without a reference to a record, use the CREATE_NODE( ..) method. To do so, use the IV_NAME attribute instead of IS_RECORD.
Delete all records you have treated in this method from CTS_RECORD.
Code Example
METHOD CREATE_TRACE_CHILD_NODES.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
66
DATA: ls_node TYPE fdts_wd_trace_model,
lt_record TYPE STANDARD TABLE OF if_fdt_lean_trace=>s_record,
lr_record TYPE REF TO if_fdt_lean_trace=>s_record,
lv_name TYPE string,
lv_poscode TYPE string,
lv_token_number TYPE string.
* Operands may not be sorted
LOOP AT cts_record REFERENCE INTO lr_record WHERE
position CS cl_fdt_trace=>gc_poscode_token.
APPEND lr_record->* TO lt_record.
ENDLOOP.
SORT lt_record BY position.
“Sort the table of records in a particular way
* Create nodes and enrich their names
LOOP AT lt_record REFERENCE INTO lr_record.
CLEAR: ls_node.
“Invoke the standard method for node creation
IO_LEAN_TRACE_HELPER->create_node( EXPORTING is_parent_node = is_parent_node
is_record = lr_record->*
IMPORTING es_node = ls_node ).
“Extend the default node name
SPLIT lr_record->position AT gc_separator INTO lv_poscode lv_token_number.
MESSAGE i325(fdt_wd_tools) WITH lv_token_number ls_node-name INTO lv_name.
ls_node-name = lv_name.
APPEND ls_node TO et_node.
“Append new node to exporting table of nodes
DELETE TABLE cts_record FROM lr_record->*.
“Delete the records you have already dealt with
ENDLOOP.
ENDMETHOD.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
67
13 Data Exchange This section explains the methods and the procedure to allow user defined expression types to participate in FDT XML Export / Import for the custom action email.
13.1 EXPORT_XML This method exports the objects in XML format, reads the value from the database using GET methods, and sets it in the XML.
DATA: lv_body TYPE if_fdt_actn_email=>body, lt_recpt TYPE if_fdt_actn_email=>t_recipient,
lv_subject TYPE if_fdt_actn_email=>subject,
lts_msg_parameter TYPE if_fdt_actn_email=>ts_parameter,
lts_rec_parameter TYPE if_fdt_actn_email=>ts_parameter.
super->if_fdt_data_exchange_internal~export_xml( iv_timestamp = iv_timestamp
io_parent = io_parent ).
* Add the Email Recipient Information to xml document.
lt_recpt = if_fdt_actn_email~get_recipients( iv_timestamp ).
convert_abap_to_xml(
io_parent = io_parent
iv_name = 'RECIPIENTS'
iv_namespace_prefix = if_fdt_data_exchange=>gc_xml_namespace_prefix
ia_abap = lt_recpt ).
* Add the Email Body Information to xml document.
lv_body = if_fdt_actn_email~get_body( iv_timestamp ).
convert_abap_to_xml(
io_parent = io_parent
iv_name = 'BODY'
iv_namespace_prefix = if_fdt_data_exchange=>gc_xml_namespace_prefix
ia_abap = lv_body ).
* … and so on for the rest
ENDMETHOD.
There might be elements inside your buffer that are optional. For example, in the case expression, the user can put a simple value as test parameter or an ID that could be a constant. Therefore, within the buffer there will be the simple test value or the ID field filled, but not both. Both XML elements are optional.
In this case, you should put an, “IF… is initial”, statement around the method CONVERT_ABAP_TO_XML.
You will see how to define optional XML elements within your DTD (Implementation of method GET_DTD).
Recommendation
You can shrink the lines of code needed for this method if you make use of a macro such as the following:
DEFINE export_xml.
convert_abap_to_xml( io_parent = io_parent
iv_name = &1
iv_namespace_prefix = gc_my_namespace_prefix
ia_abap = &2 ).
END-OF-DEFINITION.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
68
This might be useful when you have many objects inside your buffer. You would only have to use the following:
export_xml: 'RECIPIENTS' ls_buffer-t_recipient,
'BODY' ls_buffer-body,
'SUBJECT' ls_buffer-subject,
...
You should use your own namespace for the XML elements, as this will prevent possible errors due to
double definition. It is useful to define a global constant (see gc_my_namespace in the previous coding) that
contains your namespace prefix. You can choose whatever you want for a namespace prefix, (for example,
CUST_EXTY1). You have to pass this namespace with exporting parameter es_namespace:
es_namespace-prefix = gc_my_namespace_prefix.
es_namespace-uri = if_fdt_data_exchange=>gc_xml_namespace_uri.
For the namespace URI, you can use the default BRFplus namespace URI.
13.2 IMPORT_XML This method reads the XML file and imports the objects from XML. It reads the value from XML and sets the database using SET methods. After you set one XML object to ABAP, you must raise the event
if_fdt_data_exchange_internal~object_id_as_attribute with the corresponding ID as export
parameter iv_id. You don’t have to do this, when importing objects without an ID (for example, a simple
value of type cl_fdt_expr_sv=>s_value).
DATA: lv_no_element TYPE abap_bool,
ls_msg_parameter TYPE if_fdt_actn_email=>s_parameter,
ls_rec_parameter TYPE if_fdt_actn_email=>s_parameter,
ls_buffer TYPE s_buffer.
super->if_fdt_data_exchange_internal~import_xml( io_parent ).
* Import the Email Body from XML
convert_xml_to_abap( EXPORTING io_parent = io_parent
iv_name = 'BODY'
iv_namespace_prefix = gc_my_namespace_prefix
IMPORTING ea_abap = ls_buffer-body
ev_no_element = lv_no_element ).
IF lv_no_element EQ abap_true.
ls_msg-msgv1 = cx_fdt=>get_object_description( mv_id ).
* Message: No &1 supplied for &2
MESSAGE x302(fdt_core) WITH 'BODY' ls_msg-msgv1 INTO ls_msg-text.
message_exception ls_msg lt_msg cx_fdt_input.
ENDIF.
* Import the Email Recipients from XML
convert_xml_to_abap( EXPORTING io_parent = io_parent
iv_name = 'RECIPIENTS'
iv_namespace_prefix = gc_my_namespace_prefix
IMPORTING ea_abap = ls_buffer-t_recipient
ev_no_element = lv_no_element ).
IF lv_no_element EQ abap_true.
ls_msg-msgv1 = cx_fdt=>get_object_description( mv_id ).
* Message: No &1 supplied for &2
MESSAGE x302(fdt_core) WITH 'RECIPIENTS' ls_msg-msgv1 INTO ls_msg-text.
message_exception ls_msg lt_msg cx_fdt_input.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
69
ENDIF.
* Import the Email Subject from XML
convert_xml_to_abap( EXPORTING io_parent = io_parent
iv_name = 'SUBJECT'
iv_namespace_prefix = gc_my_namespace_prefix
IMPORTING ea_abap = ls_buffer-subject
ev_no_element = lv_no_element ).
IF lv_no_element EQ abap_true.
ls_msg-msgv1 = cx_fdt=>get_object_description( mv_id ).
* Message: No &1 supplied for &2
MESSAGE x302(fdt_core) WITH 'SUBJECT' ls_msg-msgv1 INTO ls_msg-text.
message_exception ls_msg lt_msg cx_fdt_input.
ENDIF.
* Set the paramters
convert_xml_to_abap( EXPORTING io_parent = io_parent
iv_name = 'MESSAGE_PARAMETERS'
iv_namespace_prefix = gc_my_namespace_prefix
IMPORTING ea_abap = ls_buffer-ts_message_parameter
ev_no_element = lv_no_element ).
IF lv_no_element EQ abap_true.
ls_msg-msgv1 = cx_fdt=>get_object_description( mv_id ).
* Message: No &1 supplied for &2
MESSAGE x302(fdt_core) WITH 'MESSAGE_PARAMETERS' ls_msg-msgv1 INTO ls_msg-text.
message_exception ls_msg lt_msg cx_fdt_input.
ENDIF.
convert_xml_to_abap( EXPORTING io_parent = io_parent
iv_name = 'RECIPIENT_PARAMETERS'
iv_namespace_prefix = gc_my_namespace_prefix
IMPORTING ea_abap = ls_buffer-ts_rec_parameter
ev_no_element = lv_no_element ).
IF lv_no_element EQ abap_true.
ls_msg-msgv1 = cx_fdt=>get_object_description( mv_id ).
* Message: No &1 supplied for &2
MESSAGE x302(fdt_core) WITH 'RECIPIENT_PARAMETERS' ls_msg-msgv1 INTO ls_msg-text.
message_exception ls_msg lt_msg cx_fdt_input.
ENDIF.
* Import the IDs first.
LOOP AT ls_buffer-ts_message_parameter INTO ls_msg_parameter.
RAISE EVENT if_fdt_data_exchange_internal~object_id_as_attribute
EXPORTING iv_id = ls_msg_parameter-parameter_id.
ENDLOOP.
LOOP AT ls_buffer-ts_rec_parameter INTO ls_rec_parameter.
RAISE EVENT if_fdt_data_exchange_internal~object_id_as_attribute
EXPORTING iv_id = ls_rec_parameter-parameter_id.
ENDLOOP.
set_buffer( is_buffer = ls_buffer ).
notify_change( ).
ENDMETHOD.
You could make use of an appropriate macro here as well.
As shown in the previous code, you should implement corresponding exception handling after each element in case a mandatory XML element is not supplied within the XML.
Therefore, you can retrieve the parameter EV_NO_ELEMENT, which indicates the status of the element in
the XML ( abap_false ) or ( abap_true ).
You do not need this error handling in case of optional XML elements because their occurrence inside the XML is not mandatory.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
70
13.3 Implementation for User Defined Expression Types
This section enables user-defined expression types to participate in FDT XML export / import for the custom action email.
To participate in XML export / import, you need to create another class that implements the interface IF_FDT_DATA_EXCHANGE_EXTERNAL.
The following sections explain the methods in the interface that need to be re-defined.
13.3.1 GET_DTD
Since the implementation of method GET_DTD requires a deeper understanding of how a DTD has to be built, this chapter focuses on another example in which the different elements of DTD are detailed.
The purpose of this method is to define DTD for User Defined Expressions used for validating the XML during import. For BRFplus internal expression types and action types, the DTD is defined in the XSLT transformation, FDT_APPEND_INTERNAL_DTD. It might be useful to look at this transformation while developing method GET_DTD of your own expression type. You can see a simple expression type (CASE ) is defined. This might help you understand how to build the DTD for your expression type. The tricky part for a custom expression type is that you cannot update the BRFplus internal XSLT transformation to work with your expression type. You must create the DTD definition manually with coding. This task is completed by method GET_DTD.
Your expression type has two parameters with IDs, PARAMETER1 and “PARAMETER2; one simple value
(type CL_FDT_EXPR_SV=>S_VALUE) called SIMPLE_VALUE, and one table with multiple lines called
DEMO_TABLE. (The line type of this table is not important since it should be defined the same way as, for example, the structure SIMPLE_VALUE is defined). Concerning the BRFplus default XSLT transformation, you can figure out that the DTD has been defined as follows:
<!ELEMENT CUST_EXTY1:PARAMETER1 (#PCDATA)>
<!ELEMENT CUST_EXTY1:PARAMETER2 (#PCDATA)>
<!ELEMENT CUST_EXTY1:SIMPLE_VALUE (CUST_EXTY1:ELEMENT_TYPE?, CUST_EXTY1:VALUE?,
CUST_EXTY1:SUPPLEMENT?,
CUST_EXTY1:NONE_AP?)>
<!ELEMENT CUST_EXTY1:DEMO_TABLE (CUST_EXTY1:ITEM| CUST_EXTY1:item)*>
You can see how the structure of your XML is defined.
In the XML, there will be one XML element called PARAMETER1, which has its ID embedded. It has no deeper structure. Therefore the embedded data inside this XML element is defined as #PCDATA.
The same applies to PARAMETER2.
The simple value is different because the SIMPLE_VALUE XML element consists of several other XML elements since its type is CL_FDT_EXPR_SV=>S_VALUE.
These are as follows:
o ELEMENT_TYPE
o VALUE
o SUPPLEMENT
o NONE_AP
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
71
The interrogation symbol (?) indicates that this element is optional in the XML. All sub-elements are separated by a comma, which simply means they occur in sequence.
The DEMO_TABLE is different since it has the same element embedded multiple times (in this case, ITEM). The structure of the ITEM XML element could be defined again, for example:
<!ELEMENT CUST_EXTY1:ITEM (CUST_EXTY1:ELEMENT_TYPE?, CUST_EXTY1:VALUE?,
CUST_EXTY1:SUPPLEMENT?, CUST_EXTY1:NONE_AP?)>
<!ELEMENT CUST_EXTY1:item (CUST_EXTY1:ELEMENT_TYPE?, CUST_EXTY1:VALUE?,
CUST_EXTY1:SUPPLEMENT?, CUST_EXTY1:NONE_AP?)>
You would have modeled a DTD for a table, DEMO_TABLE, with line type simple value. Remember that your elements consist of sub-elements that you must define.
<!ELEMENT CUST_EXTY1:ELEMENT_TYPE (#PCDATA)>
<!ELEMENT CUST_EXTY1:VALUE (#PCDATA)>
<!ELEMENT CUST_EXTY1:SUPPLEMENT (#PCDATA)>
<!ELEMENT CUST_EXTY1:NONE_AP (#PCDATA)>
If you compare the sub-XML elements of DEMO_TABLE to the ones in SIMPLE_VALUE, you will notice that they are not separated by a comma but by a |. The separator sign defines the grouping of the sub-elements, whereas the | defines a choice and the comma defines a sequence. Within the XML, the iterating sub-XML-element of DEMO_TABLE could be ITEM (upper case) or item (lower case).
The occurrence sign differs as well from SIMPLE_VALUE since it is a “*” meaning the element (either ITEM or item) can occur never, once, or multiple times inside XML element DEMO_TABLE.
As previously mentioned, you must create this definition manually with ABAP/4 code.
Method GET_DTD supplies two important objects in order to define the DTD, namely:
IO_CONTENT_PARTICLE Type ref to IF_IXML_CONTENT_PARTICLE
IO_DOC_TYPE Type ref to IF_IXML_DOCUMENT_TYPE
If you are looking at the structure of an element inside the transformation, it can be defined as embedded particles.
The outer particle is:
<!ELEMENT CUST_EXTY1:PARAMETER1
. . . >
It can be split into a left particle (the red one) and a right element description (the blue one). Initially, we will focus on the left particle.
The underlined elements of this particle are split into a prefix and a name. You can create this particle with the following code:
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
72
DATA: lo_outer_cp TYPE REF TO if_ixml_content_particle.
lo_outer_cp = io_doc_type->create_content_particle( name = 'PARAMETER1'
prefix = cl_fdt_actn_email=>gc_my_namespace_prefix
grouping = if_ixml_content_particle=>co_sequence
occurrence = if_ixml_content_particle=>co_one ).
Recommendation
We use namespace to avoid ambiguity and also if you define the same element twice in XSLT,
then the complete validation fails. To avoid ambiguity, we recommend using customer-specific namespace.
.
The grouping and occurrence parameters will be explained later. For this first particle they should always be
co_sequence and co_one. After having created this particle, you have to add it as content to the main particle which you get as an import parameter:
io_content_particle->add_content_particle( content_particle = lo_outer_cp ).
This is all you have to do for the left particle. For the right particle, you have to create an element description object. You can do this with the following code:
DATA: lo_element_descr TYPE REF TO if_ixml_element_decl.
* Define the content of each XML element used
lo_element_descr = io_doc_type->create_element_decl(
name = 'PARAMETER1'
prefix = cl_fdt_actn_email=>gc_my_namespace_prefix ).
lo_element_descr->set_content_spec( content_spec
= if_ixml_element_decl=>co_mixed ).
lo_element_descr->set_grouping( grouping
= if_ixml_content_particle=>co_sequence ).
lo_element_descr->set_occurrence( occurs
= if_ixml_content_particle=>co_one ).
You must pass the correct name and prefix corresponding to your left particle.
First, you create the content for this object. The main particle embedded as element description is the surrounding brackets.
<!ELEMENT CUST_EXTY1:PARAMETER1 (#PCDATA)>
To achieve this, you have to create an empty particle and add it as content to the element description object.
DATA: lo_cp_brackets TYPE REF TO if_ixml_content_particle.
* -> create anonymous content (brackets-)particle: (...)
lo_cp_brackets = io_doc_type->create_content_particle(
name = ''
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
73
grouping = if_ixml_content_particle=>co_sequence
occurrence = if_ixml_content_particle=>co_one ).
Now you can create the content particle that is embedded inside the brackets-particle for PARAMETER1.
DATA: lo_cp_element TYPE REF TO if_ixml_content_particle.
lo_cp_element = io_doc_type->create_content_particle(
name = '#PCDATA'
grouping = if_ixml_content_particle=>co_sequence
occurrence = if_ixml_content_particle=>co_one ).
Note
If you do not pass a “prefix” parameter the particle is created with its name (in this example, #PCDATA).
Now add LO_CP_ELEMENT as content to LO_CP_BRACKETS, which will be added as content to LO_ELEMENT_DESCR.
lo_cp_brackets->add_content_particle( content_particle = lo_cp_element ).
lo_element_descr->add_content_particle( content_particle = lo_cp_brackets ).
Now that you are finished with the element description (with #PCDATA embedded), you can proceed to add the element description as child to the IO_CONTENT_PARTICLE.
io_doc_type->append_child( new_child = lo_element_descr ).
If you look at the SIMPLE_VALUE, you must add not only one particle (#PCDATA) to the brackets-particle but actually four other particles. You have to set the grouping signs and occurrence signs appropriately.
For the XML element SIMPLE_VALUE, the coding is as follows:
lo_outer_cp = io_doc_type->create_content_particle(
name = 'SIMPLE_VALUE’
prefix = cl_fdt_actn_email=>gc_my_namespace_prefix
grouping = if_ixml_content_particle=>co_sequence
occurrence = if_ixml_content_particle=>co_one ).
io_content_particle->add_content_particle( content_particle = lo_outer_cp ).
lo_element_descr = io_doc_type->create_element_decl(
name = 'SIMPLE_VALUE'
prefix = cl_fdt_actn_email=>gc_my_namespace_prefix ).
lo_element_descr-
>set_content_spec( content_spec = if_ixml_element_decl=>co_mixed ).
lo_element_descr-
>set_grouping( grouping = if_ixml_content_particle=>co_sequence ).
lo_element_descr-
>set_occurrence( occurs = if_ixml_content_particle=>co_one ).
* -> create anonymous content (brackets-)particle: (...)
lo_cp_brackets = io_doc_type->create_content_particle(
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
74
name = ''
grouping = if_ixml_content_particle=>co_sequence
occurrence = if_ixml_content_particle=>co_one ).
lo_cp_element = io_doc_type->create_content_particle(
name = 'ELEMENT_TYPE'
prefix = cl_fdt_actn_email=>gc_my_namespace_prefix
grouping = if_ixml_content_particle=>co_sequence
occurrence = if_ixml_content_particle=>co_zero_or_one ).
lo_cp_brackets->add_content_particle( content_particle = lo_cp_element ).
lo_cp_element = io_doc_type->create_content_particle(
name = 'VALUE'
prefix = cl_fdt_actn_email=>gc_my_namespace_prefix
grouping = if_ixml_content_particle=>co_sequence
occurrence = if_ixml_content_particle=>co_zero_or_one ).
lo_cp_brackets->add_content_particle( content_particle = lo_cp_element ).
lo_cp_element = io_doc_type->create_content_particle(
name = 'SUPPLEMENT'
prefix = cl_fdt_actn_email=>gc_my_namespace_prefix
grouping = if_ixml_content_particle=>co_sequence
occurrence = if_ixml_content_particle=>co_zero_or_one ).
lo_cp_brackets->add_content_particle( content_particle = lo_cp_element ).
lo_cp_element = io_doc_type->create_content_particle(
name = 'NONE_AP'
prefix = cl_fdt_actn_email=>gc_my_namespace_prefix
grouping = if_ixml_content_particle=>co_sequence
occurrence = if_ixml_content_particle=>co_zero_or_one ).
lo_cp_brackets->add_content_particle( content_particle = lo_cp_element ).
lo_element_descr->add_content_particle( content_particle = lo_cp_brackets ).
io_doc_type->append_child( new_child = lo_element_descr ).
After defining the structure of the SIMPLE_VALUE XML element, you then define the sub-elements. As this is done the same way as with the definition of PARAMETER1 (element description, #PCDATA) the appropriate coding is not covered here.
Recommendation
The order of defining the XML-elements is relevant and you must adhere to the same sequence they are exported via method EXPORT_XML.
The following tables show the outcome of each constant used for grouping and occurrence:
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
75
Grouping
Constant Result if_ixml_content_particle=>co_sequence Appended separator for this particle is a
comma (CUST_EXTY1:ELEMENT_TYPE?, )
if_ixml_content_particle=>co_choice Appended separator for this particle is a | (CUST_EXTY1:ITEM| CUST_EXTY1:item )
Occurrence
Constant Result if_ixml_content_particle=>co_one The corresponding XML element occurs
only once in the XML (#PCDATA nothing is appended)
if_ixml_content_particle=>co_one_or_more The corresponding XML element occurs one or multiple times in the XML (CUST_EXTY1:ELEMENT_TYPE+,)
if_ixml_content_particle=>co_zero_or_more The corresponding XML element occurs zero or multiple times in the XML ( (CUST_EXTY1:ITEM| CUST_EXTY1:item)* here the occurrence is set in the bracket particle)
if_ixml_content_particle=>co_zero_or_one The corresponding XML element occurs zero or once (optional XML element) in the XML (CUST_EXTY1:ELEMENT_TYPE?,)
13.3.2 GET_ELEMENT_DOMAIN_LIST
This method defines the domain list of an element. This method must be redefined when the element is bound to DDIC; the value help can read from the domain values.
METHOD if_fdt_data_exchange_external~get_element_domain_list.
DATA: ls_element_domain TYPE if_fdt_data_exchange_external=>s_element_domain,
lt_element_domain TYPE if_fdt_data_exchange_external=>t_element_domain.
Define the DDIC element to which the element ACTIVITY is bounded
ls_element_domain-node_name = 'ABC:RECIPIENT'.
ls_element_domain-type = 'if_fdt_actn_email=>t_recipient'.
INSERT ls_element_domain INTO TABLE et_element_domain_list.
ls_element_domain-node_name = 'ABC:BODY'.
ls_element_domain-type = 'if_fdt_actn_email=>body'.
INSERT ls_element_domain INTO TABLE et_element_domain_list.
ls_element_domain-node_name = 'ABC:SUBJECT'.
ls_element_domain-type = 'if_fdt_actn_email=>subject'.
INSERT ls_element_domain INTO TABLE et_element_domain_list.
ENDMETHOD.
The output parameter for this method is ET_ELEMENT_DOMAIN_LIST. The method lists the domain or type
of user defined XML element.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
76
13.3.3 GET_VERSION_CHANGE_LIST
This method maintains the changes in different XML versions. The name and structure of XML elements can change in different versions, which may prevent the import of older versions. This method allows you to track the changes in different versions, and know which XML version to use.
We need to define this method for any changes and add values to ET_VERSION_CHANGE. During runtime, we call this method to determine the objects for which the modification needs to be made, and if not defined then the modification is skipped.
For example, if the changes were made for expression type email, we need to add for that version.
METHOD if_fdt_data_exchange_external~get_version_change_list.
DATA: ls_version_change TYPE if_fdt_data_exchange_external=>s_xml_version_change,
ls_message TYPE if_fdt_types=>s_message.
ls_version_change-xml_version = '1.07'. " This should be FDT XML Version
ls_message-msgty = 'I'.
ls_message-text = 'Version 1.07 has Recipient, Body and Subject
of Action Email added '.
INSERT ls_message INTO TABLE ls_version_change-messages.
INSERT ls_version_change INTO TABLE et_version_change.
ENDMETHOD.
The output parameter for this method is ET_VERSION_DOMAIN_LIST. The method lists the domain or type
of user defined XML element.
13.3.4 MODIFY_XML_VERSION_CHANGES
This method transforms one version to another, where the version is updated (1.06 to 1.07) or downgraded (1.07 to 1.06).
This method allows you to adhere to the XML downgrade and upgrade from different releases. For example, if your previous release has Element-Recipient that was changed to Recipient(s), you need to delete the old element and insert new one (recipients) so the DTD is validated correctly. The value of the new element is defaulted; vice versa for downgrade.
In our example, if the Recipient changes from a single value to a multiple value, then the changes would be as follows:
METHOD if_fdt_data_exchange_external~modify_xml_version_changes.
DATA: lo_ixml TYPE REF TO if_ixml,
lo_document TYPE REF TO if_ixml_document,
lo_node TYPE REF TO if_ixml_node,
lo_ref_element TYPE REF TO if_ixml_element,
lo_new_element TYPE REF TO if_ixml_element,
lo_element TYPE REF TO if_ixml_element,
lv_value TYPE string,
ls_value TYPE if_fdt_actn_email=>recipient,
lt_value TYPE if_fdt_actn_email=>t_recipient.
lo_ixml = cl_ixml=>create( ).
lo_document = lo_ixml->create_document( ).
IF iv_from_version LT iv_to_version.
* Case of Version Upgradation
* For Version 1.06 -> 1.07 upgradation.
IF iv_from_version EQ '1.01'
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
77
AND iv_to_version EQ '1.02'.
lo_element = io_parent->find_from_name( name = 'RECIPIENTS'
namespace = 'ABC' ).
lt_value = lo_element->get_value( ).
* Perform user defined logic which might be required during transformation
LOOP AT lt_value INTO ls_value.
lv_value = la_value-recipient.
RETURN.
ENDLOOP.
CALL METHOD cl_fdt_maintenance=>convert_abap_to_xml
EXPORTING
iv_name = 'RECIPIENT'
ia_abap = lv_value
iv_namespace_prefix = 'ABC'
IMPORTING
eo_element = lo_new_element.
io_parent->replace_child( new_child = lo_new_element
old_child = lo_element ).
ENDIF.
ELSEIF iv_from_version GT iv_to_version.
* For Version 1.02 -> 1.01 downgradation.
IF iv_from_version EQ '1.02' AND
iv_to_version EQ '1.01'.
CALL METHOD cl_fdt_maintenance=>convert_xml_to_abap
EXPORTING
io_parent = io_parent
iv_name = 'RECIPIENT'
iv_namespace_prefix = 'ABC'
IMPORTING
ea_abap = lv_value.
* Perform user defined logic which might be required during transformation
CALL METHOD cl_fdt_maintenance=>convert_abap_to_xml
EXPORTING
iv_name = 'RECIPIENTS'
ia_abap = lt_value
iv_namespace_prefix = 'ABC'
IMPORTING
eo_element = lo_new_element.
io_parent->append_child( lo_new_element ).
io_parent->remove_child( lo_element ).
ENDIF.
ENDMETHOD.
The input parameters for this method are as follows:
IO_PARENT Provides a table of the object IDs for export
IV_FROM_VERSION Lower boundary BRFplus XML version in version change
IV_TO_VERSION Upper boundary BRFplus XML version in version change
The output parameter is as follows:
ET_MESSAGE
Provides a list of failure and success messages
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
78
14 Creation of the Action Type or Expression Type as an Object in BRFplus
When the development of the class and the interface is completed, the new action type or expression type needs to be created as a BRFplus object in the BRFplus workbench. You can do this from the context menu in the repository tree on the application in which you want to create it.
FDT_SYSTEM is the application for all action types and expression types in the BRFplus standard.
Procedure
1. Create your own transportable application (like FDT_SYSTEM).
2. Create your own action types and expression types.
3. Change the Access Level to Global so the action type or expression type can be used by objects from other applications.
4. Enter class and application name and make it an action.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
79
5. Set the transport objects and create some meaningful texts.
For more information, see chapter 4 Transport Objects.
6. For email action type, you can add a constant to IF_FDT_ACTN_EMAIL with the ID of the type (see general data section).
Note
For most action types and expression types you can find constants in IF_FDT_CONSTANTS.
Transaction FDT_RESERVED gives an overview of UUIDs included into the BRFplus shipment and reserved UUIDs.
You cannot and you do not have to change those objects.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
80
15 Testing the Action Type or Expression Type
Having a test report or even a unit test is a good idea. One is created for our example email action type and used to debug the new development.
REPORT fdt_demo_report_actn_email.
DATA: lo_factory TYPE REF TO if_fdt_factory,
lo_email TYPE REF TO if_fdt_actn_email,
lo_function TYPE REF TO if_fdt_function,
lt_message TYPE if_fdt_types=>t_message,
lo_result TYPE REF TO if_fdt_result,
lr_data TYPE REF TO data,
lv_body TYPE if_fdt_actn_email=>body,
lv_recipient TYPE if_fdt_actn_email=>recipient,
lt_recipient TYPE if_fdt_actn_email=>t_recipient,
lv_subject TYPE if_fdt_actn_email=>subject,
lo_trace TYPE REF TO if_fdt_trace,
lo_lean_trace TYPE REF TO if_fdt_lean_trace.
lo_factory = cl_fdt_factory=>if_fdt_factory~get_instance(
if_fdt_constants=>gc_application_tmp ).
* create the email action
lo_email ?= lo_factory->get_expression(
iv_expression_type_id = if_fdt_actn_email=>gc_exty_email ).
lo_email->if_fdt_transaction~enqueue( ).
lo_email->if_fdt_admin_data~set_name( 'DEMO_ACTN_MESSAGE_LOG' ).
* the email action specific attributes
lv_recipient = '[email protected]'.
APPEND lv_recipient TO lt_recipient.
lo_email->set_recipients( lt_recipient ).
lv_subject = 'This is a test of an email action'.
lo_email->set_subject( lv_subject ).
lv_body = 'This is the first test report. When you can see you email it works.'.
lo_email->set_body( lv_body ).
* make the email action the top expression (most simple test)
lo_function ?= lo_factory->get_function( ).
lo_function->if_fdt_transaction~enqueue( ).
lo_function->if_fdt_admin_data~set_name( cl_fdt_services=>get_unique_name( ) ).
lo_function->set_expression( lo_email->mv_id ).
* activate and save function + email action
lo_function->if_fdt_transaction~activate( EXPORTING iv_deep = abap_true
IMPORTING et_message = lt_message ).
IF lt_message IS NOT INITIAL.
BREAK-POINT.
RETURN. ">>>
ENDIF.
lo_function->if_fdt_transaction~save( iv_deep = abap_true ).
lo_function->if_fdt_transaction~dequeue( iv_deep = abap_true ).
lo_function->process( EXPORTING iv_trace_mode = if_fdt_constants=>gc_trace_mode_lean
IMPORTING eo_result = lo_result
eo_trace = lo_trace ).
lo_lean_trace ?= lo_trace.
lo_lean_trace->save( ).
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
81
16 Creation of the User Interface
Prerequisites
To create your own user interfaces for expression types and action types you have a basic understanding about Web Dynpro (WD) ABAP. Tutorials and videos about WD ABAP are available in SCN.
16.1 Overview about the Components of the User Interface
The following table provides an overview of the components of the user interface for custom action types and expression types. They are discussed in more detail in subsequent chapters.
Component Description Web Dynpro Component
The WD component visualizes and controls (react on user actions) the UI. It is the Controller and View of the well-known MVC pattern. The WD component is designed to display the detail part of the expression type/action. The general and toolbar sections are provided by the BRFplus UI framework. The lifecycle and transactional functionalities arehandled by this framework.
The WD component implements two WD interfaces that control the lifecycle of the component.
Model Class
The model class is the connector between the UI and the backend. It represents the model entity of the MVC pattern. It is a standard ABAP OO-class that inherits from CL_FDT_WD_MAINTENANCE_MODEL and retrieves and updates the data that are maintained in the UI. The data are stored in model nodes, which are technically structured WD context nodes. The model class stores the translatable text elements used in dynamic UI screens.
UI Class
The UI OO class determines the model class and the WD component for an expression type/action (Registry). It ensures that customer expression types/actions are protected against future enhancements of the BRFplus UI framework through versioning.
Services
The UI Infrastructure provides various services you use in your UI. One major service is the query component that enables the user to select BRFplus objects. You need this component if your expression type/object refers to other BRFplus objects.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
82
16.2 Web Dynpro Component
Note
Since the shipped WD component for the email action type creates UI parts during runtime (dynamically), the component is not discussed, but those parts relevant to understand the framework. Some features are discussed without referring to the implementation of the email action type to make this tutorial easier to understand.
16.2.1 Implementation of the BRFplus WD Interfaces
The WD component has to reimplement the following WD interfaces in the component definition:
FDT_IWD_COMPONENT
FDT_IWD_OBJECT
FDT_IWD_OBJECT_EXTENDED
FDT_IWD_QUERY
The methods, which have to be implemented, are discussed in detail later.
16.2.2 Definition of the Context
The context of the WD component consists of the model context nodes (root node MODEL) and the node UI_ADJUSTMENT, which is provided by the interface FDT_IWD_OBJECT.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
83
Model Nodes
Model nodes are actually data containers filled in the model class by accessing the backend.
There is a root node, MODEL, under which all model nodes are stored. You have to create this root node for each of your action or expression type WD components.
Each model node (RECIPIENTS) is bound to a DDIC structure (check the FDTS_WD_ACTN_EMAIL_RECP_MODEL and FDTS_WD_ACTN_EMAIL_MSG_MODEL structures to get an idea), which defines its attributes. The cardinality of the node determines if the data are a collection (table) or a flat structure. The model nodes should be defined semantically. That means the attributes that belong together should be part of one model node. Usually those attributes are displayed together in a WD view.
UI Adjustment Node
The UI_ADJUSTMENT node is provided by the UI framework. The attributes read_only and enabled must be bound to the corresponding attributes of the UI elements. This has the advantage that these UI elements are set to read-only or disabled if the user is not allowed to maintain the content. This is the case, when the object is visualized in display mode.
The readOnly attribute of the recipient input field in the WD view is bound to the read_only attribute of the UI_ADJUSTMENT node.
16.2.3 Exception Handling in the UI
In the WD environment, we cannot use CX_FDT (and its subclasses) because the UI layer in BRF has an exception hierarchy, which cannot be used in method signatures (unchecked exceptions) due to history reasons. There is an exception class hierarchy for the WD UI. Its root class is CX_FDT_WD. If you have to propagate a backend exception (CX_FDT and subclasses), you have to convert the backend exception to CX_FDT_WD_BACKEND.
Method CL_FDT_WD_SERVICE=>CONVERT_BACKEND_EXCEPTION does the conversion.
If you call a method that can raise CX_FDT (and its subclasses) ensure you either report the exception to the user or propagate it correctly.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
84
IF_FDT_WD_MODEL~INIT (Model Class)
DATA: lx_fdt TYPE REF TO cx_fdt.
***************************************************************************************
TRY.
*call super
super->if_fdt_wd_model~init( io_context_node = io_context_node
io_controller = io_controller ).
*get action message application log
mo_actn_msg_log ?= mo_factory->get_expression( iv_id = mv_id ).
CATCH cx_fdt INTO lx_fdt.
cl_fdt_wd_service=>convert_backend_exception( lx_fdt ).
ENDTRY.
Now you may catch this exception in the WD component for example and react on it.
Reacting on Exceptions
You have to catch all possible exceptions properly. Depending on the exceptions, you can do one of the following:
Try to react by providing a workaround
Example: If the user cannot edit an object because it is locked you can try to display it.
Within the WD component, do not catch exceptions if they are part of the signature in an interface method.
Example: All maintenance UI WD components are implementing the interface FDT_IWD_OBJECT. These methods have the exception cx_fdt_wd in its signature and are handled by the object manager.
In the majority of cases, you probably cannot react and want to display the exception (error text) to the user. This can be accomplished by calling the method CL_FDT_WD_SERVICE=>REPORT_EXCEPTION.
It provides two parameters:
o IX_EXCEPTION for reporting backend exceptions
o IX_WD_EXCEPTION for UI exceptions
Method in a WD caller, calling a method in the model class and handling exception properly.
TRY.
wd_this->mo_model->refresh_model( ). “->may raise cx_fdt_wd
CATCH cx_fdt_wd_backend INTO lx_fdt_backend.
lo_msg_manager = wd_this->wd_get_api( )->get_message_manager( ).
cl_fdt_wd_service=>report_exception( io_message_manager = lo_msg_manager
ix_wd_exception = lx_fdt_backend ).
ENDTRY.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
85
16.2.4 Implementation of Interface FDT_IWD_OBJECT/FDT_IWD_COMPONENT
The following methods make use of additional attributes and methods seen in the Attributes tab from the COMPONENTCONTROLLER for the WD component FDT_WD_ACTN_EMAIL.
INIT
This method is called by the framework, after the WD component is created.
METHOD INIT .
wd_this->mo_model = io_model.
wd_this->mo_state = io_state.
wd_this->mv_id = iv_id.
wd_this->mo_model->init( io_context_node = wd_context
io_controller = wd_this->wd_get_api( ) ).
wd_this->mo_model_cl ?= io_model.
wd_this->gc_usage_type_message = 'USAGE_MESSAGE_VAR'.
wd_this->gc_usage_type_rec = 'USAGE_REC_VAR'.
DATA: lo_context_node TYPE REF TO if_wd_context_node,
ls_rec TYPE wd_this->element_recipients,
lv_string TYPE string.
lo_context_node = wd_context->path_get_node( path = `MODEL.RECIPIENTS` ).
lo_context_node->get_static_attributes( IMPORTING static_attributes = ls_rec ).
ls_rec-rec_1_req = abap_true.
ls_rec-rec_1_name = wd_this->mo_model-
>if_wd_component_assistance~get_text( key = '001' ).
ls_rec-rec_2_req = abap_true.
ls_rec-rec_2_name = wd_this->mo_model-
>if_wd_component_assistance~get_text( key = '001' ).
ls_rec-rec_3_req = abap_true.
ls_rec-rec_3_name = wd_this->mo_model-
>if_wd_component_assistance~get_text( key = '001' ).
ls_rec-rec_4_req = abap_true.
ls_rec-rec_4_name = wd_this->mo_model-
>if_wd_component_assistance~get_text( key = '001' ).
lo_context_node->bind_structure( EXPORTING new_item = ls_rec ).
ENDMETHOD.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
86
It provides the following parameters:
Parameter Type Purpose
IV_ID Importing ID of the object
IO_MODEL Importing The instance of the model (described later in this document)
IO_STATE Importing The instance of the state representation (described later in this document)
CX_FDT_WD Exception Initialization failed
You should set those parameters into attributes of your component controller.
DISPLAY
The purpose of this method is to notify that the object should be displayed.
METHOD DISPLAY .
wd_this->mo_model->get_model( ).
wd_this->create_toolbar( ).
ENDMETHOD.
Usually, you call the GET_MODEL( ) of your model to get the model from the backend. This method retrieves the data from the backend and provides it by supplying the corresponding model nodes.
Sometimes, you do not want to retrieve the complete data from the backend initially but only if the data needs to be visualized. For example, if you have different tabs in your view. Therefore, instead of calling GET_MODEL( ), you can call GET_MODEL_BY_NODE by providing the name of a model node. In this case, only the provided node is filled with data from the backend.
This method can throw CX_FDT_WD.
EDIT
This method notifies that the object should be edited. Do not enqueue the object since this is done by the UI framework.
METHOD edit .
wd_this->mo_model->get_model( ).
wd_this->create_toolbar( ).
ENDMETHOD.
Please check the previous section concerning the model access.
This method can throw CX_FDT_WD.
SAVE
This method notifies that the object should be saved/synchronized in a consistent state to the backend. The UI component/model must not call the save method in the backend. This is done by the framework, if necessary.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
87
METHOD save .
wd_this->mo_model->refresh_model( ).
ENDMETHOD.
Usually, you call the refresh_model( ) of your model, which synchronizes the UI with the backend.
This method can throw CX_FDT_WD.
SET_CONFIGURATION
After the creation of your WD component, this method is called by the framework and provides the configuration handle (IO_CONFIGURATION). You must store this parameter into an attribute (convention: mo_configuration) of your component controller. The configuration represents the user configuration and technical settings (for example if a toolbar button should be displayed). The configuration handle (IO_CONFIGURATION) is a mandatory parameter for every service method.
wd_this->MO_CONFIGURATION = io_configuration.
VIEW
The view (MAIN_VIEW) contains the layout of the action Email UI. The UI ADJUSTMENT and MODEL nodes are bound from the component controller.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
88
As usual in WD you define your UI elements here and bind the content properties of it to context nodes. The content of the data is provided by the model nodes. For example, you bind the Recipients input field to the
context attribute Model.Recipients.Recipients (same applies to Subject, Message, and Parameters).
You also bind the attributes of the UIAdjustment node to the proper properties (enabled, read_only) of the elements in your view.
Window OBJECT IVIEW
The WD component has a view MAIN_VIEW, which contains the layout for the UI, as discussed. To display the view include it in the window OBJECT_IVIEW, which is an interface view of FDT_IWD_OBJECT.
Only views that are embedded in the window OBJECT_IVIEW are displayed.
REGISTER_TOOLBAR
This event registers a toolbar in the „DETAIL‟ tray.
The toolbar can consist of max 8 toolbar buttons.
Mandatory fields are the name of the action („action‟) and the text of the button („text‟).
Look at IF_FDT_WD_TYPES=>S_TOOLBAR for further fields.
When you press the button on the toolbar you are notified via the method, HDL_ACTION.
If you raise the event again, the toolbar is rebuilt from scratch.
The following method is an example how to populate buttons in the toolbar. You call this method in the display and edit methods of the Componentcontroller.
METHOD create_toolbar_.
DATA: ls_toolbar TYPE if_fdt_wd_types=>s_toolbar,
lt_toolbar TYPE if_fdt_wd_types=>t_toolbar.
ls_toolbar-action = “My_Action”
ls_toolbar-text = “My Test Action”
APPEND ls_toolbar TO lt_toolbar.
"register toolbar
wd_this->fire_register_toolbar_evt( it_toolbar = lt_toolbar ).
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
89
If the user presses a button, the method HDL_ACTION is called by the framework. This gives you the chance to handle the action.
METHOD hdl_action .
IF iv_action = My_Action”.
“Do something here
ENDIF.
ENDMETHOD.
Wizard UI
Optionally, you have the possibility to provide a simplified UI for your action or expression type, which is used when the object is created.
An example is the random expression type, which provides the following screen during creation:
You can provide such a wizard UI for your expression or action type by implementing the followings steps:
1. Create a view for the wizard UI. The view should be simple, small and easy to use. You should reuse the existing model nodes, which are already available for your main view. It must not have any links, since it is not possible to navigate to other objects within this view.
2. Nest the view in the interface view WIZARD_IVIEW, which is available.
3. Implement the method IS_SUPPORTING_WIZARD_MODE in your component controller and set:
rv_supports_wizard_mode = abap_true
4. Implement the method MAINTAIN_IN_WIZARD of your component controller. This method is called by the framework, before the create popup is displayed.
You can fill model nodes via your model class if you want to set default values.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
90
When the user has acknowledged the creation of the object, you are notified via the SAVE method. As in the standard view, synchronize your UI with the backend by calling the refresh_model( ).
The method CREATE of your WD component is called by the framework if the user creates a new instance of the object (Create Popup). You use this method to preconfigure your component and to decide dynamically if you provide a wizard UI or not (implementation of IS_SUPPORTING_WIZARD_MODE).
Note
It does not make sense to provide a wizard UI for every expression type or action type, especially for more complex expression types such as the decision table.
Be aware that it is not possible to navigate from a wizard UI to other BRFplus objects.
Therefore, do not use links within this view.
16.3 Model Class Each WD component has an assigned model class with the main task of filling and retrieving the data of the model nodes of the WD component. The framework automatically tracks the user who has changed data in the UI.
The GET_MODEL… methods read data from the backend and provide it as (model) context nodes for the WD component.
The SET_MODEL… methods read data from the (model) context nodes and update the backend accordingly. Each model class must inherit from CL_FDT_WD_MAINTENANCE_MODEL.
Note
You should read data from the API in the GET_MODEL_BY_NODE method and fill the model nodes accordingly. Change the content of model nodes in the WD component only if the user changes data. Otherwise, the tracking of the state of your component will be erroneous.
16.3.1 Implementation of Model Class
The model class for custom action email is CL_FDT_WD_ACTN_EMAIL_MODEL and is inherited from CL_FDT_WD_MAINTENANCE_MODEL.
The redefinition of the following methods is required.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
91
IF_FDT_WD_MODEL~INIT
This method initializes the model and is called during the initialization of the component.
METHOD if_fdt_wd_model~init.
DATA lx_fdt TYPE REF TO cx_fdt.
" calling init of superclass
super->if_fdt_wd_model~init( (1)
io_context_node = io_context_node
io_controller = io_controller ).
TRY.
me->mo_actn_email ?= me->mo_factory->get_expression( iv_id = mv_id ). (2)
CATCH cx_fdt INTO lx_fdt.
cl_fdt_wd_service=>convert_backend_exception( ix_fdt = lx_fdt ). (3)
ENDTRY.
ENDMETHOD.
A call is made to INIT( ) of super class that is CL_FDT_WD_MAINTENANCE_MODEL to initialize the framework (1).
In the next step (2), the reference to the backend is saved in member variable mo_actn_email, which is received from the factory. You get the instance of the corresponding backend class.
Very important is the exception handling in the UI layer. You must always convert backend exceptions (3) with the service call
cl_fdt_wd_service=>convert_backend_exception( ix_fdt = lx_fdt ).
This means, every time you call methods of the backend API, which can potentially raise an exception, you
have to put it in a TRY…CATCH block with the mentioned service call.
The INIT() can throw CX_FTD_WD.
PUBLISH_MODEL_NODES
The purpose of this method is to publish the model nodes which are defined in the WD component controller under the model root node.
METHOD if_fdt_wd_model~publish_model_nodes.
DATA: ls_model_node TYPE if_fdt_wd_types=>s_model_node.
* node recipients
ls_model_node-name = gc_model_node_recipients. (1)
ls_model_node-read_only = abap_false.
INSERT ls_model_node INTO TABLE rt_model_node. (2)
* node subject & Body
ls_model_node-name = gc_model_node_message.
ls_model_node-read_only = abap_false.
INSERT ls_model_node INTO TABLE rt_model_node.
ENDMETHOD.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
92
For every model node you create a constant (1) in the model class by using the name of the model name as its value.
You publish those model nodes in this method. For each model node, you create an entry in table rt_model_node (2). You specify if the model is only read-only; the user cannot change the data. Since this is not true for this component, the read_only flag is set to abap_false.
Sometimes there is the need to add a non-data attribute to your model node. This is true if you use the model for binding against a WD table since WD does not provide the possibility to bind two data sources for content and metadata (for example, design of a row) of the table. To mark an attribute as meta-data and not to consider it for checking if the object has changed, you can model the table ‘attributes’ accordingly, which is part of the structure of the returning parameter rt_model_node.
For example, to exclude the CELL VARIANT attribute from a check to determine if the object was changed,
you would implement the following:
ls_attribute-name = 'CELL_VARIANT'.
ls_attribute-exclude_from_dirty_check = abap_true.
INSERT ls_attribute INTO TABLE lt_attributes.
ls_model_node-attributes = lt_attributes.
GET_MODEL_BY_NODE
This method retrieves the model from the backend and fills the proper model node. We discuss only the recipient model node to simplify this tutorial.
METHOD get_model_by_node.
DATA: lo_model_node TYPE REF TO if_wd_context_node,
lx_fdt TYPE REF TO cx_fdt,
lt_recipients TYPE if_fdt_actn_email=>t_recipient,
lts_rec_param TYPE if_fdt_actn_email=>ts_parameter,
ls_rec_param TYPE if_fdt_actn_email=>s_parameter,
ls_recipients_model TYPE fdts_wd_actn_email_recp_model,
ls_icon TYPE cl_fdt_wd_icons=>s_icon,
lv_body TYPE string,
lv_subject TYPE string,
lv_text TYPE string,
ls_message_model TYPE fdts_wd_actn_email_msg_model.
TRY.
lo_model_node = me->if_fdt_wd_model~get_model_node_by_name( iv_model_node_name ).
(1)
CASE iv_model_node_name.
WHEN gc_model_node_recipients. (2)
IF mv_timestamp IS NOT INITIAL.
lt_recipients = mo_actn_email->get_recipients( mv_timestamp ).
(3)
mo_actn_email->get_parameters(
EXPORTING iv_timestamp = mv_timestamp
IMPORTING ets_recipient_parameter = lts_rec_param ).
ELSE.
lt_recipients = mo_actn_email->get_recipients( ).
mo_actn_email->get_parameters(
IMPORTING ets_recipient_parameter = lts_rec_param ).
ENDIF.
lo_model_node->get_static_attributes(
IMPORTING static_attributes = ls_recipients_model ).
LOOP AT lts_rec_param INTO ls_rec_param.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
93
IF ls_rec_param-parameter_id IS NOT INITIAL.
IF mv_timestamp IS NOT INITIAL.
lv_text = cl_fdt_wd_service=>get_display_name(
iv_id = ls_rec_param-parameter_id
iv_timestamp = mv_timestamp
io_configuration = mo_configuration
iv_bound_object_id = mv_id ).
ELSE.
lv_text = cl_fdt_wd_service=>get_display_name(
iv_id = ls_rec_param-parameter_id
io_configuration = mo_configuration
iv_bound_object_id = mv_id ).
ENDIF.
ELSE.
lv_text = text-001.
ENDIF.
CASE ls_rec_param-position.
WHEN 1.
ls_recipients_model-rec_1 = ls_rec_param-parameter_id.
ls_recipients_model-rec_1_name = lv_text.
ls_recipients_model-rec_1_req = abap_true.
IF ls_rec_param-parameter_id IS NOT INITIAL.
CLEAR ls_icon.
ls_icon = cl_fdt_wd_icons=>get_icon(
iv_id = ls_rec_param-parameter_id
io_configuration = mo_configuration
iv_timestamp = mv_timestamp
iv_bound_object_id = mv_id ).
ls_recipients_model-rec_1_icon = ls_icon-icon_source.
ls_recipients_model-rec_1_tooltip = ls_icon-tooltip.
ELSE.
ls_recipients_model-rec_1_tooltip = lv_text.
ENDIF.
WHEN 2.
ls_recipients_model-rec_2 = ls_rec_param-parameter_id.
ls_recipients_model-rec_2_name = lv_text.
ls_recipients_model-rec_2_req = abap_true.
IF ls_rec_param-parameter_id IS NOT INITIAL.
CLEAR ls_icon.
ls_icon = cl_fdt_wd_icons=>get_icon(
iv_id = ls_rec_param-parameter_id
io_configuration = mo_configuration
iv_timestamp = mv_timestamp
iv_bound_object_id = mv_id ).
ls_recipients_model-rec_2_icon = ls_icon-icon_source.
ls_recipients_model-rec_2_tooltip = ls_icon-tooltip..
ELSE.
ls_recipients_model-rec_2_tooltip = lv_text.
ENDIF.
WHEN 3.
ls_recipients_model-rec_3 = ls_rec_param-parameter_id.
ls_recipients_model-rec_3_name = lv_text.
ls_recipients_model-rec_3_req = abap_true.
IF ls_rec_param-parameter_id IS NOT INITIAL.
CLEAR ls_icon.
ls_icon = cl_fdt_wd_icons=>get_icon(
iv_id = ls_rec_param-parameter_id
io_configuration = mo_configuration
iv_timestamp = mv_timestamp
iv_bound_object_id = mv_id ).
ls_recipients_model-rec_3_icon = ls_icon-icon_source.
ls_recipients_model-rec_3_tooltip = ls_icon-tooltip.
ELSE.
ls_recipients_model-rec_3_tooltip = lv_text.
ENDIF.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
94
WHEN 4.
ls_recipients_model-rec_4 = ls_rec_param-parameter_id.
ls_recipients_model-rec_4_name = lv_text.
ls_recipients_model-rec_4_req = abap_true.
IF ls_rec_param-parameter_id IS NOT INITIAL.
CLEAR ls_icon.
ls_icon = cl_fdt_wd_icons=>get_icon(
iv_id = ls_rec_param-parameter_id
io_configuration = mo_configuration
iv_timestamp = mv_timestamp
iv_bound_object_id = mv_id ).
ls_recipients_model-rec_4_icon = ls_icon-icon_source.
ls_recipients_model-rec_4_tooltip = ls_icon-tooltip.
ELSE.
ls_recipients_model-rec_4_tooltip = lv_text.
ENDIF.
ENDCASE.
ENDLOOP.
CONCATENATE LINES OF lt_recipients INTO ls_recipients_model-
recipients SEPARATED BY '; '. (4)
lo_model_node->bind_structure( new_item = ls_recipients_model
(5)
set_initial_elements = iv_initialize_elements ).
...
ENDCASE.
CATCH cx_fdt INTO lx_fdt.
cl_fdt_wd_service=>convert_backend_exception( ix_fdt = lx_fdt ).
ENDTRY.
ENDMETHOD.
Initially, the instance of the model node is retrieved by calling get_model_node_by_name( ) (1).
For each model node (2) which is provided by the importing parameter iv_model_node_name, we fill the
data accordingly. If you have several model nodes in your component, it makes sense to spread the implementation into several private methods of your model class, which are called by this method.
When you call the GET methods of the backend-API (mo_actn_email->get_recipients) you must
check if the UI should display an older version: if mv_timestamp is not initial, you have to supply this
timestamp for the backend call (3).
The recipient table (lt_recipients) provided by the backend is then prepared for displaying. Therefore,
the recipients are split into a string (lv_recipients_model) (4) and bound to the structure
ls_recipients_model of the structure node ls_recipients.
Then, the structure is bound to the model node (5). If you have a table (context node with cardinality > 1) you
must use the method bind_table instead. Be aware of the optional parameter iv_initialze_elements,
which you should assign to set_initial_elements when calling the BIND… methods of the WD context
node.
Finally, the exceptions of the backend are converted into the cx_fdt_wd_backend exception of the UI
layer.
Note
The protected method SET_MODEL_BY_NODE of CL_FDT_WD_MODEL has to be re-implemented and not the interface IF_FDT_WD_MODEL method.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
95
SET_MODEL_BY_NODE
The purpose of this method is to set the model node to the backend for the respective node. This means the backend is updated.
METHOD set_model_by_node.
DATA: lo_nd_model TYPE REF TO if_wd_context_node,
ls_message_model TYPE fdts_wd_actn_email_msg_model,
ls_parameter TYPE if_fdt_actn_email=>s_parameter,
lts_parameter TYPE if_fdt_actn_email=>ts_parameter,
lt_recipients TYPE if_fdt_actn_email=>t_recipient,
lt_recipients_final TYPE if_fdt_actn_email=>t_recipient,
ls_recipients LIKE LINE OF lt_recipients,
lt_recipients_model TYPE STANDARD TABLE OF fdts_wd_actn_email_recp_model,
ls_recipients_model TYPE fdts_wd_actn_email_recp_model,
ls_bs_model TYPE fdts_wd_actn_email_bs_model,
lx_fdt TYPE REF TO cx_fdt,
lv_recipients_model TYPE string,
lt_plc_hldr TYPE match_result_tab,
lo_query TYPE REF TO if_fdt_query,
lt_ids TYPE REF TO if_fdt_types=>t_object_id,
lt_token TYPE cl_fdt_actn_email=>ts_token,
ls_token TYPE cl_fdt_actn_email=>s_token,
lv_body TYPE string,
lv_message TYPE string.
DATA: result_tab TYPE match_result_tab,
ls_result_tab TYPE match_result.
TRY.
lo_nd_model = me->if_fdt_wd_model~get_model_node_by_name( iv_model_node_name ).
(1)
WHEN gc_model_node_recipients.
(2)
" getting element of node
lo_nd_model-
>get_static_attributes( IMPORTING static_attributes = ls_recipients_model ). (3)
lv_recipients_model = ls_recipients_model-recipients.
SPLIT lv_recipients_model AT ';' INTO TABLE lt_recipients.
LOOP AT lt_recipients INTO ls_recipients.
CONDENSE ls_recipients.
INSERT ls_recipients INTO TABLE lt_recipients_final.
ENDLOOP.
mo_actn_email->set_recipients( lt_recipients_final ).
IF ls_recipients_model-rec_1_req EQ abap_true AND ls_recipients_model-
rec_1 IS NOT INITIAL.
ls_parameter-position = 1.
ls_parameter-parameter_id = ls_recipients_model-rec_1.
INSERT ls_parameter INTO TABLE lts_parameter.
ENDIF.
IF ls_recipients_model-rec_2_req EQ abap_true AND ls_recipients_model-
rec_2 IS NOT INITIAL..
ls_parameter-position = 2.
ls_parameter-parameter_id = ls_recipients_model-rec_2.
INSERT ls_parameter INTO TABLE lts_parameter.
ENDIF.
IF ls_recipients_model-rec_3_req EQ abap_true AND ls_recipients_model-
rec_3 IS NOT INITIAL..
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
96
ls_parameter-position = 3.
ls_parameter-parameter_id = ls_recipients_model-rec_3.
INSERT ls_parameter INTO TABLE lts_parameter.
ENDIF.
IF ls_recipients_model-rec_4_req EQ abap_true AND ls_recipients_model-
rec_4 IS NOT INITIAL..
ls_parameter-position = 4.
ls_parameter-parameter_id = ls_recipients_model-rec_4.
INSERT ls_parameter INTO TABLE lts_parameter.
ENDIF.
mo_actn_email->set_parameters( EXPORTING its_recipient_parameter = lts_parameter ).
(4)
ENDCASE.
CATCH cx_fdt INTO lx_fdt.
cl_fdt_wd_service=>convert_backend_exception( ix_fdt = lx_fdt ).
ENDTRY.
ENDMETHOD.
To set the model to the API you need a variable (ls_recipients_model) which refers to the same DDIC-
type as your model node in the Web Dynpro component.
Initially, the reference for the model node is retrieved (1).
Afterwards, you have to check which model node (iv_model_node_name) shall be updated (2).
Then, the data are retrieved from the (model) context node (3).
Finally, the backend is updated (4).
16.3.2 Detecting if the object has changed
In the majority of cases, you do not have to take care about the dirty state of the maintained object, if the current maintained object is changed.
The BRFplus UI framework provides this check as an automatic service by tracking the state of your model nodes. However, this service is not available when there is the need to use a recursive node (displaying hierarchical data in a tree).
In this case, you are responsible to detect if the user has changed the tree (and its content). To do this with the minor effort, apply the following pattern:
Implementation of IF_FDT_WD_MODEL~PUBLISH_MODEL_NODE: Set the field
ls_model_node-exclude_from_dirty_check = abap_true for the model node of the tree
representation (hierarchy) to exclude this model from the ‘dirty check’.
Use a separate model node for the content (attributes) of the node tree. It has the advantage that you do not have to check, if the user has changed these node attributes. Of course, when the user chooses ‘SAVE’ or switches to another node in the tree you must update the model
(set_model_by_node…).
When the user creates or deletes a node, this action calls proper methods in you model class. These methods (CREATE_NODE) change the tree and have to inform the framework by calling the method
if_fdt_wd_model~set_model_changed( ) to indicate that the model has changed.
16.3.3 Reference
The following table provides an overview about the methods of the model class.
The interface IF_FDT_WD_MODEL consists of the following methods. You can call these methods in your WD component:
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
97
Method Description IF_WD_COMPONENT_ASSISTANCE~GET_TEXT
If you have defined text symbols in your model class, you can access them with the help of this method.
PUBLISH_MODEL_NODES Redefine this method.
Publish all the model nodes you have defined in your WD
component. Set the read_only flag = abap_true, if the
user cannot change the data of the node. When you have a recursive context node please set the field ‘exclude_from_dirty_check’ = abap_true.
Detecting if the object has changed.
There is also a hash table available (attributes component in the rt_model_node table) that enables you to exclude certain attributes of your node(s) for the dirty state check. This means that if the values of these attributes have changed, the object (=>your WD component) is not considered as unchanged. Do not misapply this option to mix the model with non-model parameters.
INIT Redefine this method. Call this method during initialization of your WD component. Do any necessary initialization here. Do not forget to call the super method.
(super->if_fdt_wd_model~init(io_context_node = io_context_node io_controller =
io_controller)
This method can throw CX_FDT_WD_BACKEND.
GET_MODEL This method gets the whole model for the WD component and calls GET_MODEL_BY_NODE for each model node.
This method can throw CX_FDT_WD_BACKEND.
SET_MODEL This method updates the backend from the WD component and calls SET_MODEL_BY_NODE for each changeable model node.
Optional: you can provide an action which describes the changes (insert, update) if you need such semantics in your implementation.
This method can throw CX_FDT_WD_BACKEND.
GET_MODEL_BY_NODE This method retrieves your model from the backend and fills the proper model node. You can provide a WD context node and/or a WD context element if you need to query the data from the backend. A use case for these query elements would be for instance: you have a tree, the user adds a new node, and you want to re-read the corresponding sub-tree to reflect the changes on the UI.
Do not redefine this method but instead use the protected method GET_MODEL_BY_NODE.
This method can throw CX_FDT_WD_BACKEND.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
98
SET_MODEL_BY_NODE This method sets/updates your model to the backend for the node. Do not redefine this method but instead use the protected method SET_MODEL_BY_NODE. You can provide an action which describes the changes (insert, update). The action is not evaluated by the framework; you can use it in your implementation.
This method can throw CX_FDT_WD_BACKEND.
IS_MODEL_CHANGED This method returns true, if the model has changed.
GET_MODEL_NODE_BY_NAME Retrieves the reference to a model node.
REFRESH_MODEL This method refreshes the model; the UI and backend are synchronized. Technically, SET_MODEL (optional: SET_MODEL_BY_NODE) is called first and then GET_MODEL (optional GET_MODEL_BY_NODE).
This method is useful if the user has changed some data on the UI and you want to call the API to update further fields (texts, descriptions).
This method can throw CX_FDT_WD_BACKEND.
SET_MODEL_CHANGED This method sets the model as changed.
The following protected methods are available:
Method Description SET_NOT_FOUND_TEXT_KEY Do not use this method.
It is inherited from the WD Assistance Class, which is the super class of our model class.
GET_MODEL_BY_NODE Redefine this method:
Implement the functionality to retrieve the model from the API in this method.
This method can throw CX_FDT_WD_BACKEND.
SET_MODEL_BY_NODE Redefine this method:
Implement the functionality to set the model to the API.
This method can throw CX_FDT_WD_BACKEND.
You are provided with the following:
UUID (mv_id)
Timestamp (mv_timestamp)
Instance of the factory (mo_factory)
Current state (mo_state) of the object
16.4 UI Class The purpose of this OO class is to determine the characteristics of the expression / action type regarding its UI. It determines the model class, the WD Component, and the implemented version of the UI framework/infrastructure. The UI class is an attribute of the expression /action type and can be set via the UI.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
99
The necessary reimplementation of the methods of interface IF_FDT_UI_DEFINITION is described in the following sections.
16.4.1 HAS_WD_ABAP_UI
The purpose of this method is to determine if an expression type or an action type has a WD ABAP UI. The method returns a Boolean variable stating the support for WD ABAP UI.
METHOD if_fdt_ui_definition~has_wd_abap_ui.
rv_has_wd_ui = abap_true.
ENDMETHOD.
There is an active WD ABAP UI for custom action email, thus the parameter is set to abap_true.
16.4.2 GET_WD_ABAP_MODEL_CLASS
In this method, the model class for an expression type or action type UI is determined.
METHOD if_fdt_ui_definition~get_wd_abap_model_class.
rv_model_class = 'CL_FDT_WD_ACTN_EMAIL_MODEL'.
ENDMETHOD.
The parameter returns the active model class for the custom action email.
16.4.3 GET_WD_ABAP_COMPONENT
By using this method, the WD ABAP component can be determined.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
100
METHOD if_fdt_ui_definition~get_wd_abap_component.
rv_wd_component = 'FDT_WD_ACTN_EMAIL'.
ENDMETHOD.
The parameter returns the active WD component for the custom action email.
16.4.4 GET_WD_ABAP_VERSION
This method determines the implemented version of the BRFplus UI framework.
METHOD if_fdt_ui_definition~get_wd_abap_version.
rv_version = ‘1.01’.
ENDMETHOD.
By versioning the UI components, it ensures that customer expression type or action types are protected against future enhancements of this BRFplus UI framework. When your system is updated to a newer release and you do not have the chance or do not want to immediately adapt your components, you should return the version, which was available during development. The current version number is the value of the constant IF_FDT_UI_DEFINITION=>GC_WD_ABAP_VERSION.
It is not recommended to return IF_FDT_UI_DEFINITION=>GC_WD_ABAP_VERSION because then you would be forced to immediately react on every framework change. Instead, you should return the value of the constant (1.01) to ensure your coding runs in subsequent SAP NetWeaver releases without adaptation of your expression type.
Note
If you return IF_FDT_UI_DEFINITION=>GC_WD_ABAP_VERSION, you adapt your WD component if the system is upgraded to a newer SAP NetWeaver release. Since the adaptation is not always immediately possible, you are strongly encouraged to return the current version as a value and not the constant. You can make use of new framework features by adapting your coding and then return a higher version.
The change history is documented in the interface documentation of IF_FDT_UI_DEFINITION.
Note
The versioning is independent of an SAP NetWeaver release. An update of an SAP NetWeaver release may not result in an update of the UI framework when there are no incompatible changes.
16.5 Reuse Components and Service in the UI Layer
16.5.1 Creating a new BRFplus Object
If you want to create a new BRFplus object within your expression UI, you can do this by implementing the following steps:
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
101
The WD interface FDT_IWD_OBJECT, which is implemented by your WD component, provides two events for creating new objects:
CREATE_OBJECT_SIMPLE
CREATE_OBJECT_EXPERT
CREATE_OBJECT_SIMPLE is easier to use and CREATE_OBJECT_EXPERT provides more options with respect of controlling if the user can use reusable objects.
After the event is fired, the standard creation dialog is displayed. If the user actually creates an object, your component is informed by calling the method ADD_USAGE, which is also part of the interface FDT_IWD_OBJECT. You are provided with the ID of the newly created object and can update your model accordingly.
Example
You have an attribute ‘MY_ATTRIBUTE’ in your expression type, which can be bound to another expression.
Therefore, you create a method ‘CREATE_MY_ATTRIBUTE’ in one of your WD controllers, which is called by a UI action.
DATA: ls_object_type TYPE if_fdt_wd_types=>s_object_type_extended.
ls_object_type-type = if_fdt_constants=>gc_object_type_expression. (1)
wd_comp_controller->fire_create_object_simple_evt( (2)
is_object_type = ls_object_type
The possible object types that can be created by the user are narrowed by expressions (1) and then the event is triggered (2).
When the expression is created, you are notified by the call of the ADD_USAGE method in the component controller:
METHOD add_usage .
wd_this->mo_model->set_element_attribute( (1)
iv_model_node_name = CL_FDT_WD_MY_MODEL=>GC_MY_MODEL_NODE_NAME
iv_attribute_name = MyAttributeName
iv_value = iv_id ).
wd_this->mo_model->refresh_model( wd_this->wdctx_properties ). (2)
The incoming parameter IV_ID represents the ID of the created object. If the attribute is part of a flat model structure you can easily update the model by calling (1), providing the name of the model node and the name of the attribute. You use standard WD means to update the attribute in your WD context (model) node. This is needed if your context node has cardinality > 1 (table).
After the attribute is set, the model is refreshed (2). This means, the API setter and getter are called via your model class. This ensures that dependent attributes are also updated in the UI.
Note
When you have more than one parameter created by the user, you must memorize which parameter is created before raising the event. In the ADD_USAGE method, you can use this variable to differentiate between those parameters.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
102
16.5.2 Selecting an Existing BRFplus Object
If you want to bind a parameter against an existing BRFplus object within your action / expression UI, you can use the UI query.
The WD interface FDT_IWD_QUERY provides various methods to query for a specific BRFplus object. After calling one of those methods, a query dialog is displayed from which the user can choose an object. If the user has actually chosen an object, your component is notified and the ID of the object is provided.
Example
You have an attribute MY_ATTRIBUTE in your expression type, which is bindable to another expression.
To access the query, you must add the component (interface) FDT_IWD_QUERY to your component.
To trigger the query by a user action, create a method, SELECT_MY_ATTRIBUTE, in one of your WD controllers:
DATA: lo_comp_factory TYPE REF TO if_fdt_wd_component_factory.
IF wd_this->wd_cpuse_query( )->has_active_component( ) IS INITIAL. (1)
lo_comp_factory ?= cl_fdt_wd_factory=>if_fdt_wd_factory~get_instance( ).
lo_comp_factory->create_query_component(
io_usage = wd_this->wd_cpuse_query( )
io_configuration = wd_this->mo_configuration ).
ENDIF.
TRY.
lo_admin_data = cl_fdt_wd_service=>get_admin_data( wd_comp_controller->mv_id )(2)
lv_application_id = lo_admin_data->get_application( ).
wd_comp_controller->get_query( )->object_query(
io_calling_object = lo_admin_data
is_object_type = ls_object_type
iv_application_id = lv_application_id
iv_initial_search = abap_true
iv_type_fixed = abap_true
iv_multiple_selection = abap_false
).
CATCH cx_fdt_input INTO lx_fdt_input.
cl_fdt_wd_service=>report_exception(
io_message_manager = wd_this->wd_get_api( )->get_message_manager( )
ix_exception = lx_fdt_input ).
ENDTRY.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
103
(1) If the query is not already instantiated, this is done via the factory. Ensure that you supply the configuration handle (if_fdt_wd_configuration).
(2) The admin data instance of the current object is retrieved and the query is called.
The standard object query consists of the following parameters:
Parameter Type Description
IO_CALLING_PARAMETER Importing Mandatory:
Admin Data Reference of current displayed object (expression).
IS_OBJECT_TYPE Importing Object types, which are considered.
IV_APPLICATION_ID Importing Query can be limited to an application.
IV_INITIAL_SEARCH Importing If ABAP_TRUE, hit list is immediately displayed.
IV_TYPE_FIXED Importing User cannot change object type in the search dialog.
IV_MULTIPLE_SELECTION Importing If ABAP_TRUE, user can select multiple objects from hit list.
Note
IO_CALLING_PARAMETER is optional in the signatures of query methods. This will change in the future. Therefore, make sure that you always provide IO_CALLING_PARAMETER.
To receive the selected object(s), implement an event handler.
The query interface FDT_IWD_QUERY provides the event OBJECTS_SELECTED, which you can implement in an event handler HDL_OBJECTS_SELECTED.
DATA: lt_object TYPE fdtt_wd_object,
lx_fdt_wd TYPE REF TO cx_fdt_wd.
FIELD-SYMBOLS: <ls_object> TYPE fdts_wd_object.
**********************************************************************
READ TABLE mt_objects INDEX 1 ASSIGNING <ls_object>. (1)
wd_comp_controller->mo_model->set_element_attribute(
iv_model_node_name = wd_comp_controller->wdctx_properties (2)
iv_attribute_name = 'EXPRESSION_ID'
iv_value = <ls_object>-id
).
wd_comp_controller->mo_model->refresh_model( wd_comp_controller->wdctx_properties ). (3)
The selected objects are available in the importing table mt_objects (1) of the event.
The selected expression is set to the model node by the convenience method set_element_attribute.
The call of refresh_model (3) refreshes the view.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
104
Note
When you have more than one parameter that can be selected by the user, memorize which parameter is created before raising the event. In the event handler, you can then use this variable to differentiate between those parameters.
The previous example presents the usage of the OBJECT_QUERY method from the class CL_FDT_WD_QUERY_MODEL. This method controls the execution of the object query. In addition to this method, the CL_FDT_WD_QUERY_MODEL class has other useful methods for querying objects.
Object Description
ELEMENT_QUERY Retrieves elements from the query.
CONTEXT_QUERY Retrieves context elements.
ADVANCED_QUERY Retrieves BRFplus objects by specifying a list of object types in the IT_OBJECT_TYPES table and restricting them, based on the result type of expressions.
16.5.3 Navigating to Other BRFplus Objects
You can navigate to other objects within your UI. For example, you can create a link to another expression that is bound to an attribute of your expression type.
To navigate to another object, simply raise the event NAVIGATE_TO_OBJECT and provide the ID of the object. The event is available in the component controller of your component (implemented in interface FDT_IWD_OBJECT).
16.5.4 Object Menu
To enable the user to bind a BRFplus object to a parameter, use a link menu by which the user is able to choose an existing object or to create a new object. The scope of this service is for use within an expression type UI.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
105
The interface IF_FDT_WD_OBJECT_MENU implements the service to render the menu for binding objects. It includes the system objects (Standard). Further, it displays either the context data objects (render_menu_default) or the last accessed objects (render_menu). The number of these ‘quick accessible’ objects is limited. For accessing elements of a structure, the user must use the ‘select’ functionality (standard query).
Additionally, the standard menu items such as Select, Create and Remove are displayed. This interface allows you to customize the menu. Due to WD restriction, this menu has to be created before the user clicks on it.
Demo Component
The WD component FDT_WD_DEMO_OBJECT_MENU provides an example how to use the object menu service.
Rendering the Menu
To render the menu, implement the following steps (name of attributes/methods are proposals):
1. Create a menu for the link (UI element, but without any menu items!).
2. Create an attribute MO_OBJECT_MENU (public access) type ref to IF_FDT_WD_OBJECT_MENU (preferable in the ‘Custom/Action’ controller, public access).
3. Create an attribute MV_REFRESH_OBJECT_MENU, type boole_d (public access) in the component controller.
4. Implement a method ‘BUILD_OBJECT_MENU’ in a controller (it is preferable to do this in the Component / Action controller). Call this method from the wdmodifyview method in your view, if this method is called for the first time or if mv_refresh_object_menu = abap_true. You have to set mv_refresh_object_menu to true if the menu has to be refreshed (the user has selected another object, thus the menu must be updated).
5. Call the constructor by supplying the admin data of the maintained object and the UI configuration (should be available as mo_configuration in your WD comp controller).
Implementation of Method BUILD_OBJECT_MENU
The service interface IF_FDT_WD_OBJECT_MENU provides the following methods to render the menu:
RENDER_DEFAULT_MENU
This method renders a default menu, which includes accessing the context and the selection/creation of an expression.
Use this menu if you want to bind a context parameter or an expression to an attribute of your expression type. In most cases you use this method.
RENDER_MENU
This method renders a menu according to the supplied object_type (is_object_type).
It provides a Select <Object Type>, Create <Object Type>, and a number of last accessed objects, which fits to the object type.
These methods contain the following parameters:
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
106
Parameter Type Description
IO_VIEW Importing View handle (provided in WDDOMODIFYVIEW).
IV_MENU_ID Importing ID of the (link) menu (UI element).
IV_ACTION_HANDLER Importing Action (in the view) which handles the object menu actions. You only need to define one action for all menu items.
IS_OBJECT_TYPE Importing Only RENDER_MENU: Defines the object type, which can be selected.
IV_ACTION_MODE Importing Only RENDER_MENU_DEFAULT: If true only actions will be considered for binding (instead of expressions).
IS_CONFIGURATION Importing Customizes the menu. For example, you can define the maximum items for the quick selection items. See type section in IF_FDT_WD_OBJECT_MENU. (Optional)
IV_BOUND_OBJECT_ID Importing Object which is currently bound to the parameter... initial is possible. If no object is bound, the delete menu item is not visible. (Optional)
IV_BIND_ENABLED_PATH Importing Path of enabled attribute (for enabling/disabling the menu items). This is normally UI_ADJUSTMENT.ENABLED; the context node supplied by FDT_IWD_OBJECT. (Optional)
Implementation Example for Method BUILD_OBJECT_MENU
DATA:
lo_configuration TYPE REF TO if_fdt_wd_configuration,
lo_factory TYPE REF TO if_fdt_wd_service_factory,
lo_node TYPE REF TO if_wd_context_node,
lo_node_mode TYPE REF TO if_wd_context_node,
lv_bound_object_id TYPE if_fdt_types=>id,
ls_object_type TYPE if_fdt_wd_types=>s_object_type_extended,
lt_element_type TYPE if_fdt_wd_object_menu=>t_element_type,
lo_caller TYPE REF TO if_fdt_admin_data,
lx_fdt_wd TYPE REF TO cx_fdt_wd.
*************************************************************************
TRY.
*create an instance of the object menu if this is the first call
IF wd_this->mo_object_menu IS NOT BOUND.
lo_factory ?= cl_fdt_wd_factory=>if_fdt_wd_factory~get_instance( ).
lo_caller = cl_fdt_wd_service=>get_admin_data( wd_this->mv_id ).
wd_this->mo_object_menu = lo_factory-
>get_object_menu( io_configuration = wd_this->mo_configuration
io_caller = lo_caller ).
ENDIF.
*you could call the set_filter... methods to filter out the available objects
*wd_this_mo_object_menu->set_filter...
*finally render the menu
wd_this->mo_object_menu->render_menu_default(
io_view = io_view
iv_action_handler = 'OBJECT_MENU_HANDLER'
* iv_bind_enabled_path = 'UI_ADJUSTMENT.ENABLED'
iv_menu_id = 'OBJECT_MENU' ).
CATCH cx_fdt_wd INTO lx_fdt_wd.
cl_fdt_wd_service=>report_exception( io_message_manager =
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
107
wd_this->wd_get_api( )->get_message_manager( )
ix_wd_exception = lx_fdt_wd ).
ENDTRY.
You can add additional menu items but you must implement the action handler yourself.
If you want to change the text of a rendered menu, you can get the instance of the menu item by calling the method, GET_MENU_ITEM_BY_ID.
Filter for Data Object + Element Types
The methods SET_FILTER_DATA_OBJECT_TYPES, SET_FILTER_ELEMENT_TYPES, and SET_FILTER_OBJECTS provide the possibility to limit the display of instances to certain element / data object types. Use these filters if the result data object has to be a Boolean type.
Note
Be aware that the filter affects only the quick list (context data objects/history) and system objects.
If you call the query component according to the user action, you have to apply the corresponding filter for the query. The same is true if a new object is created.
Menu Actions
Create an action in the view to react on menu actions. This action must have the same name as the one you assigned by calling the RENDER…. methods.
Call the method GET_ACTION_PARAMETER to retrieve the parameters for the action to detect if the user wants to create/select/remove an object – or if the user has already selected an instance:
DATA: ls_action_parameter TYPE if_fdt_wd_object_menu=>s_action_parameter.
***************************************************************************
*retrieve the action parameter to do followup actions.
ls_action_parameter = wd_comp_controller->mo_object_menu-
>get_action_parameter( wdevent ).
*create/remove/select etc. an object regarding the action, which was triggered by the use
r
CASE ls_action_parameter-action.
*user wants to create a new object (via event CREATE_OBJECT)
WHEN if_fdt_wd_object_menu=>gc_action_param_create.
*user wants to select a context object via the UI context query (via FDT_IWD_QUERY-
>CONTEXT_QUERY)
WHEN if_fdt_wd_object_menu=>gc_action_param_context_query.
*user wants to select an expression via the standard query via methods of FDT_IWD_QUERY
WHEN if_fdt_wd_object_menu=>gc_action_param_query.
*user wants to remove the current bounded object (delete the attribute of your model node
)
WHEN if_fdt_wd_object_menu=>gc_action_param_remove.
*user has selected an instance...
*...the instance is provided in ls_action_parameter-object_id
*...the selected object type is provided in ls_action_parameter-object_type
WHEN if_fdt_wd_object_menu=>gc_action_param_obj_selected.
ENDCASE.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
108
Note
If the user assigns another object, you must refresh the object menu.
If you follow the described pattern, set the member variable mv_refresh_object_menu =
abap_true.
16.5.5 Other Services
IF_FDT_WD_STATE
An instance of this interface is available in the model class (mo_state) and in your WD component
(provided as a parameter in the INIT method).
It represents the state of the currently displayed object (e.g. is displayed, is unsaved, etc.) and is especially helpful if you want to develop more complex UIs, within which the layout is created dynamically. Look at the available methods.
CL_FDT_WD_SERVICE
This service class provides methods to assist you in developing your own UIs. See the following methods:
GET_DISPLAY_NAME
You should use this method to retrieve the display name for an object since the user can decide if he wants to see the name or the text for an object via the configuration. By providing the ID of the object and the configuration handle you retrieve the display name.
GET_ADMIN_DATA
Returns the admin data instance, by providing its ID and the configuration handle.
REPORT_EXCEPTION
Reports a BRFplus exception as user message(s) by providing the exception instance and the WD message handler.
REPORT_MESSAGES
Reports BRFplus messages as user messages by providing the messages and the WD message handler.
CONVERT_BACKEND_EXCEPTION
Every time you call a method in the backend, which can throw an exception, you must catch it and convert it to the CX_FDT_WD_BACKEND exception by using this method.
16.6 Testing If you implemented the UI components correctly, the expression type or action type should be available in the create dialog. To speed up the testing process during development, you can display your UI without the need to navigate to it. The prerequisite for the following shortcut is that there is an existing expression (instance) for your expression type available.
To display any object in BRFplus, you can simply add the URL parameter ID to the URL.
Example:
http://myServer:50000/sap/bc/webdynpro/sap/fdt_wd_workbench?sap-language=EN&sap-client=000&ID=00132120A5A802DB87A5A05EEFFC987B
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
109
Recommendation
We recommend setting a breakpoint in the constructor method of CX_FDT. This is a good idea because all of the BRFplus exception classes inherit from CX_FDT, so whenever an error occurs, chances are high that you can catch it there.
Also, when catching exceptions in your code, use the MT_MESSAGE variable that is known to all exception class instances. In most of the cases, this variable contains information on the current exception that you will find helpful in understanding and solving the issue.
CUSTOM EXPRESSION TYPES AND ACTION TYPES – A STEP-BY-STEP GUIDE WITH EXAMPLE
110
17 Related Content Wikipedia, Business Rules:
http://en.wikipedia.org/wiki/Business_rules
Wikipedia, Business Rule Management System: http://en.wikipedia.org/wiki/Business_Rule_Management_System
SCN, SAP NetWeaver Decision Service Management: http://scn.sap.com/docs/DOC-29158
SCN, Business Rule Framework plus: http://scn.sap.com/docs/DOC-8824
SCN, Business Rules Management http://scn.sap.com/community/brm
© 2014 SAP AG. All rights reserved.
SAP, R/3, SAP NetWeaver, Duet, PartnerEdge, ByDesign, SAP
BusinessObjects Explorer, StreamWork, SAP HANA, and other SAP
products and services mentioned herein as well as their respective
logos are trademarks or registered trademarks of SAP AG in Germany
and other countries.
Business Objects and the Business Objects logo, BusinessObjects,
Crystal Reports, Crystal Decisions, Web Intelligence, Xcelsius, and
other Business Objects products and services mentioned herein as
well as their respective logos are trademarks or registered trademarks
of Business Objects Software Ltd. Business Objects is an SAP
company.
Sybase and Adaptive Server, iAnywhere, Sybase 365, SQL
Anywhere, and other Sybase products and services mentioned herein
as well as their respective logos are trademarks or registered
trademarks of Sybase Inc. Sybase is an SAP company.
Crossgate, m@gic EDDY, B2B 360°, and B2B 360° Services are
registered trademarks of Crossgate AG in Germany and other
countries. Crossgate is an SAP company.
All other product and service names mentioned are the trademarks of
their respective companies. Data contained in this document serves
informational purposes only. National product specifications may vary.
These materials are subject to change without notice. These materials
are provided by SAP AG and its affiliated companies ("SAP Group")
for informational purposes only, without representation or warranty of
any kind, and SAP Group shall not be liable for errors or omissions
with respect to the materials. The only warranties for SAP Group
products and services are those that are set forth in the express
warranty statements accompanying such products and services, if
any. Nothing herein should be construed as constituting an additional
warranty.
www.sap.com