Table of Contents

Chapter 5: Tabular data
Initial properties and emphasize rows
Filters and base condition
Pure SQL select
Default order

Chapter 5: Tabular data

Tabular data is data that is displayed in table format. If you create a conventional OpenXava module, then the user can manage the component data with a list like this:
tab_en010.jpg
This list allows user to:
The default list is enough for many cases, moreover the user can customize it. Nevertheless, sometimes it is convenient to modify the list behavior. For this you have the @Tab annotation within the entity definition.
The syntax of @Tab is:
@Tab(
    name="name",                     // 1
    filter=filter class,             // 2
    rowStyles=array of @RowStyle,    // 3
    properties="properties",         // 4
    baseCondition="base condition",  // 5
    defaultOrder="default order"     // 6
)
public class MyEntity {
 
  1. name (optional): You can define several tabs in a entity (using @Tabs annotation), and set a name for each one. This name is used to indicate the tab that you want to use (usually in application.xml).
  2. filter (optional): Allows to define programmatically some logic to apply to the values entered by user when he filters the list data.
  3. rowStyles (optional): A simple way to specify a different visual style for some rows. Normally to emphasize rows that fulfill certain condition. You specify an array of @RowStyle, in this way you can use several styles for a tab.
  4. properties (optional): The list of properties to show initially. Can be qualified (that is you can specify referenceName.propertyName at any depth level).
  5. baseCondition (optional): Condition to be fulfilled by the displayed data. It's added to the user condition if needed.
  6. defaultOrder (optional): To specify the initial order for data.

Initial properties and emphasize rows

The most simple customization is to indicate the properties to show initially:
@Tab(
    rowStyles=@RowStyle(style="highlight", property="type", value="steady"),
    properties="name, type, seller.name, address.city, seller.level.description, address.state.name"
)
 
These properties are shown the first time the module is executed, after that the user will have the option to change the properties to display. Also you see how you can use qualified properties (properties of references) in any level.
In this case you can see also how to indicate a @RowStyle; you are saying that the object which property type has the value steady will use the style highlight. The style has to be defined in the CSS style-sheet. The highlight style are already defined in OpenXava, but you can define more.
The visual effect of above is:
tab_en020.jpg

Filters and base condition

A common technique is to combine a filter with a base condition:
@Tab(name="Current",
    filter=CurrentYearFilter.class,
    properties="year, number, amountsSum, vat, detailsCount, paid, customer.name",
    baseCondition="${year} = ?"
)
 
The condition has to have SQL syntax, you can use ? for arguments and the property names inside ${}. In this case a filter is used to set the value of the argument. The filter code is:
package org.openxava.test.filters;
 
import java.util.*;
 
import org.openxava.filters.*;
 
/**
 * @author Javier Paniza
 */
 
public class CurrentYearFilter implements IFilter {                  // (1)
 
    public Object filter(Object o) throws FilterException {      // (2)
        Calendar cal = Calendar.getInstance();
        cal.setTime(new java.util.Date());
        Integer year = new Integer(cal.get(Calendar.YEAR));
        Object [] r = null;
        if (o == null) {                                     // (3)
            r = new Object[1];
            r[0] = year;
        }
        else if (o instanceof Object []) {                   // (4)
            Object [] a = (Object []) o;
            r = new Object[a.length + 1];
            r[0] = year;
            for (int i = 0; i < a.length; i++) {
                r[i+1]=a[i];
            }
        }
        else {                                               // (5)
            r = new Object[2];
            r[0] = year;
            r[1] = o;
        }
 
        return r;
    }
 
}
A filter gets the arguments of user type for filtering in lists and for processing, it returns the value that is sent to OpenXava to execute the query. As you see it must implement IFilter (1), this force it to have a method named filter (2) that receives a object with the value of arguments and returns the filtered value that will be used as query argument. These arguments can be null (3), if the user does not type values, a simple object (5), if the user types a single value or an object array (4), if the user types several values. The filter must consider all cases. The filter of this example adds the current year as first argument, and this value is used for filling the arguments in the baseCondition of the tab.
To sum up, the tab that you see above only shows the invoices of the current year.
Another case:
@Tab(name="DefaultYear",
    filter=DefaultYearFilter.class,
    properties="year, number, customer.number, customer.name, amountsSum, " +
        "vat, detailsCount, paid, importance",
    baseCondition="${year} = ?"
)
 
In this case the filter is:
package org.openxava.test.filters;
 
import java.util.*;
 
import org.openxava.filters.*;
 
/**
 * @author Javier Paniza
 */
 
public class DefaultYearFilter extends BaseContextFilter {          // (1)
 
    public Object filter(Object o) throws FilterException {
        if (o == null) {
            return new Object [] { getDefaultYear() };              // (2)
        }
        if (o instanceof Object []) {
            List c = new ArrayList(Arrays.asList((Object []) o));
            c.add(0, getDefaultYear());                             // (2)
            return c.toArray();
        }
        else {
            return new Object [] { getDefaultYear(), o };           // (2)
        }
    }
 
    private Integer getDefaultYear() throws FilterException {
        try {
            return getInteger("xavatest_defaultYear");              // (3)
        }
        catch (Exception ex) {
            ex.printStackTrace();
            throw new FilterException(
            "Impossible to obtain default year associated with the session");
        }
    }
 
}
This filter extends BaseContextFilter, this allow you to access to the session objects of OpenXava. You can see how it uses a method getDefaultYear() (2) that call to getInteger() (3) which (as getString(), getLong() or the more generic get()) that allows you to access to value of the session object xavatest_defaultYear. This object is defined in controllers.xml this way:
<object name="xavatest_defaultYear" class="java.lang.Integer" value="1999"/>
The actions can modify it and its life is the user session life but it's private for each module. This issue is treated in more detail in chapter 7.
This is a good technique for data shown in list mode to depend on the user or the configuration that he has chosen.
Also it's possible to access environment variables inside a filter of type BaseContextFilter, using getEnvironment() method, just in this way:
new Integer(getEnvironment().getValue("XAVATEST_DEFAULT_YEAR"));
For learning more about environment variables see the chapter 7 about controllers.

Pure SQL select

You can write the complete select statement to obtain the tab data:
@Tab(name="CompleteSelect",
    properties="number, description, family",
    baseCondition=
        "select" +
        "    ${number}, ${description}, XAVATEST@[email protected] " +
        "from " +
        "    XAVATEST@separator@SUBFAMILY, XAVATEST@separator@FAMILY " +
        "where " +
        "    XAVATEST@[email protected] =  " +
        "    XAVATEST@[email protected]"
)
 
Use it only in extreme cases. Normally it is not necessary, and if you use this technique the user cannot customize his list.

Default order

Finally, setting a default order is very easy:
@Tab(name="Simple", properties="year, number, date",
    defaultOrder="${year} desc, ${number} desc"
)
 
This specified the initial order and the user can choose any other order by clicking in the heading of a column.