home
products
education
support
partners
company

introduction

get started

articles

manual

tutorials

reference docs

case studies

knowledge base

goodies

screenshots

demos

echoes

Carousel zone


SourceForge.net Logo

home

forums

downloads

bugs

mailing list
XUI Zone - case studies

number of ratings 0
average rating 0.00

Case Study 1 - Mortgage Application

Introduction

The Mortgage application which is outlined as part of this case study is taken from the tutorial which is available for download from the XUI project on the sourceforge site. The intention of the tutorial is the make the developer familiar with development elements of the XUI/Carousel framework. The development of the application is split over several different tutorials which gives the developer a fully working version of the application at the end of each step.

In this case study we are going to examine the elements which make up the application in each tutorial. In summary the tutorials are as follows

Table 1-1 - The tutorials and what they teach

Introduction

This step shows how to set up the basic structure of a XUI application and what files and libraries need to be included. It creates some simple pages and shows how to navigate the application. It shows how to add page events, validations, data bindings and dynamic bindings. The application is completely localized in the final step and the model is used to output captured data.

Advanced

This step builds upon the introductory tutorial and provides the user with a more generic navigation which allows new pages to be added very easily. It also shows how library functions can be used to help with the navigation. Custom validations are created which are loaded via a custom XValidationFactory. Framesets are customised and component registration is used to develop and load custom components. Custom dialogs are created which report on the types of errors being generated. Model data is saved from and restored to the model showing how easy it is to create applications where projects can be created and opened.

KalIDEoscope

This tutorial really replicates what was done in the first step except that the development is carried out with the help of the KalIDEoscope editor.

Carousel

In introducing Carousel the application is makes use of the routes which are available in the Carousel library. This allows the developer to create applications which can easily connect to a servlet server making use of authentication and session management services. Data can be cached on the client and the application can be used to work with them offline and then synchronise with the server. Custom ServiceProxy classes are created which are used in transforming mode data.

Introduction tutorial

This tutorial begins by showing the project structure for a typical XUI application as shown in the diagram below.

The run.bat file contains the text below.

Code Sample 1-1 - The run.bat file

java -cp .;lib\XuiCoreSwing.jar;images;pages;resources;build\classes net.xoetrope.swing.XApplet

The application is made up of frames which are defined in the frames.xml file as shown below.

Code Sample 1-2 - The frames.xml file

Navigation

The project navigation was handled by the navPanel page in the navPanel frame. In order to do this two images where added to the navPanel page as shown below

Code Sample 1-3 - navPanel.xml page

Two images are added and each has a MouseHandler event added to them. The nextPage and prevPage functions are defined in the NavPanel class as follows.

Code Sample 1-4 - The NavPanel class

package net.xoetrope.mortgage;

import net.xoetrope.xui.*;

public class NavPanel extends XPage

{

public void nextPage()

{

if ( wasMouseClicked())

navigateToPage( "next" );

}

protected void navigateToPage( String key )

{

XPage target = ( XPage )( ( XTarget ) pageMgr.getTarget( "content" ) ).getComponent( 0 );

String dest = ( String )target.getAttribute( key, null );

if ( dest != null )

pageMgr.showPage( dest, "content" );

}

public void prevPage() {

if ( wasMouseClicked() )

pageMgr.showPrevious();

}

}

This navigation depended on the presence of the next attribute in the main content page

Bindings

Dynamic binding was used to address different nodes within the data model. On the personal page the binding are declared as follows.

Code Sample 1-5 - The bindings on the personal page

...

output="mortapp/${getCustomerID()}/firstname" />

output="mortapp/${getCustomerID()}/surname" />

output="mortapp/${getCustomerID()}/dob" />

...

These bindings depend on the callback function getCustomerID in the Personal class as follows.

Code Sample 1-6 - The callback function in the Personal page

public String getCustomerID()

{

return "customer" + ( String )currentCust.get();

}

This class takes care of tracking which customer is being currently processed allowing numerous customers to be added to the model in their own specific nodes.

Saving the model

The model data was then saved within the Finish class pageActivated function.

Code Sample 1-7 - The Finish class

public class Finish extends XPage

{

public void pageActivated() {

try {

String path = System.getProperty( "user.dir" ) + File.separator + "save.xml";

FileOutputStream fos = new FileOutputStream( path );

OutputStreamWriter osw = new OutputStreamWriter( fos, "UTF8" );

BufferedWriter bw = new BufferedWriter( osw );

XDataSource.outputModel( bw, ( ( XModel ) rootModel.get( "xui_state/mortapp" ) ) );

bw.flush();

bw.close();

}

catch (Exception e) {

System.out.println( "error" );

}

}

}

This provided us with the following output.

Code Sample 1-8 - Model data saved from the application

<

Localization

The entire application was localized through the use of language specific resource bundles. For example the personal page was localized simply by changing the content attributes for the components.

Code Sample 1-9 - The localized personal page

alignment="Left" opaque="true"/>

alignment="Left" opaque="true"/>

content="WEL_SOLE" alignment="Leading"/>

