Thomas Weller
Thomas Weller

Reputation: 59279

Number of filled rows in a SapTable

I'm using Silk4J and I have a table which is reported as SapTable in the Locator Spy. From that table, I'm trying to get all the texts of the second column, but it hangs or terminates with an exception. In the following you find the code for my tries. Finally I reached the last row of the table, but it hangs there again.

In all examples I'm using a while loop instead of a for loop, because I want to insert more conditions later.

Try 1: Straight forward (I thought)

    SapTable table; // initialized somewhere else
    int maxrows = table.getRowCount();
    int row = 0;
    while (row < maxrows)
    {
        String text = table.getCell(row, COLUMN).getText();
        logger.debug(text);
        row++;
    }

However, this code prints all visible columns, then hangs.

Try 2: adding a PageDn keypress via Silk

Since try 1 printed only the visible cells, I thought adding a keypress every page could help. That was my code:

SapTable table; // initialized somewhere else
int maxrows = table.getRowCount();
int row = 0;
int visibleRows = table.getVisibleRowCount();
table.setFocus();
while (row < maxrows)
{
    String text = table.getCell(row, COLUMN).getText();
    logger.debug(text);
    row++;
    if (row % visibleRows == 0)
        window.sendVKey(VKey.PAGE_DOWN);
}

Unfortunately this results in an exception "The virtual key is not enabled".

Try 3: adding a PageDn keypress via AwtRobot

Since the built-in sendVKey method did not work, but pressing the PageDn manually works, I switched to an AwtRobot:

SapTable table; // initialized somewhere else
int maxrows = table.getRowCount();
int row = 0;
int visibleRows = table.getVisibleRowCount();
table.setFocus();
while (row < maxrows)
{
    String text = table.getCell(row, COLUMN).getText();
    logger.debug(text);
    row++;
    if (row % visibleRows == 0)
    {
        Robot robot = new Robot();
        robot.keyPress(KeyEvent.VK_PAGE_DOWN);
        robot.keyRelease(KeyEvent.VK_PAGE_DOWN);
    }
}

Pressing the key now works and I can see the table scroll to the next entry. However, my test application still hangs.

Try 4: Resetting the row count

Using the Locator Spy again, I found out that the index of the row is reset to zero, so I mimiced that in my code:

SapTable table; // initialized somewhere else
int maxrows = table.getRowCount();
int row = 0;
int visibleRows = table.getVisibleRowCount();
table.setFocus();
while (row < maxrows)
{
    String text = table.getCell(row, COLUMN).getText();
    logger.debug(text);
    row++;
    if (row % visibleRows == 0)
    {
        Robot robot = new Robot();
        robot.keyPress(KeyEvent.VK_PAGE_DOWN);
        robot.keyRelease(KeyEvent.VK_PAGE_DOWN);
        row = 0; // <-- 
    }
}

In this case, it prints the first N (number of visible) items of the list, scrolls to position N+1, prints the name of the first (!) row and then hangs when accessing the item with index 1 (after the reset).

Try 5: Sleeping

With some sleeping, I can reach the end of the table:

SapTable table; // initialized somewhere else
int maxrows = table.getRowCount();
int row = 0;
int visibleRows = table.getVisibleRowCount();
table.setFocus();
while (row < maxrows)
{
    String text = table.getCell(row, COLUMN).getText();
    logger.debug(text);
    row++;
    if (row % visibleRows == 0)
    {
        Robot robot = new Robot();
        robot.keyPress(KeyEvent.VK_PAGE_DOWN);
        robot.keyRelease(KeyEvent.VK_PAGE_DOWN);
        row = 0;
        Thread.sleep(1000); // <--
    }
}

In this case, I get all items in the table. But since I don't know when the table ends, it does another getCell() call, which results in a hang again.

The question

