6.5 Widget Models

A Widget Model is the primary mechanism to exchange data between the XMA-client and the XMA-server. If you think of an UI control, for example an entry field (text field), according to the MVC paradigm we distinguish three areas of responsibility:

When it comes to programming an application, the model aspect often is the most important one. Layout (view) and event handling (controller) more often than not defaults to standard behaviour and standard look and feel, but the model must be manipulated and queried regularly to move data (and selection status).


Therefore, SWT widgets and XMA models always appear in pairs at the XMA client. SWT widgets manage the view and controller aspect, but the XMA widget models take over the responsibility of the model aspect. Although SWT-widgets naturally also have models, they are not used by the XMA programmer. The reason for this separation of concerns is: View and Controller are bound to the UI-library at the XMA client, whereas the XMA widget model is not. Moreover, the model is automatically synchronized to the XMA server so that the model (data and selection status) may be manipulated and queried within the web application centrally hosted in an application server in the data center.

If SWT-widgets and XMA widget models come in pairs, what must be programmed where? There are the following rules:

[Important]

What may be programmed using widget models,must not be done on SWT widgets! Example: The ISimpleWMClient from above has methods to set and query many data types (Booleans, Dates, Timestamps, Numbers, and so on). Doing so has an immediate result on the UI, where the data types are formatted and user input is validated according to type rules. The SWT widget Text also has a method to set a string into the field. Since you may do the same on the widget model, you must do it there.

[Important]

The model data managed by an UI control is always manipulated and queried on the widget model.

[Important]

Managing selection information of complex controls (tables, trees, combo boxes) is always done on widget models, e.g., setting and querying the selected rows of a table or entries in a tree.

What remains to be done with SWT-widgets are very UI centric activities, the most common are:

The real benefits of widget models show up when you start programming at both XMA sides, client and server, because:

[Important]

Widget models are automatically synchronized between Xma client and XMA server. Suppose, for example, your code is executing at the server, you manipulate models and transfer control back to the client. Then, the state of the models, as exposed by the API, is the same at the client as compared to the point your thread of control left the server. The same applies to the other way round. If you modify the models using the client-API, or the end-user modifies the models through the attached widgets, the changes are reflected at the server at the point in time when you transfer control to the server.

This section lists the permissible combinations of SWT-widgets, XMA widget models and Java-Types that may be bound to widget models.

XMA widget model classes adhere to the following naming convention: All widget models are accessible through interfaces and therefore start with the character I ( I interface), followed by a string indicating what it is, for example a Tree, followed by the constant WM (WidgetModel). On client side, Client is appended. On server side the base interface is used directly. E.g., the model for a Tree control is accessible through the interface ITreeWMClient at the XMA client and via ITreeWM at the server.

The following table shows the pairs that make sense (and are generated by the UI-designer/generator). Additionally, the possible bindings to Java types at the server side of XMA are defined. See also Data Bindings.

SWT WidgetXMA Widget ModelDescriptionType-Bindings

Text

ISimpleWM

ISimpleWM holds a typed atomic value, also have a look at IAtomic. The contained types may be Numbers, Dates, Timestamps and Strings, see Types. The contained value is formatted using IFmt and the resulting text is displayed in the SWT text field. Also see Simple Widget Models.

The following table lists the allowed data types that may be bound to particular types of ISimleWM s:


Button with style SWT.RADIO

ISimpleWM

ISimpleWM must be of type Boolean, causing the radio button to be checked or not. Also see Simple Widget Models.

No binding supported, since they usually appear in groups.

Button with style SWT.CHECK

ISimpleWM

ISimpleWM must be of type Boolean, causing the check box to be checked or not. Also see Simple Widget Models.

May be bound to boolean, Boolean.

SimpleCombo a Combo without a data source.

or List .

IListWM

IListWM holds a list of typed atomic values. The list of selectable values is controlled by the programmer. All entries of the list must be of the same type. Also see List Widget Model(List Widget Models).

May be bound to all types that Text-widgets may be bound to. See first row above.

DomainCombo an editable Dropdown-Combo with a non-empty data source specification defined in the UI-designer.

