Sei sulla pagina 1di 111

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

SAP NetWeaver Business Decision Service Management


and
SAP NetWeaver Business Rule Framework plus

Custom Expression Types and Action Types


A Step-by-Step Guide with Example

CUSTOMER
Document Version: 1.0 - 15 October 2013

2013 SAP AG or an SAP affiliate company. All rights reserved .

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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

Table of Contents
1

Introduction ........................................................................................................................................... 6

1.1

Overview ................................................................................................................................................. 6

1.2

Expression Types and Action Types ...................................................................................................... 6

Specification/Design .......................................................................................................................... 10

Database Layout ................................................................................................................................. 12

Transport Objects ............................................................................................................................... 17

Interface Creation ............................................................................................................................... 21

Class Creation ..................................................................................................................................... 26

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

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
8.3.1

Interface IF_FDT_ADMIN_DATA ......................................................................................................... 46


TO_STRING .................................................................................................................................... 46

8.4

Interface IF_FDT_DATA_EXCHANGE_INTERNAL ............................................................................. 47

Check Methods ................................................................................................................................... 48


3

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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
12

Tools for supporting the generation development and maintenance .............................................. 63


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

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

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 customers 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.
6

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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

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.

10

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

Note
The configuration of an application server and prerequisites for sending emails are described in
SAP Note 455140.

11

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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:

12

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

4. Our new database table, FDT_ACTN_6000:

13

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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.

14

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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

15

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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.

16

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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:

Object Category

Transport Object

Customizing

FDT0000

System

FDT0001

BCF (optional)

FDT0002

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.

17

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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.

18

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

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
BEFORE_EXP
ZDMPRELOCK

FDT_AFTER_IMPORT_C
FDT_BEFORE_EXPORT_C
FDT_PRELOCK

Cross Object = X
Cross Object = X
Cross Object =

FDT_AFTER_IMPORT
FDT_BEFORE_EXPORT
FDT_PRELOCK

Cross Object = X
Cross Object = X
Cross Object = X

For S and T tables:


AFTER_IMP
BEFORE_EXP
ZDMPRELOCK

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.

19

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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.

20

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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.

21

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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.

22

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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

23

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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

24

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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.

25

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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

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

Initial Value

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.

28

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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.

29

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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
lv_version
ls_actn_6000

TYPE REF TO if_fdt_persistence,


TYPE if_fdt_types=>version,
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 ).

30

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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
db_to_buffer_format ls_actn_6000-rec_param_2
db_to_buffer_format ls_actn_6000-rec_param_3
db_to_buffer_format ls_actn_6000-rec_param_4
db_to_buffer_format
db_to_buffer_format
db_to_buffer_format
db_to_buffer_format
db_to_buffer_format
db_to_buffer_format
db_to_buffer_format
db_to_buffer_format

ls_actn_6000-msg_param_1
ls_actn_6000-msg_param_2
ls_actn_6000-msg_param_3
ls_actn_6000-msg_param_4
ls_actn_6000-msg_param_5
ls_actn_6000-msg_param_6
ls_actn_6000-msg_param_7
ls_actn_6000-msg_param_8

1
2
3
4

rs_buffer-ts_rec_parameter.
rs_buffer-ts_rec_parameter.
rs_buffer-ts_rec_parameter.
rs_buffer-ts_rec_parameter.

1
2
3
4
5
6
7
8

rs_buffer-ts_message_parameter.
rs_buffer-ts_message_parameter.
rs_buffer-ts_message_parameter.
rs_buffer-ts_message_parameter.
rs_buffer-ts_message_parameter.
rs_buffer-ts_message_parameter.
rs_buffer-ts_message_parameter.
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.
31

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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.

32

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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.

33

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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
ls_parameter
lts_parameter
ls_msg
lt_msg
lv_unknown

TYPE s_buffer,
TYPE if_fdt_actn_email=>s_parameter,
TYPE if_fdt_actn_email=>ts_parameter,
TYPE if_fdt_types=>s_message,
TYPE if_fdt_types=>t_message,
TYPE abap_bool.

