For someone coming from the Java world, the way JCO 2.1 Tables are processed may seem a bit clumsy. Instead of

if (!table.isEmpty()) {

    do {

          process(table);
          // this may possibly fill the screen (if it does one might think about refactoring)

    } while (table.nextRow());

}

it would be more intuitive to have something like for SQL ResultSets. You should not have to check for an empty table and you should be able to use a while-loop instead of do-while:

while (table.next()) {

    process(table);

}

Another convenient way to loop over a Table, Collection, etc. would be using a for-each-loop:

for (Record record : table) {

    process(record)

}

(of course, one needs to be aware that the record is actually the same object as the table but with some “cursor” moved to the next position).

Until someone at SAP decides that this would be a good thing to add to JCO tables, one way to be able to use the for-each-loop style is to create a JCO Table Iterator from a given table (or a given function & table name) and loop over that iterator, as follows:

for (Record record : new JCOTableIterator(table)) {

    process(record)

}

or

for (Record record : new JCOTableIterator(function, tableName)) {

    process(record)

}

Below, you find a simple version of such a JCOTableIterator (it does not care about NullPointerExceptions and so on).

Any comments, ideas, improvements on this code would be highly appreciated.

package com.ascom.sap;

import java.util.Iterator;

import com.sap.mw.jco.JCO;

/**

* An iterator class to iterate over a JCO Table with a for-each-loop.

*/

public class JCOTableIterator implements Iterator<JCO.Table>, Iterable<JCO.Table> {

    private final JCO.Table table;

    private boolean firstRow;

    /**

     * Creates a table iterator for the given function and table name.

     *

     * @param tableName table name

     * @param function function

     */

    public JCOTableIterator(JCO.Function function, String tableName) {

        this(function.getTableParameterList(), tableName);

    }

    /**

     * Creates a table iterator for the given parameter list and table name.

     *

     * @param paramList parameter list

     * @param tableName table name

     */

    public JCOTableIterator(JCO.ParameterList paramList, String tableName) {

        this(paramList.getTable(tableName));

    }

    /**

     * Creates a table iterator for the table.

     *

     * @param table table

     */

    public JCOTableIterator(JCO.Table table) {

        this.table = table;

        table.firstRow();

        // for the first row, next() may not call nextRow()

        firstRow = true;

    }

    @Override

    public void remove() {

        table.deleteRow();

    }

    @Override

    public JCO.Table next() {

        if (firstRow) {

            // do not call nextRow() but remember to do so next time next() is called

            firstRow = false;

        } else {

            table.nextRow();

        }

        return table;

    }

    @Override

    public boolean hasNext() {

        // if there is no row at all, isEmpty() has to be checked

        // if there is exactly one row, isLastRow() is true for the first row; therefore hasNext() should be true if firstRow is true

        // otherwise, isLastRow() works as expected

        return !table.isEmpty() && (!table.isLastRow() || firstRow);

    }

    @Override

    public Iterator<JCO.Table> iterator() {

        return this;

    }

}

To report this post you need to login first.

5 Comments

You must be Logged on to comment or reply to a post.

  1. Markus Tolksdorf

    Hi Daniel,

    You are referring to JCo 2.1. As this version is only supported for Java 1.3 an 1.4, a for each loop cannot be supported for it from the API directly. Actually, support for it has ended 31 March, 2013.

    JCo 3.0 offers for-each support for a single table row, i.e. you can loop over the fields of a record via such a loop.

    Looping over the rows of a table works best with a for loop:

    JCoTable table=function,getTableParameterList().getTable(myTable);

    for (int i=0; i<table.getNumRows(); i++)

    {

         table.setRow(i);

         //the for-each loop works here, however better performance is provided

         //by accessing fields directly from the table e.g. with getString(i)

         for (JCoField field : table)

             processFiekl();

    }

    Best regards,

    Markus

    (0) 
    1. Daniel Klink Post author

      Hi Markus,

      thank you for your input. I just checked, and apparently I am using JCo 2.1.8 as it is provided in the current version of the (formerly Syclo) Agentry Workmanager for SAP. I probably cannot use a newer version in combination with the Workmanager, can I?

      Regards, Daniel

      (0) 
      1. Markus Tolksdorf

        HI Daniel,

        I don’t know Agentry WorkManager in detail, but I guess you cannot upgrade as JCo 3.0 has an incompatible API and programming model and I guess the WorkManager is using the JCo internally as well. 

        Best regards,

        Markus

        (0) 
          1. Michael Appleby

            Hi Daniel,

            Please open a Discussion marked as a question.  This will bring more Agentry/Work Manager savvy folks in.  Make sure to use the tags, “agentry” and “work_manager”.  Comments on documents often do not get the same scrutiny as Discussions since the majority fall into “Wow, this is a great document” and “thanks for providing this”.  If you do not get a response in a day or so, ping me using DM (Direct Message) and I will steer someone to review it.

            Regards, Mike (Moderator)

            SAP Customer Experience Group – CEG

            (0) 

Leave a Reply