IListDomWM

The displayed domain of values is drawn from a ITabularDataSource, see Data Sources.

May be bound to an String.

Table

ITableWM

The contents (rows) of the ITableWM is fully controlled by the programmer and may be manipulated at the client and at the server.

Columns may be bound to any of the types mentioned above. If at least one column is bound, the table must be additionally bound to an attribute that provides the table key . The attribute is then mapped to a String by calling String.valueOf() -method at runtime. The String generated in this way is then used as table-key.

Tree

ITreeWM

Freely modifiable hierarchical model with optional image icons.No binding supported.

Grid

IGridWM

Excel like grid control with formula support.No binding supported.
    

DatePicker(DatePicker)

ISimpleWM

ISimpleWM must be of the type T_DATE. A DateValidator has to be used as validator. Also see: ADateFmt

Must be bound to java.util.Date.

Table 6.2. widget model table


A Simple Widget Model encapsulates an atomic value, e.g., an Boolean, String, Date, Timestamp or a Number. The value may be shown in check boxes and radio buttons, if the type is Boolean. Otherwise, the value is shown in a SWT Text field.


Unlike Simple Widget Models, which encapsulate a single atomic value, Complex Widget Models are the model types for multi valued UI-controls like Lists, Tables and Trees. Complex widget models are composed of two separate types of information:

The first information is specific to the particular model type, i.e., providing rows in a table requires a different API than filling the nodes of a tree. Nonetheless, the second type of information, the selection status, is treated uniformly amongst the different models and therefore gets devoted the next section.

All Complex WMs are required that their entries are identified through a Key of type String that is unique over all entries of a complex WM. Consider for example a table where you want display rows from a database. Then the best choice to provide a key is an artificial key (surrogate) from the database schema. If an artificial key is out of reach, you may use a natural key which you know is unique, although that should not be the case since SPARDAT's databases are required to use artificial keys. For example, in ADABAS, 8-byte ids are used. In relational DBMSs, like Oracle, 12-digit ids are used.

Based on this requirement, all complex widget models (tables, lists, trees) implement the interface ISelectable. It allows to query and manipulate the selection status at both sides of XMA, the client and the server. The methods to modify the selection status ( select(String key), deselect(String key) ) and the methods to query the selection ( getSelected(), getSelection() ) all deal with the before mentioned String keys.

The advantage of using String-keys for all entries is that you do not have to remember a mapping from keys to indexes of, for example, a SWT-table. You are allowed to program at a more conceptual level. Sooner or later, when you have to access the database again to query more information for a selected entry, you will need the selected key anyway.

An ISelectable is either single - or multiselect capable. The property is defined at UI-design time and may be queried using the method isMultiSelect().

An ISelectable is either strict - or not. This property is set automatically depending on the widget model type and defines if, for a particular key, there must also be an entry in the widget model. An example where a widget model's selection need not to be strict are domain-lists. Suppose, the list consists of the values {(ma,married), (si,single)} , where legitimate keys are ma and si . But getSelected() might return the key de because some time ago the domain of values also included the entry (de,dead) , that was deleted in the meantime. Nevertheless, the key de is still present in many database rows. DBMS folks would say that we are missing referential integrity, which is true, but that's reality.

[Tip]

You should use artificial keys in your database tables .

[Tip]

You should use your database table's key also to identify entries in complex widget models.

[Tip]

The keys should be as short as possible , as they are transmitted over the wide area network to exchange data between the XMA client and the XMA server.

[Caution]

You should not store the result of a database query as List in the server session, component or page and use the List's index as key in the complex widget model. That would consume server memory which is a scarce resource. Instead use your database keys. Then, there is no need to store the list at the server.

A List model holds a list of selectable entries. The entries are Atoms of the types T_STRING , T_BCD , T_DATE or T_TIMESTAMP . All entries of one model must be of the same type. There are convenience methods to use Strings directly.

Additionally the list model holds the current selection. Like the complex widget models it implements the interface ISelectable .

This model my be used with the SWT-Widgets Combo and List.