content="WEL_JOINT" alignment="Leading"/>

...

The resources were entered into two resource bundles for English and French.

Code Sample 1-10 - Extract from en.properties

WEL_LANG=Language

WEL_TEXT=Welcome to ABC Bank's mortgage application. Before we proceed please note the following...

WEL_SOLE=Sole

WEL_JOINT=Joint

BAN_TITLE=ABC Bank Mortgage Application

BAN_CLOSE=Close Application

PER_HEADING_1=Personal Details (First Applicant)

PER_HEADING_2=Personal Details (Second Applicant)

...

Code Sample 1-11 - Extract from fr.properties

WEL_LANG=Langue

WEL_TEXT=Bienvenue ? la demande de pr?t hypoth?caire de l'hypoth?que de la banque de ABC.

Avant que nous veuillez proc?der note le suivant...

WEL_SOLE=Unique

WEL_JOINT=Joint

BAN_TITLE=Demande de pr?t hypoth?caire D'Hypoth?que De Banque de ABC

BAN_CLOSE=Application ?troite

PER_HEADING_1=D?tails Personnels (Premier Demandeur)

PER_HEADING_2=D?tails Personnels (Deuxi?me Demandeur)

...

A combo box was then placed on the welcome page which allowed the user to switch language. The selection of a language invoked the following code in the Welcome class.

Code Sample 1-12 - Language switching code

public void changeLanguage()

{

if ( langClicked ) {

String lang = ( String )languageList.getSelectedObject();

pageMgr.reset();

String langCode = lang.compareTo( "English" ) == 0 ? "en" : "fr";

project.setStartupParam( "Language", langCode );

pageMgr.loadFrames( "frames", true );

}

}

This completed the introductory tutorial and the resulting application can be seen below

Advanced Tutorial

This tutorial uses the application created in the introductory tutorial and begins by adding custom validations and a custom validation factory. The custom validation calls a function within the XPage and returns the result of it. The custom validation is shown below.

Code Sample 1-13 - The custom FunctionValidation factory

public class FunctionValidation extends XBaseValidator {

public FunctionValidation() {

}

public void validate( Object c, boolean forceMandatory ) throws Exception {

Integer ret = ( Integer ) invokeMethod();

if ( ret.intValue() > LEVEL_IGNORE ) {

errorLevel = ret.intValue();

throwException();

}

}

public void setup( XmlElement element )

{

message = element.getAttribute( "msg" );

super.setup( element );

}

}

In order to use this validation a custom validation factory need to be created as shown below.

Code Sample 1-14 - The custom ValidationFactory class

public class ValidationFactory extends XValidationFactory {

...

public XValidator getValidation( String validationName, Method m, int mask, Object page )

{

XmlElement ele = ( XmlElement ) validations.get( validationName );

String type = ele.getAttribute( "type" );

if ( type.compareTo( "function" ) == 0 ) {

FunctionValidation validator = new FunctionValidation();

validator.setName( validationName );

validator.setValidationMethod( m, page );

validator.setup( ele );

return validator;

} else {

XBaseValidator validator = ( XBaseValidator ) super.getValidation( validationName, mask, page );

validator.setup( ele );

return validator;

}

}

}

The getValidation function checks to see if the type being created is function . If it is it creates a new instance of the FunctionValidation class and returns it. If it isn't the super getValidation function is called in order to retrieve the validation.

In order for XUI to use this validation factory the following line needed to be added to the startup properties.

Code Sample 1-15 - Startup property for the custom validation factory

ValidationFactory=net.xoetrope.mortgage.validation.ValidationFactory

Now the welcome page was amended to use the new validation.

Code Sample 1-16 - XML declaration for the function validation

And the welcome class was amended to include the checkCreateSetup function

Code Sample 1-17 - The checkCreateSetup function

...

public Integer checkCreateSetup()

{

int level = XBaseValidator.LEVEL_IGNORE;

if ( !jointRadio.isSelected() && !soleRadio.isSelected() )

level = XBaseValidator.LEVEL_ERROR;

return new Integer( level );

}

...

This function checks to see that at least one of the radio buttons on the welcome page has been selected. If everything is OK then LEVEL_IGNORE is returned otherwise LEVEL_ERROR is returned and the error message is displayed.

Bindings

The tutorial now goes on to show how the data model can be reopened once saved. This required the use of a custom data binding which was added to a list component on the welcome page.

Code Sample 1-18 - Binding to the list component on the welcome page

...

...

...

Now the saved files needed to be listed when the pageCreated function was called.

Code Sample 1-19 - Loading files into the model

public void pageCreated()

{

...

loadFiles();

}

...

private void loadFiles()

{

String dir = System.getProperty( "user.dir" );

appsMdl = ( XBaseModel )rootModel.get( "applications" );

File folder = new File( dir + "\\files" );

File files[] = folder.listFiles();

for ( int i = 0; i < files.length; i++ ) {

String name = files[ i ].getName();

XBaseModel filMdl = new XBaseModel ( appsMdl, name, name );

}

updateBindings();

}