I'm really stuck. I've also looked for other methods like getting the real number of rows in the table (getRowCount() doesn't), but didn't find one yet.

How do I get the real number of rows of a SapTable in Silk4J?

Upvotes: 2

Views: 1010

Answers (2)

Sandra Rossi
Sandra Rossi

Reputation: 13629

Preamble: I have nothing to add to your answer concerning Silk4J, I just want to add my point of view as an ABAP developer concerning the SAP GUI control which corresponds to "SapTable", it may help other Silk4J developers to better understand how it works. ABAP is a well-known (old) SAP programming language. There's also a SAP library named SAP GUI Scripting which I describe. I have no Silk4J knowledge so maybe my answer is completely wrong, but after looking carefully to your question and answer, I think I can publish it.

The SAP GUI control element corresponding to "SapTable" is known by ABAP developers/SAP GUI experts as a Table Control (ABAP documentation > dynpro - Table Controls). For information, ABAP developers are using the "Dynpro" technology to create SAP GUI screens (which are named "Dynpros"). Example of Table Control:

SAP GUI Table Control

This example shows a total of 3 rows, and the number of rows is 8 (rows visible in each "page").

As you said, the backend system sends only the visible lines of a table to Table Controls at the frontend, and the user must scroll to call the backend and get the next visible lines.

The method GetVisibleRowCount returns this exact information.

It's best practice in ABAP development to store the total number of rows in the technical properties of the Table Control, so that the scrollbar shows this information (a small position cursor means many rows in the Table Control, the user may drag and drop it at the desired row). For some reason, ABAP developers may choose to append few additional empty rows (usually, it's maximum one more page of empty lines), especially to let the user input new lines without having to press a button to add a line, which is reflected in this total number of rows.

The method GetRowCount should return this total number of rows.

Silk4J is probably using a SAP library named SAP GUI Scripting, which exists for both Windows and Java, where the Table Control elements correspond to the GuiTableControl objects.

  • For more information, see the documentation of GuiTableControl (SAP Help Portal > SAP GUI Scripting API > Objects).
  • Silk4J methods seem to have the exact same names as GuiTableControl methods, except a little difference for the "Properties": your Silk4J code refers to the methods GetRowCount and GetVisibleRowCount, which correspond to the read-only Properties RowCount and VisibleRowCount of SAP GUI Scripting. I guess that Silk4J is wrapping the SAP GUI properties with Get methods, plus Set methods for the writeable properties (i.e. those which are not read-only).

With SAP GUI Scripting, it's only possible to work on the visible data (to confirm what you saw with Silk4J). Scrolling is needed to tell the SAP backend to bring data visible to the frontend. Note that all the columns are sent to the frontend/to the SAP GUI Scripting (so I guess it's the same with Silk4J), even if the user needs to scroll horizontally to make them visible to him/her.

With SAP GUI Scripting, the methods are synchronous: when scrolling, it takes a little time to the backend server to send data to the frontend, and the scroll method waits till it's loaded. This is the part in your question ("Try 5: sleeping") that I don't understand, although it's no more present in the solution you presented (in your answer).

I had also posted this other answer, concerning how to scroll in Table Controls with SAP GUI Scripting, which may possibly help.

Upvotes: 0

tehlexx
tehlexx

Reputation: 2859

It took some trying - the underlying SAP automation API is not really helpful in this case - but here is how you can make it work:

private List<String> fetchItems() {
    SapTable table = desktop.find("sap.Table");
    SapVerticalScrollBar scrollBar = table.find("/SapVerticalScrollBar");

    // the scrollbar maximum value seems to be a more reliable
    // way to get the number of items than getRowCount
    int itemCount = scrollBar.getMaximum() + 1;

    List<String> items = new ArrayList<String>();
    int currentAbsoluteRow = 0;
    // the first loop iterates through the table page by page
    for (int firstRowInPage = 0; 
            firstRowInPage < itemCount; 
            firstRowInPage = scrollToNextPage(firstRowInPage)) {

        // this loop goes through the items of the current page
        for (int currentRowInPage = 0; 
                currentRowInPage < table.getVisibleRowCount(); 
                currentRowInPage++) {

            if(++currentAbsoluteRow > itemCount) {
                // we've read all the available items
                return items;
            }

            SapComponent cell = table.getCell(currentRowInPage, 1);
            items.add(cell.getProperty("Text").toString());
        }
    }
    return items;
}

private int scrollToNextPage(int firstRowInPage) {
    SapTable table = desktop.find("sap.Table");
    SapVerticalScrollBar scrollBar = table.find("/SapVerticalScrollBar");
    firstRowInPage += scrollBar.getPageSize();
    scrollBar.scrollTo(firstRowInPage);
    return firstRowInPage;
}

Some pitfalls that I encountered:

  • getRowCount() returned a higher count than there was in actual items
  • getVisibleRowCount() returns the number of items that would fit on the current page, even if not all rows were filled with actual items
  • The returned cell objects are only valid as long as the cell is on the screen, so you'll need to pull the information you want before you scroll to the next page.

Upvotes: 2

Related Questions