CALL METHOD with dynamic method name, RuntimeException - abap

I try to demonstrate a CALL METHOD statement with dynamic method name on a 7.40 system. I use the following test code and get an ABAP Runtime Error in line 27. The Error Analysis in the Description of Exception states ... in the class LCL, the method "m" could not be found. But the standalone method call succeeds in calling m.
REPORT ZUTEST10.
CLASS lcl DEFINITION.
PUBLIC SECTION.
METHODS m.
ENDCLASS.
CLASS lcl IMPLEMENTATION.
METHOD m.
write / 'success'.
ENDMETHOD.
ENDCLASS.
START-OF-SELECTION.
DATA oref TYPE REF TO lcl.
CREATE OBJECT oref.
oref->m( ). " works fine
DATA name TYPE c VALUE 'm'.
CALL METHOD oref->(name). " <-- Runtime Error

In the background all method names are uppercase, so you have to call the method like this:
DATA name TYPE c VALUE 'M'.
On the other hand you can catch this exception, so the program won't dump, even if the method does not exist:
TRY.
CALL METHOD oref->(name).
CATCH cx_sy_dyn_call_illegal_method
INTO DATA(lx_illegal_method).
"handle illegal method call
ENDTRY.

Related

Declaring dynamic call of BAdi?

I want to define an object for BAdI implementation that will not initialize the BAdI by its name in declaration.
Example of what I don't want:
DATA l_split_badi TYPE REF TO fieb_get_bank_stmts_x.
Question 1 : I want something like:
DATA l_split_badi TYPE REF TO object.
lv_class_name = 'fieb_get_bank_stmts_x'.
create object l_split_badi type (lv_class_name).
If I declare like above, I get the following syntax error:
"L_SPLIT_BADI" is not a valid BAdI handle here.
The reason why I need to perform such implementation is, while importing the Change request to a system which has an older SAP version, the import fails because of BAdI declaration with TYPE REF TO (because that BAdI doesn't exist in the system).
My idea is with dynamic declaration to avoid the pre-check on importing the Change request.
Any idea is welcome !
Thanks to all !
EDIT Question 2 : after solution proposed by Sandra Rossi to use DATA l_split_badi TYPE REF TO cl_badi_base and GET BADI l_split_badi TYPE ('FIEB_GET_BANK_STMTS_X'), I get the same syntax error at line CALL BADI l_split_badi->split below:
CALL BADI l_split_badi->split
EXPORTING
i_string = lv_cont
IMPORTING
et_string = lt_xml_string
EXCEPTIONS
split_not_possible = 1
wrong_format = 2.
Question 1
For BAdIs which are part of an Enhancement Spot (display the BAdI via the transaction code SE18 and you will know), you must not use CREATE OBJECT, but instead GET BADI which has a dynamic variant:
DATA badi TYPE REF TO cl_badi_base.
TRY.
GET BADI badi TYPE ('FIEB_GET_BANK_STMTS_X')...
CATCH cx_badi_unknown_error INTO DATA(lx).
" The BAdI doesn't exist, handle the case...
ENDTRY.
EDIT: notice that the instance declaration is referencing CL_BADI_BASE, the super class of all BAdI definitions.
Question 2
Calling the method SPLIT statically is invalid because SPLIT doesn't exist in CL_BADI_BASE. You must use the dynamic variant of CALL BADI:
CALL BADI l_split_badi->('SPLIT')
EXPORTING
i_string = lv_cont
IMPORTING
et_string = lt_xml_string
EXCEPTIONS
split_not_possible = 1
wrong_format = 2.

SAPGUI not displaying long_text or text for unhandled custom exceptions

In SAP NetWeaver 7.52 I created an ABAP classed based exception that works fine while executing within a try catch clause in the report/program. But the custom message is not displayed in SAPGUI when the exception is not handled by a try catch clause.
What I'm looking for is that when no try catch is defined the exporting message used at the moment of the raise statements is shown in the "UNCAUGHT_EXCEPTION". I have tried redefining the get_text( ) and get_longtext( ) methods. But the ABAP Run time error does not give any useful information to the developer about the cause (which is stored in the "attr_message" attribute of the exception).
When using the "try catch" the message can be retrieved without problems, but the idea is that SAPGUI presents the developer the right message in the "ABAP Runtime Error" report.
zcx_adk_exception.abap
"! Base exception for all ABAP Development Kit (ADK) exceptions
class zcx_adk_exception definition public create public inheriting from cx_dynamic_check.
public section.
"! Initializes the exception message
"! #parameter message | Message related to the reason of the exception
methods constructor
importing value(message) type zadk_str optional.
"! Returns the message associated to the exception
methods get_message
returning value(result) type zadk_str.
methods if_message~get_text redefinition.
methods if_message~get_longtext redefinition.
private section.
data attr_message type zadk_str value ''.
endclass.
class zcx_adk_exception implementation.
method constructor ##ADT_SUPPRESS_GENERATION.
super->constructor( ).
if message is not initial.
me->attr_message = message.
endif.
endmethod.
method get_message.
result = me->attr_message.
endmethod.
method if_message~get_text.
result = me->get_message( ).
endmethod.
method if_message~get_longtext.
result = me->get_message( ).
endmethod.
endclass.
What works fine:
try.
raise exception type zcx_adk_exception exporting message = 'Base_Exception_Error'.
catch zcx_adk_exception into data(ex).
write: / 'Example 1:', ex->get_message( ).
write: / 'Example 2:', ex->get_text( ).
write: / 'Example 2:', ex->get_longtext( ).
endtry.
And the output is this:
What does not work:
" Not Catching the exception
raise exception type zcx_adk_exception exporting message = 'Base_Exception_Error'.
This results in the following message being displayed instead
Following the previously proposed idea of using a message I came up with the following code that allows the exception to be raised with a message. This allows the exception to show the right message when called within a "try catch" block and display a useful message in the "Error analysis" section of the dump generated by SAPGUI.
Solution:
"! Program to test functionalities and utilities
REPORT zsandbox_tests.
" Exception Class
CLASS lcl_exception DEFINITION INHERITING FROM cx_dynamic_check.
PUBLIC SECTION.
INTERFACES if_t100_dyn_msg.
METHODS if_message~get_text REDEFINITION.
METHODS constructor
IMPORTING VALUE(message) TYPE string.
PRIVATE SECTION.
DATA attr_message TYPE string VALUE ''.
ENDCLASS.
CLASS lcl_exception IMPLEMENTATION.
METHOD if_message~get_text.
result = attr_message.
ENDMETHOD.
METHOD constructor.
super->constructor( ).
me->attr_message = message.
ENDMETHOD.
ENDCLASS.
" Class that raises the exception
CLASS lcl_main DEFINITION.
PUBLIC SECTION.
CLASS-METHODS main RAISING lcl_exception.
ENDCLASS.
CLASS lcl_main IMPLEMENTATION.
METHOD main.
DATA raise_message TYPE string VALUE 'Custom Message for the Exception'.
RAISE EXCEPTION TYPE lcl_exception
MESSAGE e000(lcl_exception) WITH raise_message '' '' '' " The if_t100_dyn_msg supports 4 attributes: V1, V2, V3 and V4 but I only use the first one
EXPORTING message = raise_message.
ENDMETHOD.
ENDCLASS.
" Call to Main Method
START-OF-SELECTION.
TRY.
lcl_main=>main( ).
CATCH lcl_exception INTO DATA(ex).
WRITE ex->get_text( ).
ENDTRY.
This generates the following output:
When no try catch is used:
" Call to Main Method
start-of-selection.
lcl_main=>main( ).
This is the output:
TL;DR : SAP did not plan to permit the customization of a short dump by the developer.
A "short dump" is a report which is generated by the ABAP kernel when an unexpected error occurs in an ABAP program, i.e. an error due to a bug in the program (usually an uncaught exception, or non-catchable errors) or to a system failure (input/output resources, memory resources, etc.)
It's intended to help the developer analyze the cause of the error and correct it.
It's not intended to be generated on purpose, except in a situation that the developer has made theoretically impossible, but actually happens, and which is thought to require many information to analyze if it happens, hence a short dump.
If it's really your intention to generate a short dump with a message, for some purpose, there are two ways:
MESSAGE 'message text' TYPE 'X'. (often used in standard SAP programs, especially in update function modules)
RAISE SHORTDUMP ... or ... THEN THROW SHORTDUMP ... in conditional expression. Both exist since ABAP 7.53. For instance RAISE SHORTDUMP TYPE zcx_adk_exception EXPORTING message = 'Base_Exception_Error'.
The short dump will contain the message text in the Analysis section.

CALL METHOD and method chaining

For CALL METHOD - Static Method Call (Obsolete), the ABAP keyword documentation says: "If CALL METHOD is used for the standalone method call, no chained method calls are possible ..."
Nevertheless, the following happily executes on an 7.40 system. Isn't that an example of a standalone method call? Or else, what am I getting wrong?
REPORT ZUTEST3.
CLASS class_parent Definition.
PUBLIC Section.
METHODS m1 returning value(r) type ref to class_parent.
ENDCLASS.
CLASS class_parent Implementation.
Method m1.
create object r.
write / 'm1'.
EndMethod.
ENDCLASS.
start-of-selection.
data cl type ref to class_parent.
CREATE OBJECT cl.
CALL METHOD cl->m1( )->m1( ).
Edit: Disclaimer
We are writing a tool in Java that parses and transforms ABAP code. In particular, we have no intention to write new ABAP code. But instead, our tool has to handle all of ABAP, even obsolete statements and obscure syntax variants. Furthermore, I'd like to mention that I'm not an ABAP expert.
Addendum February 23rd, the right answer is given by Florian in the comments : "I reported the bug to the docu team and they answered that it has already been reported and they corrected it in the latest version. The new statement is: "With the second variant without round brackets, chained method calls are not possible and the operators NEW and CAST cannot be used.""
I let my original answer below (by the way, I think now that in CALL METHOD static_meth..., the term "standalone method call" refers to the part "static_meth", so it refers to the two groups of constructs, hence my answer is inexact and the one by SAP is 100% correct)
As I can see, the documentation says that the term "standalone method call" refers to these constructs (observe the use of parentheses), which are declared obsolete :
CALL METHOD method( ).
CALL METHOD method( 25 ).
CALL METHOD method( a = 1 ).
CALL METHOD method( EXPORTING a = 1 ).
CALL METHOD instance->method( ).
CALL METHOD class=>method( ).
etc.
The term ""standalone method call" doesn't refer to these constructs :
CALL METHOD method.
CALL METHOD method EXPORTING a = 1.
CALL METHOD instance->method.
CALL METHOD class=>method.
etc.
I guess that CALL METHOD cl->m1( ) belongs to the first group of constructs so there's an error in the documentation.
Probably a not is missing because it should apply to the second group of constructs (for instance, CALL METHOD method->method( ) is invalid).
Conclusion by me: you should read "If CALL METHOD is not used for the standalone method call, no chained method calls are possible ..."
Conclusion by Florian & SAP: In the comments below, Florian has asked the SAP support and indicates which exact sentence SAP should use in the next official release of the documentation
ADDENDUM (read it if you think wrongly that the documentation page is about "static methods", I hope I will make it clear that it's not).
The answers in this question prove that the documentation "CALL METHOD - Static Method Call (Obsolete)" is quite confusing.
The documentation title: here "static method call" means "static call of methods", not "call of static methods" (while at other places it could possibly have this meaning). If we could add parentheses in the written language, that would give these two possibilities, respectively :
static (method call) : static call of a method (whatever this method is of type "static" or "instance"; we could have a static call of an instance method)
(static method) call : call of a static method
Definitions :
static call : the class, interface or method names are "hardcoded" as symbols in the source code, not text literals, so that they are known by the compiler (for instance, CALL METHOD class=>method.). The opposite, a dynamic call, means that the names are passed through variables, which are only known at runtime (for instance, DATA classvar TYPE seoclsname VALUE 'CL_ABAP_TYPEDESCR'. CALL METHOD (classvar)=>(methodvar).) This other documentation page shows well that "static method call" is opposed to "dynamic method call", it never talks about "static and instance methods", only about "static method call" and "dynamic method call".
static method : a method being declared with CLASS-METHODS. For instance, a static call could be cl_ixml=>create( ), and a dynamic call could be DATA classvar TYPE seoclsname VALUE 'CL_IXML'. CALL METHOD (classvar)=>create.
Something in the documentation which confused me too, is the use of the term "static method" and examples based on static method only, because in fact the documentation page is about "static call", not static methods (instance methods could have been used) :
Syntax : CALL METHOD { static_meth( ) | static_meth( a ) | ... : what does mean "static_meth" here? In fact "static_meth" doesn't mean that it's a static method but it's any method in the context of a static method call. If you look at the documentation pages about "static calls" and "dynamic calls", you will see that the syntax is respectively static_meth( ) ... and CALL METHOD dynamic_meth ...
Example : a static method is again used in the three calls, all three of them having the same exact meaning but written with different syntaxes, to demonstrate that the first two calls are obsolete, and only the third one is recommended. In fact all three examples should have better used an instance method to avoid the confusion!
First of all, the method m1 is not static in your example and the quotation from the documentation has it that it is about a static method (CLASS-METHOD).
The only possible thing might be like in this example.
REPORT zutest3.
CLASS class_parent DEFINITION.
PUBLIC SECTION.
METHODS m1 RETURNING VALUE(r) TYPE REF TO class_parent.
CLASS-METHODS m1_static RETURNING VALUE(r) TYPE REF TO class_parent.
ENDCLASS.
CLASS class_parent IMPLEMENTATION.
METHOD m1.
CREATE OBJECT r.
WRITE / 'm1'.
ENDMETHOD.
METHOD m1_static.
CREATE OBJECT r.
WRITE / 'm2'.
ENDMETHOD.
ENDCLASS.
START-OF-SELECTION.
* this seems to be possible but no one sane calls a static method on an object reference
CALL METHOD class_parent=>m1_static( )->m1_static( ).
* the following two are not possible and will not compile either
* CALL METHOD class_parent=>m1_static( )=>m1_static( ).
* class_parent=>m1_static( )=>m1_static( ).
Second of all the CALL METHOD statement in this case is just a redundancy and its role is only informative.
Those two are equivalents
CALL METHOD cl->m1( ).
cl->m1( ).
analogically to for example this
DATA i TYPE i.
COMPUTE i = i + 1.
i = i + 1.
A bug in the docu. I reported it to the docu team and they answered that it has already been reported and they corrected it in the latest version.
The new statement is:
With the second variant without round brackets, chained method calls
are not possible and the operators NEW and CAST cannot be used.

Single-line method calls with untyped parameters

Can I define an ABAP method where the RETURNING parameter and any IMPORTING parameters have a generic type but that can still be called in a single line as a functional method?
In other words I'd like to replace this:
CALL FUNCTION 'CONVERSION_EXIT_ALPHA_INPUT'
EXPORTING
input = lv_external_value
IMPORTING
output = lv_internal_value.
With:
lv_internal_value= zcl_conversion=>alpha_input( lv_external_value ).
Unfortunately the fact that Class Methods can't have an untyped returning parameter is preventing me from declaring the functional method's return value as type ANY or CLIKE. The accepted standard of creating generic method parameters seems to be to define them as TYPE REF TO DATA and dereference/assign them. But as far as I know that prevents me from calling the method in a single statement as I have to first assign the importing parameter and then dereference the returning parameter, resulting in the same or more lines of code than a simple FM call.
Is there a way around this?
Unfortunately, there is no other way to dereference data than to use the dereference operator, either in the form ->* for the full value segment, or in the form ->comp, if the data object is structured and has a component named comp (and, even worse, there are a lot of places in ABAP code where you would like to use a value from a derefenced data object but can't do it for internal reasons / syntax restrictions).
However, you could simply keep the data reference object retrieved by your method in a variable of the calling code and work with that variable (instead of using a field symbol or a variable for the derefenced value segment itself). Either generically, as a ref to data variable, or typed, using the CAST operator (new ABAP syntax).
Most things that can be done with a field-symbol, can also be done directly with a data reference as well.
Example: Working with a variable result of the expected return type:
data(result) = cast t000( cl=>m( ) ).
write result->mandt.
See here the full example:
report zz_new_syntax.
class cl definition.
public section.
class-methods m returning value(s) type ref to data.
endclass.
start-of-selection.
data(result) = cast t000( cl=>m( ) ).
write: / result->mandt. " Writes '123'.
class cl implementation.
method m.
s = new t000( mandt = '123' ).
endmethod.
endclass.
On ABAP NW Stack 7.4 you could just use parameters type STRING and then use the new CONV Operator to convert your actual input in string. Little ugly but should work.
lv_internal_value = CONV #(zcl_conversion=>alpha_input( CONV #(lv_external_value) )).

How to dynamically call a method in python?

I would like to call an object method dynamically.
The variable "MethodWanted" contains the method I want to execute, the variable "ObjectToApply" contains the object.
My code so far is:
MethodWanted=".children()"
print eval(str(ObjectToApply)+MethodWanted)
But I get the following error:
exception executing script
File "<string>", line 1
<pos 164243664 childIndex: 6 lvl: 5>.children()
^
SyntaxError: invalid syntax
I also tried without str() wrapping the object, but then I get a "cant use + with str and object types" error.
When not dynamically, I can just execute this code to get the desired result:
ObjectToApply.children()
How to do that dynamically?
Methods are just attributes, so use getattr() to retrieve one dynamically:
MethodWanted = 'children'
getattr(ObjectToApply, MethodWanted)()
Note that the method name is children, not .children(). Don't confuse syntax with the name here. getattr() returns just the method object, you still need to call it (jusing ()).