ls_buffer = load_buffer( ).
CHECK ( its_message_parameter
its_message_parameter
( its_recipient_parameter
its_recipient_parameter

IS
NE
IS
NE

SUPPLIED AND
ls_buffer-ts_message_parameter ) OR
SUPPLIED AND
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.

36

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

" 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_parameterposition 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_parameterposition 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( ).

37

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

38

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

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.

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

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
39

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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
iv_deep
iv_application_id

= <ls_parameter>-parameter_id
= iv_deep
= iv_application_id ).

ENDLOOP.
LOOP AT ls_buffer-ts_rec_parameter ASSIGNING <ls_parameter>.
<ls_parameter>-parameter_id = copy_on_request(
iv_id
iv_deep
iv_application_id

= <ls_parameter>-parameter_id
= iv_deep
= 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.
40

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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 selfimplemented expression types.
o

You can use report FDT_TRANS to record existing instances or


applications on a transport.

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

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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 subobject 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 subobject 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 subobjects 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.

42

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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
objects has to be included
IV_ACTIVATION_CHECK set to
objects has to be included

true means that the active version of used


into the check.
false means that the latest version of used
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_paramposition 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_paramposition INTO ls_msg-text.
append_message ls_msg rt_message.
ENDLOOP.

43

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

* 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
IMPORTING

iv_timestamp = iv_timestamp
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 ).

44

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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
IMPORTING

iv_timestamp = iv_timestamp
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*).
46

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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

47

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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
objects has to be included
IV_ACTIVATION_CHECK set to
objects has to be included

true means that the active version of used


into the check.
false means that the latest version of used
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_paramposition 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_paramposition 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.

48

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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

49

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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
lx_bcs
lr_data
lv_id
ls_buffer
lv_value
lv_main_doc
lv_param
lt_message
ls_message
ls_parameter
lv_rec_trace
lo_do_type
lo_typedescr
DATA: lv_msg_1
lv_msg_2

50

TYPE
TYPE
TYPE
TYPE
TYPE
TYPE
TYPE
TYPE
TYPE
TYPE
TYPE
TYPE
TYPE
TYPE
TYPE
TYPE

REF TO cl_bcs_message,
REF TO cx_bcs,
REF TO data,
if_fdt_types=>id,
s_buffer,
string,
string,
string,
if_fdt_types=>t_message,
if_fdt_types=>s_message,
if_fdt_actn_email=>s_parameter,
string,
if_fdt_types=>data_object_type,
REF TO cl_abap_typedescr.
string,
string,

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

lv_msg_3
lv_msg_4

TYPE string,
TYPE string.

FIELD-SYMBOLS: <lv_string>
<lv_value>
<la_data>
<lt_data>
<ls_recipient>

TYPE
TYPE
TYPE
TYPE
TYPE

string,
any,
any,
ANY TABLE,
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.

51

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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
52

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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
iv_timestamp
io_processor
io_result

=
=
=
=

io_context
iv_timestamp
io_processor
ro_result ).

53

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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.

54

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

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
=
iv_timestamp
=
io_generation_mngr =
IMPORTING et_source_code
=

lv_mail_body_name
lv_timestamp
io_generation_mngr
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

55

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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
io_target_data_object
iv_variable_name_target
iv_timestamp
io_generation_mngr
IMPORTING et_source_code

=
=
=
=
=
=

lv_tmp_result_name
lo_result_do
lv_result_do_name
lv_timestamp
io_generation_mngr
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:

56

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

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
iv_source_name
iv_target_name
iv_timestamp
IMPORTING et_source_code

=
=
=
=
=

lv_dobj_id
iv_source_do_name
iv_target_do_name
lv_timestamp
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
iv_source_name
iv_target_name
iv_timestamp
IMPORTING et_source_code

=
=
=
=
=

lv_dobj_id
iv_source_do_name
iv_target_do_name
lv_timestamp
lt_move_code

).