If you just work with Strings, you define the choices and optionally set the initial selection. For normal Strings you do not need a Validator.

    stringCombo.add(new String[] { "gut", "toll", "besser" });
    stringCombo.select("gut");    
To read back the selected value:
    String selection = stringCombo.getSelected();    
If you want to use one of the typed Atoms e.g. a Date, you have to define the validator(
Formatters/Validators)first. This is done in the guidesigner by coosing a validator or implicitly by defining a data binding(Data Bindings). Then you can define the choices in your code.
   dateCombo.add(new Atom[] {
        Atom.newInstance(Types.T_DATE,"20010101"),
        Atom.newInstance(Types.T_DATE,"20020101"),
    });    
If you defined a data binding, you can transfer all bound values for Simple Models and List Models by calling businessDataToAtomicWM(data). This sets the selection of the List Model to the value of its corresponding attribute.

If you have no data binding, you can set the selection by calling dateCombo.select("20010101"). Here you must use the internal string encoding of the desired value. You can get the internal string representation of an atom by calling toString() on it.

To read back the selection there are again two possibilities. If you have a data binding you transfer back the selected Values by calling atomicWMToBusinessData(data).

If you do not have a data binding, you can read the selected value. The key contains the internal string representation.

    String key = dateCombo.getSelected();
    Atom selectedEntry = dateCombo.getEntry(key);    

A Table Widget Model encapsulates

  1. a ordered set of rows. A row is composed of a key (String), an array of Atoms and an id of an image , that may be optionally displayed in front of the row.

  2. A selection status, see Complex Widget Models.

The code that is generated by the UI-designer/generator looks like
protected Table                myTableW = ...  // the SWT-widget
protected ITableWMClient       myTable = ...    
at the client. At the server it is almost the same, except that you cannot access the SWT-widget there:
protected ITableWMServer       myTable = ...    
The following table lists the programming tasks that you may do with table WMs.

AreaProgramming Tasks

Client and Server, (ITableWM)

  • Querying table information, like number of rows ( size() ), number of columns ( getColumnCount() ).

  • Accessing rows by key ( getRow(String key) ) or by index ( getRow(int index) ). These methods return TableRow -objects.

  • Removing rows by key ( removeRow(String key) ) or by index ( removeRow(int index) ).

  • Modifying rows is done by first retrieving a TableRow , followed by manipulative methods on the TableRow .

  • New rows are inserted either at the end or by index via direct calls to TableRow -constructors. The constructor requires a String-key, an optional image id and an array of Objects representing the cells of the row. The supported object types are String , Integer , Double , java.util.Date and Boolean .

Client only, (ITableWM Client)

At the XMA client, the following column properties are accessible:

  • sortable . Defines, if a column may be sorted by clicking on the column header. Usually set in the UI designer, but may be queried and modified in the program.

  • formatter . Defines how the column contents is displayed. Is set in the UI-designer and cannot be modified once the table is displayed.

  • visible . Defines if a column is visible. This property is set in the UI-designer and cannot be modified by API calls.

Columns may be sorted by calling the method sort(int columnIndex) . Sorting just affects the UI, not the table model, i.e., the row-order of the widget table model remains unaffected by the sort-operation, only the rows in the SWT-table get reordered. That guarantees that the model-API remains stable regardless whether or not some columns are sorted. The sort algorithm is stable. That means that the end-user may sort the rows by multiple columns by clicking at the column headers in inverse order.

Table 6.6. table tasks


For each row an image can be added to the first column by the use of TableRow.setImageId(int imageId).To use this method would mean to interfere with the code that is generated at the server side for filling a table. So there is an additional method generated <tabelname>GetImageFor(YourData data). You can override this method if you wish to calculate some registered image id from the data which is mapped to the table. This method then is called for every row.

Another possibility is to declare an XMATableColumn. as showing images (instead of the value). In this case a column tries to resolve its values to a registered image, which then is shown. If the column's value cannot be resolved then the value itself is shown at the UI. Such a column can be filled with numbers as well as Strings. It is possible to mix Strings resolving to an image (registered short values) with any other Strings in one column.Values representing '0' (Strings or numbers) are interpreted as no image and are not shown in such a column. As long as there is no support in the GUI designer for the property XMATableColumn.setImageColumn(boolean imageColumn) you can make a column showing images in this way (at the PageClient):

 public void createModels() {
    super.createModels();
    myTable.getColumn(index).setImageColumn(true);
 }

