Welcome back to our course on BPath. As you may remember from the first lesson (BPath. Lesson 01: What is BPath?) we introduced BPath and we discovered the first easy BPath statements.
Now we face the question, how our BPath statements are included into our ABAP environment. The following code example coding should illustrate the ways to access BOL with BPath.
At first we need a Business Collection where we start from:
DATA lv_compset type CRMT_GENIL_APPL.
DATA lv_bol_core TYPE REF TO CL_CRM_BOL_CORE.
DATA lv_dyn_query TYPE REF TO CL_CRM_BOL_DQUERY_SERVICE.
DATA lv_result type ref to IF_BOL_ENTITY_COL.
lv_compset = 'UIF'.
lv_bol_core = cl_crm_bol_core=>get_instance( ).
lv_bol_core->start_up( lv_compset ).
lv_dyn_query = cl_crm_bol_dquery_service=>get_instance(
iv_attr_name = 'MAX_HITS' iv_value = '5' ).
lv_result = lv_dyn_query->get_query_result( ).
At the end of this code fragment, we have a Business Collection which should contain 5 query result objects. Unfortunately BPath does not work on Business Collection directly, so we have to convert it to a Business Collection of uniform type first:
DATA lv_result2 type ref to CL_CRM_BOL_ENTITY_COL_UT.
lv_result2 ?= CL_CRM_BOL_ENTITY_COL_UT=>Convert( lv_result ).
Above statement constructs the required Business Collection with uniform type. To fetch a data result with a BPATH query from it, the following syntax has to be used:
Data lv_bpath_result type ref to data.
Data lv_sourcecode type string.
lv_sourcecode = 'SearchResFlightRel/FlightBookRel/*$'.
lv_bpath_result = lv_result2->GET_PROPERTIES_BY_BPATH(
IV_BPATH_STATEMENT = lv_sourcecode ).
If you want to execute the BPath query on a single object, the syntax is similar:
DATA: lv_element type ref to CL_CRM_BOL_ENTITY.
lv_element = lv_result->GET_LAST( ).
lv_bpath_result = lv_element->GET_PROPERTIES_BY_BPATH(
IV_BPATH_STATEMENT = lv_sourcecode ).
Before we get back to some more examples, some words on the Interpreter. BPath statements are directly translated into action (so it’s no compiled language). At the very beginning, it was implemented as top-down parser using recursive descent. But after the first enhancements, it turned out that this approach is hardly maintainable and not extendable. So the parser was migrated to a stack machine.
The parser is implemented in the CL_WCF_BPATH_PARSER class. At first the scanner (method SCAN_NEXT) comes into picture which scans the input token by token. The scanner works context free, in means the result only depends on the currently processed characters and nothing else.The parser itself works in two passes. The first pass parses only the syntax without knowing anything about the semantics (it even does not know whether a specified relation or attribute name is valid), the second pass does the action. The parser uses two control tables which are built up at the beginning. The first holds all identifiers, identifiers are a level above tokens, not completely matching. The second table holds the possible status transistions controlling the parsing. In theory all parsing is done table controlled and only the action is done “coded”. But I guess in practise things are not that clean, but I haven’t checked now.
The parsing includes a lot of going forward and backward as for each entity the corresponding BPath fragment has to be evaluated again. To avoid too much operations for scanning, the first pass stores the intermediate results in a table also. There are mechanisms to avoid the assembly of the internal tables in case the data is already available.
As the recursive information is no more hold implecitely within the calling hierarchy, it has to be held explicitely. To do so a new stack class called CL_WCF_BPATH_STACK is used, implementing the stack with some pretty uncommon inventions. BPath actually uses quite a lot of stacks during processing.
The language itself does not differentiate between upper and lower case except where the underlying layer requires it (e.g. BOL is using case sensitive relation names). All elements must succeed directly after each other, it is not allowed to use blanks or carriage returns for separation.
Code Example 7, ~STRUC, normal structures
In the examples of the last lesson the structure of the returned entity is set implicitly. It refers simply to the attribute structure of the corresponding object. Sometimes this is not required or wanted. So it is possible to set the structure explicitly as above.
The example uses the key structure which consists of 4 fields. The data is always filled with a move-corresponding statement (also when structures match). It is responsibility of the caller that the structures match, i.e. that the move-corresponding does not try to copy data to fields with the same name but of different type.
It is recommended to place the structure definition directly at the beginning, even though the syntax allows otherwise. With the first access to the structure, it has to be defined of course.
Code Example 8,  (basic filtering)
The [ ] clasps the filtering. The further evaluation is only done if the filtering can be done successfully. In our example we exclude all entries which are not the second entry within the FlightBookRel collection.
The filtering always relates to the most current relation. If we use the above source code on a collection the statement would return the second booking for every flight of the collection.
You may filter on every relation you specify. Syntactically it is correct to put several filters on one relation, eventough the result will be the same as if you use a corresponding conditional filter.
Code Example 9, [@CLASS=”C”] basic conditional filtering, string literals
There are two ways to filter, the first is operation on the index as in the previous chapter, the second is using boolean operations. If the statement is evaluated to true we proceed. Otherwise we check the next entry.
Please note, that string laterals are built using double quotes. It is possible to mask a double quote by stating two double quotes after each other.
Of course, the filtering is not restricted to simple comparisons with the equal operator. Let us check the next example:
Code Example 10, [(@CLASS<>”C”) &(@ FORCURKEY=@LOCCURKEY)], Comparisons and Bool-OPs
As you see it is also possible to use other comparison operators ( more exactly <, >, <=, >=, <>, = ). And it is possible to use the boolean functions and (with the & symbol) and or (using | ).
Code Example 11, [(@SMOKER+@INVOICE)=”X”], basic arithmetic operations
This example uses arithmetic operators, in this case as string concatenation operator. Both fields used are either “X” or empty so the expression basically evaluates to true if exactly one of the flags is set.
Please note that all operations are only working with 2 operands. An ‘or’ with three operands has to be expressed using appropriate brackets.
Beside above noted boolean and comparison operators the standard operators + (addition), – (substraction), * (multiplication) and % (division) are supported. The full operation table will be provided in the next lesson.
In general BPath operates with 4 data types: Strings, Dates, Numerics and Booleans. Within an intermediate value of an expression any of the above types may be used, but on the highest level of a filter it has to be either a boolean or a numeric value. For a numeric value an implicit check against the index is done. Note that negative value may be used also, as -1 represents the last entity of the collection (-2 consequently the second last, and so on).
With the next lesson we do something completely different. Assignments.