APPEND LINES OF lt_move_code TO et_source_code.

57

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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
IMPORTING ev_name
ev_unknown

11.2.6

= lv_recipient_id
= lv_recipient_name
= lv_unknown
).

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 &&
append lv_line to et_source_code.

58

` TYPE string.`.

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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.

59

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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.

60

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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
=
iv_create_variable =
is_value
=
iv_timestamp
=
IMPORTING et_source_code
=
APPEND LINES OF lt_def_res_code TO et_source_code.

lv_def_res_name
abap_false
s_default_result_value
lv_timestamp
lt_def_res_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


61

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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
iv_id
iv_variable_name
iv_create_variable
iv_timestamp
io_generation_mngr
iv_calling_expr_id
IMPORTING et_source_code
ev_direct_result_name
CHANGING cts_used_context_id
APPEND LINES OF lt_parm_code TO et_source_code.

=
=
=
=
=
=
=
=
=
=

lv_function_id
<ls_para>-parameter_id
lv_parm_var_name
abap_true
lv_timestamp
io_generation_mngr
mv_id
lt_parm_code
lv_direct_parm_name
cts_used_context_id ).

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

62

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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.

63

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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 dont, 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.

64

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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.

65

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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.

66

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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
iv_name
iv_namespace_prefix
ia_abap
END-OF-DEFINITION.

=
=
=
=

io_parent
&1
gc_my_namespace_prefix
&2 ).

67

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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 dont 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
ls_msg_parameter
ls_rec_parameter
ls_buffer

TYPE
TYPE
TYPE
TYPE

abap_bool,
if_fdt_actn_email=>s_parameter,
if_fdt_actn_email=>s_parameter,
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.

68

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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.

69

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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
<!ELEMENT CUST_EXTY1:SIMPLE_VALUE

(#PCDATA)>
(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:

70

ELEMENT_TYPE

VALUE

SUPPLEMENT

NONE_AP

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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-XMLelement 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:

71

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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
= ''

72

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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
prefix

= 'SIMPLE_VALUE
= 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(

73

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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
prefix
grouping
occurrence

=
=
=
=

'ELEMENT_TYPE'
cl_fdt_actn_email=>gc_my_namespace_prefix
if_ixml_content_particle=>co_sequence
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
prefix
grouping
occurrence

=
=
=
=

'VALUE'
cl_fdt_actn_email=>gc_my_namespace_prefix
if_ixml_content_particle=>co_sequence
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
prefix
grouping
occurrence

=
=
=
=

'SUPPLEMENT'
cl_fdt_actn_email=>gc_my_namespace_prefix
if_ixml_content_particle=>co_sequence
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
prefix
grouping
occurrence

=
=
=
=

'NONE_AP'
cl_fdt_actn_email=>gc_my_namespace_prefix
if_ixml_content_particle=>co_sequence
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:

74

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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

13.3.2

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?,)

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

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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
ls_message

TYPE if_fdt_data_exchange_external=>s_xml_version_change,
TYPE if_fdt_types=>s_message.

ls_version_change-xml_version
ls_message-msgty
ls_message-text

= '1.07'. " This should be FDT XML Version


= 'I'.
= '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
lo_document
lo_node
lo_ref_element
lo_new_element
lo_element
lv_value
ls_value
lt_value

TYPE
TYPE
TYPE
TYPE
TYPE
TYPE
TYPE
TYPE
TYPE

REF TO if_ixml,
REF TO if_ixml_document,
REF TO if_ixml_node,
REF TO if_ixml_element,
REF TO if_ixml_element,
REF TO if_ixml_element,
string,
if_fdt_actn_email=>recipient,
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'

76

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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

77

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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.

78

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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.

79

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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
lo_email
lo_function
lt_message
lo_result
lr_data
lv_body
lv_recipient
lt_recipient
lv_subject
lo_trace
lo_lean_trace

TYPE
TYPE
TYPE
TYPE
TYPE
TYPE
TYPE
TYPE
TYPE
TYPE
TYPE
TYPE

REF TO if_fdt_factory,
REF TO if_fdt_actn_email,
REF TO if_fdt_function,
if_fdt_types=>t_message,
REF TO if_fdt_result,
REF TO data,
if_fdt_actn_email=>body,
if_fdt_actn_email=>recipient,
if_fdt_actn_email=>t_recipient,
if_fdt_actn_email=>subject,
REF TO if_fdt_trace,
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 = 'abc@sap.com'.
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( ).

80

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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

81

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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.

82

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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.

83

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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

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

84

= lx_fdt_backend ).

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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.

85

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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.

87

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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
lt_toolbar

TYPE if_fdt_wd_types=>s_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 ).