Some applications might have the need to customize a table, like setting the font and background of special cells or mixing text and images in one cell. This can be achieved by the use of the TableWM2UIListenerClient interface. If your register such an interface for a table then its method model2UIEvent(TableRow tr, TableItem ti, int modelColumn, int swtColumn) is called for every cell model to UI writing. In this method you can manipulate the TableItem in every possible way. Registering of the listener is done by ITableWMClient.html.setTableWM2UIListener(TableWM2UIListenerClient).

The following example sets an TableItem to bold if it is of a certain column and has a negative value:

   //seting the font to bold in a special cell:
   public class TableWM2UIListenerImpl implements TableWM2UIListenerClient{ //inner class of some PageClient
       public void model2UIEvent(TableRow tr, TableItem ti, int modelColumn, int swtColumn) {
           if(modelColumn == 1 && tr.getCell(modelColumn).toInt() < 0){
               ti.setFont(swtColumn, getComponent().getFontByStyle(ti.getFont(), SWT.BOLD));
           }
       }
   }
Register your implementation at the client table model:
  public void createModels() {
     super.createModels();
     myTable.setTableWM2UIListener(new TableWM2UIListenerImpl());
  }

Recall from section What are widget models good for?(What are Widget Models good for?) that widget models are transparently synchronized between XMA client and server. In the context of tables that means that modifications made through the above mentioned API are sent between client and server by the XMA-Runtime. Tables may be modified on both sides and the runtime decides on what information is transferred over the wide area network:

  • All table rows

  • or just the modifications.

The decision depends on what would produce less traffic over the wide area network. Suppose, for example you are filling a (before empty) table at the server, then all table rows get transmitted. At the other extreme, if you are deleting a row form a large table, it is much more wise to transmit the information that a particular row has been deleted than to transmit the table completely.

For this delta mechanism to work, the table must be stored at both sides of XMA, at the client and at the server and therefore consumes memory at the server between successive RPCs. There are use cases, most often search-operations, where

  • Tables are filled at the server

  • and are just displayed at the client without modification of the rows at the client.

This is often the case with search-operations where the server does the search, retrieves the DBMS-rows and fills the table. The client does not modify this row set, but just displays it.

To support this case more efficiently, a table may be marked one-way in the UI-designer. The rows of one-way tables are transferred only from server to the client whenever the server makes modifications to the table model. After the RPC, i.e., when the server finishes processing the request, the table is deleted at the server and therefore frees its server-side table-memory.

[Tip]

Flag your table one-way in the UI-designer, if a table must be filled once at the server and just displayed and not modified later at the client. Note that with one-way tables the row-set you encounter in follow-up RPCs at the server is empty. Of course, you may fill the table at the server again, then it is transferred to the client again, but completely, without deltas.

[Caution]

At the client side, the attached SWT-table must not be modified in any way.

[Caution]

At the client side, an SWT-table is attached to the widget model. This SWT-table enables accessing the table rows by index. You must not use these indexes in any way, since the table may had been sorted. In this case, the SWT table indexes does not match the widget model indexes, since the widget model is unaffected by the sort operation.

A Tree Widget Model encapsulates

The code that is generated by the UI-designer/generator looks like
protected Tree                myTreeW = ...  // the SWT-widget
protected ITreeWMClient       myTree = ...    
at the client. At the server it is almost the same, except that you cannot access the SWT-widget there:
protected ITreeWMServer       myTree = ...    
Trees have to be build by programming, either on the server or client side.
TreeNode node1 = new TreeNode(myTree,"key1","1. Node",0);
TreeNode node2 = new TreeNode(node1,"key2","2. Node",0);
node2.addDummyNode();    
The following table lists the programming tasks that you may do with tree WMs.


Some applications require the lazy adding of child nodes after a node is expanded. Nodes can be added (at the client or the server side) in the expand event method. The generation of a tree expand event method is enabled by the widget model property YN Expand Event at the GUI designer.