...

Child nodes containing the filenames are added to the applications model node and the updateBindings function of the page is called so that the list will refresh. Now the selected file could be opened and restored into the model as follows.

Code Sample 1-20 - Opening and restoring the model state

...

public void openFile()

{

String filename = ( String )fileList.getSelectedObject();

// Not shown here but simply reads the file into a string.

String contents = getFileContents( filename );

restoreState( contents );

updateBindings();

}

private void restoreState( String contents )

{

( ( XBaseModel )rootModel.get( "xui_state/mortapp" ) ).clear();

( ( XBaseModel )rootModel.get( "mortapp" ) ).clear();

StringReader sr = new StringReader( contents );

XmlElement ele = XmlSource.read( sr );

if ( ele != null ) {

XOptionalDataSource ds = new XOptionalDataSource();

ds.loadTable( ele, rootModel );

ds.loadTable( ele, ( ( XBaseModel )rootModel.get( "xui_state" ) ) );

}

}

...

The restoreState function is really of most importance here. It initialises the xui_state and mortapp nodes. It then loads the model into both of these. Again, updateBindings is called so as to refresh the state of the radio buttons on the welcome page.

The list can be seen on the welcome page below.

Navigation

The navigation of the applicaton was improved by making use of the library functions which are part of XUI. To do this the navPanel page was changed to reference functions in the Navigator class as follows.

Code Sample 1-21 - The amended MouseHandler events in the navPanel page

target="nextButton" type="MouseHandler"/>

target="prevButton" type="MouseHandler"/>

The Navigator page was created as a Singleton instance class and included the static functions next and previous . The pages to be included in the navigation were defined in a new navigation dataset as follows.

Code Sample 1-22 - The navigation.xml file

The Navigator class took care of keeping track of where the application was in the navigation sequence and of repeating the relevant pages where for the amount of customers being processed. Now each of the pages in the navigation could use the same class to return the correct customer id.

Code Sample 1-23 - The generic CustomerPage class

public class CustomerPage extends XPage

{

public String getCustomerID()

{

return "customer" + Navigator.getInstance().getCurrentCust();

}

}

With this mechanism the navigation was greatly simplified and pages could be added very easily by referencing them in the navigation file. The amount of customers which could be process is now endless and all taken care of by the very simple Navigation class.

Framesets and component registration

The framesets in were amended to include a status frame to show the user exactly where they were in the application navigation. A new frame declaration was added to the frames.xml file as follows.

Code Sample 1-24 - The new declaration in frames.xml

This frame was made invisible on the welcome page and visible on subsequent pages. It's visibilty was controlled by the Navigation class.

A custom component was then created which listed all of the pages to be processed and highlighted the current one. It was declared in the components.xml file as follows.

Code Sample 1-25 - The components.xml file with the declaration for NavViewer

Now a NavViewer instance was added to the navPanel page as follows.

Code Sample 1-26 - The AppStatus.xml file

DefaultStyle="status/default" CurrentStyle="status/current"

DoneStyle="status/done" NavModelPath="navigation"/>

Now the NavViewer class could take care of displaying navigation information as below.

Dialogs

A custom dialog was introduced to display errors and warnings with different icons. This new dialog also allowed the user to proceed if only warnings were generated. The exception handler needed to be amended to store the errors and warnings in their own respective Vector objects as below.

Code Sample 1-27 - The ExceptionHandler class

public boolean handleException( Object comp, Exception ex, Object xvalidator )

{

...

if ( validator.getLevel() == validator.LEVEL_ERROR )

errors.add( currentPage.translate( msg ) );

else

warnings.add( currentPage.translate( msg ) );

return true;

}

public int accumulateMessages( boolean start, int level )

{

pageValidation = start;

if ( pageValidation ){

errors = new Vector();

warnings = new Vector();

} else {

if ( errors.size() > 0 || warnings.size() > 0 )

return getDialogResult( level );

}

return level;

}

private int getDialogResult( int level )

{

ErrorMessage dlg = ( ErrorMessage )XProjectManager.getPageManager().loadPage( "ErrorDlg", false );

dlg.setErrorMessages( errors, warnings );

int result = dlg.showDialog( currentPage );

if ( level == XValidator.LEVEL_WARNING )

return result == XValidator.LEVEL_IGNORE ? XValidator.LEVEL_IGNORE : level;

else

return XValidator.LEVEL_ERROR;

}

The getDialogResult function creates the new ErrorMessage dialog and passes in the two vectors. The return value determines whether the page can proceed or not depending on what button the user clicked.

In the ErrorMessage class the relevant return value is set when one of the buttons is clicked as follows.

Code Sample 1-28 - The ErrorMessage class

...

public int showDialog( Container owner )

{

super.showDialog( owner );

return ret;

}

public void cancelClicked()

{

ret = XBaseValidator.LEVEL_WARNING;

super.closeDlg();

}

public void okClicked()

{

ret = XBaseValidator.LEVEL_IGNORE;

super.closeDlg();

}

...

comments


If you were logged in you could rate this article or add a comment.