88

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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.

90

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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(
io_context_node = io_context_node
io_controller
= io_controller ).

(1)

TRY.
me->mo_actn_email ?= me->mo_factory->get_expression( iv_id = mv_id ).
CATCH cx_fdt INTO lx_fdt.
cl_fdt_wd_service=>convert_backend_exception( ix_fdt = lx_fdt ).

(2)
(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 TRYCATCH 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.
ls_model_node-read_only = abap_false.
INSERT ls_model_node INTO TABLE rt_model_node.

(1)
(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.

91

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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
lx_fdt
lt_recipients
lts_rec_param
ls_rec_param
ls_recipients_model
ls_icon
lv_body
lv_subject
lv_text
ls_message_model

TYPE REF TO if_wd_context_node,


TYPE REF TO cx_fdt,
TYPE
if_fdt_actn_email=>t_recipient,
TYPE
if_fdt_actn_email=>ts_parameter,
TYPE
if_fdt_actn_email=>s_parameter,
TYPE
fdts_wd_actn_email_recp_model,
TYPE
cl_fdt_wd_icons=>s_icon,
TYPE
string,
TYPE
string,
TYPE
string,
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.

92

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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.

93

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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_modelrecipients 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 reimplemented and not the interface IF_FDT_WD_MODEL method.

94

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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
ls_message_model
TYPE
ls_parameter
TYPE
lts_parameter
TYPE
lt_recipients
TYPE
lt_recipients_final TYPE
ls_recipients
LIKE
lt_recipients_model TYPE
ls_recipients_model TYPE
ls_bs_model
TYPE
lx_fdt
TYPE
lv_recipients_model TYPE
lt_plc_hldr
TYPE
lo_query
TYPE
lt_ids
TYPE
lt_token
TYPE
ls_token
TYPE
lv_body
TYPE
lv_message
TYPE

REF TO

if_wd_context_node,
fdts_wd_actn_email_msg_model,
if_fdt_actn_email=>s_parameter,
if_fdt_actn_email=>ts_parameter,
if_fdt_actn_email=>t_recipient,
if_fdt_actn_email=>t_recipient,
LINE OF lt_recipients,
STANDARD TABLE OF fdts_wd_actn_email_recp_model,
fdts_wd_actn_email_recp_model,
fdts_wd_actn_email_bs_model,
REF TO cx_fdt,
string,
match_result_tab,
REF TO if_fdt_query,
REF TO if_fdt_types=>t_object_id,
cl_fdt_actn_email=>ts_token,
cl_fdt_actn_email=>s_token,
string,
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 ).
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_modelrec_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_modelrec_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_modelrec_3 IS NOT INITIAL..

(3)

95

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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_modelrec_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 DDICtype 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:
96

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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.

97

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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.

98

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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.

99

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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:

100

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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(
is_object_type = ls_object_type

(2)

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

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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.


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.

(1)

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.

102

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

(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.
103

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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.

16.5.3

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.

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.

104

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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:

105

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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(

106

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

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.

107

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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&sapclient=000&ID=00132120A5A802DB87A5A05EEFFC987B
108

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

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.

109

CUSTOM EXPRESSION TYPES AND ACTION TYPES A STEP-BY-STEP GUIDE WITH EXAMPLE

17 Related Content

110

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

www.sap.com

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.

Potrebbero piacerti anche