SWT tree nodes are not expandable without a child node. Therefore this lazy tree behavior is implemented by adding a dummy child node to a node. This is done by a parameter at the TreeNode constructor or by the TreeNode's method addDummyNode(). After a dummy node was added the method TreeNode.isLazy() isLazy() returns true. So a node can be asked in the expand event if it needs lazy loading. The dummy node itself is removed before the expand event is entered. After the expand event occurred at a node TreeNode.isLazy() returns false again. For dealing with dummy nodes when iterating a tree there are additional TreeNode methods: TreeNode.hasDummyNode(), TreeNode.isDummyNode().

A Grid Model encapsulates an excel spreadsheet. It allows the presentation of excel spreadsheets in XMA including simple formatting and formula evaluation. The end user can enter data in individual cells of the grid. Formulas are automatically recalculated if there result may change.

Note: This is not a general purpose editable table. Its purpose is to present and manipulate Excel sheets directly in XMA where this is needed.

This model uses POI for parsing of excel files and JEKS for formula parsing and evaluation. These libraries can be installed using the IMC-Installer . The model itself is packaged in a separate JAR file (XMA_Grid) to keep the main XMA-Runtime JAR small. XMA_Grid can be installed using the IMC-Installer , too.

The following table lists the programming tasks that you may do with grid WMs.

AreaProgramming Tasks

Client and Server, ( IGridWM and GridCell)

  • accessing individual cells ( getCell() )

  • creating cells if they do not already exist ( getOrCreateCell() )

  • accessing rows or columns ( getRow() and getColumn() )

  • acessing named ranges ( getRange(String) ). Named ranges are rectangular regions in a sheet, which can be defined in excel.

  • creating and removing of named ranges ( setRange() and removeRange() )

  • restrict the visibility to a range ( setVisibleRange() )

  • selecting and deselecting cells ( select() and deselect() ). The methods of ISelectable are implemented, too.

On individual cells you can
  • set the cell editable by the end user ( setEditable ). By default this property is read from the excel file.

  • accessing the cell value ( getValue() and get<type>Value() for the value casted to the respective Java base type. These methods calculate the result of formulas if necessary.

  • setting the value of a cell( setValue() )

  • setting a formula for the cell ( setFormula() )

  • reading the type of the value ( getType() ). The type is implicitly defined by the value of the cell.

  • reading formating information (foreground color, background color and font). Setting this information is currently not implemented. Formatting can currently only be read from an excel file.

On ranges you can
  • test if a cell is contained ( containsCell )

  • access a cell by indexes relative to the range ( getCell() )

  • read the size of the range ( getNumRows() and getNumColumns() )

  • read the corners of the range.

On rows and columns you can
  • hide the whole row or column ( setHidden() ) to make it invisible.

Client only, (IGridWM Client)

At the XMA client, the following column properties are accessible:

  • enabled to enable or disable the whole widget.

  • editable to set the whole widget editable or not editable. Even if the widget is editable, individual cells which are not editable, cannot be changed by the user.

  • formatter to set a formatter for individual cells. Additionally default formatters can be set for rows and columns which are used for cells without an own formatter.

Server only, (GridPOI Adapter)

  • On server side, you can read an excel file into a grid widget model ( poi2xma() .

  • If only a part of the sheet is visible, you should call calcAlignements() after defining the visibility, so only the visible cells are counted when the alignment of the columns is calculated.

  • If only a part of the sheet is visible, you can call GridWM.removeInvisibleEmptyCells() to remove cells which contain only information which is never used. This will reduce the amount of data to transfer to the client.

Table 6.8. grid tasks


The following properties are read from the EXCEL file and supported in the GridWM.

In EXCEL the syntax of the formulas is localized in the different language versions of EXCEL. The names of the functions are translated to the respective language and the parameter separator for the functions may be different. In the files, EXCEL saves, all function names and other parts of the formulas are saved in English syntax. So, KRIMI APS will see in any case the English syntax and English names of the functions. The supported formulas are described in this syntax and with English function names here.

Shared Formulas are not supported by the underlaying library POI. Excel uses a compressed format to store formulas which are copied several times (more than about 6 times) into the same sheet. This format cannot be read from the EXCEL file.

In JEKS additional functions, like the trigonometric functions, are supported. These functions are not tested, especially eventual differences to EXCEL are not investigated. Use only the formula elements and functions described below.

In XMA normally all widgets are created in guidesigner. At runtime these widgets and their corresponding widget models are created by generated code. Therefore you need to specify all widgets at design time. This is normally no problem since you know how your pages should look like. But with this approach it is not possible to create generic pages witch for instance let you edit an arbitrary java bean by using reflection to iterate over all properties and place a text field for each property on the screen. Since version 2.1.0 of XMA Runtime it is possible to program such generic pages by adding widgets and widget models at runtime. This is now supported for all widget model types.

To dynamically create a new widget model at runtime, follow these steps:

  1. Create the widget model using its constructor. Every constructor of a widget model takes an ID as first parameter. You must use the method Page.getNextModelId() to retrieve a valid ID. E.g.

    mymodel = new SimpleWM(getNextModelId(),Types.T_STRING,this);
    If you create the widget model on server side, you have to use the constructor of the server side model representation, like in the example above. If you create the widget model on client side, you have to use the constructor of the client side representation (e.g. SimpleWMClient).

  2. Register the new widget model to the runtime by calling Page.addWModel(WModel) or Page.addWModel(WModel,NewModelEventParams). This method has to be called exactly once for each new widget model. With the second variant, you can pass information for creating the corresponding widget in the next step. E.g.

    addWModel(mymodel,new NewModelEventParams("s,35,m","&StringText:"));
    The first parameter to the constructor of NewModelEventParams describes the formatter/validator to use. You have the following possibilities for defining it:
    • Pass a format string understandable by FmtFactory (see Custom formatters). The formatter/validator will be automatically created by the runtime.

    • Overwrite the method PageClient.setFormatter() and pass a format string you understand. The method PageClient.setFormatter() will be called automatically by the runtime. There you can set the formatter/validator using the method setFmt().

    • If you create the widget model on client side, you can set this parameter null and set the formatter/validator directly by calling setFmt().

    The second parameter of NewModelEventParams describes the label for the corresponding widget. Additional parameters like tooltip and arbitrary parameters are supported by NewModelEventParams, too.

  3. Overwrite the method PageClient.createWidgetForModel() in your client side page. This method is called by the runtime to create the widget corresponding to the newly registered widget model. It has to create the widget and eventually needed additional widgets like labels, and append it to the layout and tab order. For creating and appending the new widget to the end of a simple layout, you can use the method PageClient.appendWidget(). E.g.

    public ControlSet createWidgetForModel(IWModelClient model,NewModelEventParams params) {
        String label = (String) params.getParameter(NewModelEventParams.PARAM_LABEL);
        ControlSet controls = appendWidget(model,getComposite(),label,seperatorW);
        return controls;
    }
    Attaching the needed event listener to the newly created widget and attaching it to its widget model is done automatically by the runtime after PageClient.createWidgetForModel() is finished. If you want to create a hidden widget, simply return an empty ControlSet.

The dynamically created widget models are fully integrated in synchronizations during RPCs. They are normally attached to its widgets and fully operational. The creation of the corresponding widget is delayed accordingly if the widget model is created before the corresponding page was invoked (see Page Life Cycle).

This mechanism works for all widget model types of XMA. The Grid Widget Model (see Grid Widget Models) is implemented in its own jar-file. To enable XMA Runtime to support dynamically added grids, too, you have to add the following lines to your xma-appl.xml file (see Application Descriptor):

<plugin-impl implements="at.spardat.xma.mdl.NewModelEventFactory"
    implClient="at.spardat.xma.mdl.grid.NewModelEventFactoryGrid"
    implServer="at.spardat.xma.mdl.grid.NewModelEventFactoryGrid"
</plugin-impl>
This is only needed, if you want to add grids dynamically at runtime. If you do not use grids or add them using guidesigner, do not add this lines.